From d6cc50eb49275bbc0de21d4c65a5172d5d65f790 Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 14 Feb 2026 14:55:58 +0100 Subject: refactor(gpu): Relocate effects to src/effects and streamline includes This refactoring improves the project's structure by decoupling visual effects from the core GPU module. All effect implementations have been moved from to a new top-level directory. Shared utilities like , , and have been consolidated into the parent directory. - **Motivation**: To create a clearer separation of concerns, making the codebase easier to navigate and maintain. This move treats effects as a distinct layer that depends on the core GPU module, rather than being embedded within it. - **Changes**: - Created new directory. - Moved all effect source files (, ) to . - Moved shared helpers (, , ) to . - Updated and to reflect the new file locations for all build targets. - Corrected all directives across the entire codebase (, , ) to point to the new paths. - Updated all markdown documentation ( files) to ensure file paths and architectural descriptions are accurate. - Fixed several compiler errors related to incorrect enum casting () that were exposed during cross-compilation for Windows. - **Verification**: - The entire project builds successfully for both native and Windows cross-compilation targets. - All 34 tests pass (Usage ctest [options]). - The --- Running Native Build & Tests --- Configuring with all options enabled (tests + tools)... -- -- Build Configuration: -- DEMO_SIZE_OPT: ON -- DEMO_STRIP_ALL: ON -- DEMO_FINAL_STRIP: OFF -- DEMO_STRIP_EXTERNAL_LIBS: OFF -- DEMO_BUILD_TESTS: ON -- DEMO_BUILD_TOOLS: ON -- DEMO_ENABLE_COVERAGE: OFF -- DEMO_ENABLE_DEBUG_LOGS: OFF -- DEMO_HEADLESS: OFF -- DEMO_WORKSPACE: main -- -- Loaded workspace: Main Demo -- Timeline: timeline.seq -- Music: pop_punk_drums.track -- Assets: assets.txt -- Using workspace: main -- Configuring done (0.0s) -- Generating done (0.1s) -- Build files have been written to: /Users/skal/demo/build Building all targets (demo, tests, and tools)... [ 0%] Built target validate_uniforms_script [ 1%] Built target procedural [ 2%] Validating uniform buffer sizes and alignments... [ 3%] Built target tracker_compiler [ 4%] Built target test_3d [ 4%] Built target test_maths [ 4%] Built target seq_compiler [ 4%] Built target tracker_compiler_host [ 5%] Built target asset_packer [ 5%] Built target test_procedural [ 6%] Compiling demo sequence from workspace main... [ 6%] Built target generate_tracker_music [ 6%] Built target generate_test_demo_music [ 6%] Compiling test_demo sequence... Using BPM: 90 Successfully generated timeline with 16 sequences. Using BPM: 120 Demo end time: 16.000000s Successfully generated timeline with 1 sequences. [ 6%] Built target generate_test_demo_timeline [ 6%] Built target generate_timeline Validation Warning for 'CommonPostProcessUniforms': Matching WGSL struct not found. Validation OK for 'FadeParams': Size 16 matches C++ expected size. Validation OK for 'ThemeModulationParams': Size 16 matches C++ expected size. Validation OK for 'GaussianBlurParams': Size 8 matches C++ expected size. Validation OK for 'DistortParams': Size 8 matches C++ expected size. Validation OK for 'CircleMaskParams': Size 16 matches C++ expected size. [ 6%] Built target generate_test_assets [ 7%] Built target generate_demo_assets [ 7%] Built target validate_uniforms [ 8%] Built target util [ 10%] Built target test_assets [ 11%] Built target test_shader_assets [ 12%] Built target test_file_watcher [ 15%] Built target 3d [ 21%] Built target test_platform [ 22%] Built target audio [ 23%] Built target test_window [ 26%] Built target test_fft [ 27%] Built target test_synth [ 27%] Built target test_spectral_brush [ 27%] Built target test_physics [ 28%] Built target test_dct [ 31%] Building CXX object CMakeFiles/gpu.dir/src/gpu/effect.cc.o [ 30%] Built target test_mock_backend [ 32%] Built target test_scene_loader [ 33%] Built target test_audio_backend [ 34%] Built target test_audio_gen [ 36%] Built target test_silent_backend [ 39%] Built target test_jittered_audio [ 39%] Building CXX object CMakeFiles/gpu.dir/src/effects/heptagon_effect.cc.o [ 42%] Built target test_wav_dump [ 44%] Built target test_tracker_timing [ 44%] Building CXX object CMakeFiles/gpu.dir/src/effects/particles_effect.cc.o [ 45%] Building CXX object CMakeFiles/gpu.dir/src/effects/passthrough_effect.cc.o [ 47%] Built target test_variable_tempo [ 50%] Built target test_audio_engine [ 52%] Built target test_tracker [ 52%] Building CXX object CMakeFiles/gpu.dir/src/effects/moving_ellipse_effect.cc.o [ 52%] Building CXX object CMakeFiles/gpu.dir/src/effects/particle_spray_effect.cc.o [ 52%] Building CXX object CMakeFiles/gpu.dir/src/effects/gaussian_blur_effect.cc.o [ 54%] Built target test_spectool [ 55%] Building CXX object CMakeFiles/gpu.dir/src/effects/solarize_effect.cc.o [ 55%] Building CXX object CMakeFiles/gpu.dir/src/effects/scene1_effect.cc.o [ 55%] Building CXX object CMakeFiles/gpu.dir/src/effects/chroma_aberration_effect.cc.o [ 55%] Building CXX object CMakeFiles/gpu.dir/src/gpu/shaders.cc.o [ 57%] Building CXX object CMakeFiles/gpu.dir/src/effects/vignette_effect.cc.o [ 57%] Building CXX object CMakeFiles/gpu.dir/src/gpu/post_process_helper.cc.o [ 57%] Linking CXX static library libgpu.a [ 60%] Built target gpu [ 60%] Linking CXX executable test_uniform_helper [ 60%] Linking CXX executable test_shader_composer [ 60%] Building CXX object CMakeFiles/test_sequence.dir/src/tests/assets/test_sequence.cc.o [ 61%] Linking CXX executable test_noise_functions [ 62%] Linking CXX executable test_shader_compilation [ 62%] Building CXX object CMakeFiles/test_demo.dir/src/app/test_demo.cc.o [ 62%] Building CXX object CMakeFiles/demo64k.dir/src/app/main.cc.o [ 62%] Building CXX object CMakeFiles/test_3d_render.dir/src/generated/timeline.cc.o [ 63%] Built target test_uniform_helper [ 64%] Built target test_shader_composer [ 64%] Building CXX object CMakeFiles/test_3d_physics.dir/src/generated/timeline.cc.o [ 65%] Built target test_noise_functions [ 66%] Built target test_shader_compilation [ 67%] Building CXX object CMakeFiles/test_mesh.dir/src/generated/timeline.cc.o [ 67%] Building CXX object CMakeFiles/test_effect_base.dir/src/tests/gpu/test_effect_base.cc.o [ 67%] Building CXX object CMakeFiles/test_demo_effects.dir/src/tests/gpu/test_demo_effects.cc.o [ 67%] Building CXX object CMakeFiles/test_sequence.dir/src/generated/timeline.cc.o [ 68%] Building CXX object CMakeFiles/test_demo.dir/src/generated/test_demo_timeline.cc.o [ 68%] Building CXX object CMakeFiles/demo64k.dir/src/generated/timeline.cc.o [ 68%] Linking CXX executable test_3d_render [ 68%] Building CXX object CMakeFiles/test_effect_base.dir/src/generated/timeline.cc.o [ 68%] Linking CXX executable test_3d_physics [ 68%] Linking CXX executable test_mesh [ 71%] Built target test_3d_render [ 71%] Building CXX object CMakeFiles/test_post_process_helper.dir/src/tests/gpu/test_post_process_helper.cc.o [ 72%] Building CXX object CMakeFiles/test_demo_effects.dir/src/generated/timeline.cc.o [ 72%] Linking CXX executable test_demo [ 75%] Built target test_3d_physics [ 77%] Built target test_mesh [ 77%] Linking CXX executable test_texture_manager [ 78%] Linking CXX executable test_sequence [ 78%] Linking CXX executable test_gpu_procedural [ 80%] Built target test_demo [ 81%] Linking CXX executable test_gpu_composite [ 81%] Linking CXX executable demo64k [ 83%] Built target test_sequence [ 85%] Built target test_texture_manager [ 86%] Built target test_gpu_procedural [ 86%] Linking CXX executable test_post_process_helper [ 86%] Linking CXX executable test_effect_base [ 87%] Built target test_gpu_composite [ 90%] Built target demo64k [ 92%] Built target test_post_process_helper [ 96%] Built target test_effect_base [ 96%] Linking CXX executable test_demo_effects [100%] Built target test_demo_effects Running test suite... Test project /Users/skal/demo/build Start 1: HammingWindowTest 1/34 Test #1: HammingWindowTest ................ Passed 0.00 sec Start 2: MathUtilsTest 2/34 Test #2: MathUtilsTest .................... Passed 0.00 sec Start 3: FileWatcherTest 3/34 Test #3: FileWatcherTest .................. Passed 0.00 sec Start 4: SynthEngineTest 4/34 Test #4: SynthEngineTest .................. Passed 0.00 sec Start 5: DctTest 5/34 Test #5: DctTest .......................... Passed 0.00 sec Start 6: FftTest 6/34 Test #6: FftTest .......................... Passed 0.01 sec Start 7: SpectralBrushTest 7/34 Test #7: SpectralBrushTest ................ Passed 0.01 sec Start 8: AudioGenTest 8/34 Test #8: AudioGenTest ..................... Passed 0.00 sec Start 9: AudioBackendTest 9/34 Test #9: AudioBackendTest ................. Passed 0.00 sec Start 10: SilentBackendTest 10/34 Test #10: SilentBackendTest ................ Passed 0.00 sec Start 11: MockAudioBackendTest 11/34 Test #11: MockAudioBackendTest ............. Passed 0.00 sec Start 12: WavDumpBackendTest 12/34 Test #12: WavDumpBackendTest ............... Passed 0.00 sec Start 13: JitteredAudioBackendTest 13/34 Test #13: JitteredAudioBackendTest ......... Passed 0.00 sec Start 14: TrackerTimingTest 14/34 Test #14: TrackerTimingTest ................ Passed 0.00 sec Start 15: VariableTempoTest 15/34 Test #15: VariableTempoTest ................ Passed 0.00 sec Start 16: TrackerSystemTest 16/34 Test #16: TrackerSystemTest ................ Passed 0.01 sec Start 17: AudioEngineTest 17/34 Test #17: AudioEngineTest .................. Passed 0.00 sec Start 18: ShaderAssetValidation 18/34 Test #18: ShaderAssetValidation ............ Passed 0.00 sec Start 19: ShaderCompilationTest 19/34 Test #19: ShaderCompilationTest ............ Passed 0.02 sec Start 20: NoiseFunctionsTest 20/34 Test #20: NoiseFunctionsTest ............... Passed 0.01 sec Start 21: UniformHelperTest 21/34 Test #21: UniformHelperTest ................ Passed 0.00 sec Start 22: AssetManagerTest 22/34 Test #22: AssetManagerTest ................. Passed 0.01 sec Start 23: SequenceSystemTest 23/34 Test #23: SequenceSystemTest ............... Passed 0.01 sec Start 24: ProceduralGenTest 24/34 Test #24: ProceduralGenTest ................ Passed 0.01 sec Start 25: PhysicsTest 25/34 Test #25: PhysicsTest ...................... Passed 0.01 sec Start 26: ThreeDSystemTest 26/34 Test #26: ThreeDSystemTest ................. Passed 0.00 sec Start 27: ShaderComposerTest 27/34 Test #27: ShaderComposerTest ............... Passed 0.01 sec Start 28: SceneLoaderTest 28/34 Test #28: SceneLoaderTest .................. Passed 0.01 sec Start 29: EffectBaseTest 29/34 Test #29: EffectBaseTest ................... Passed 0.04 sec Start 30: DemoEffectsTest 30/34 Test #30: DemoEffectsTest .................. Passed 0.03 sec Start 31: PostProcessHelperTest 31/34 Test #31: PostProcessHelperTest ............ Passed 0.02 sec Start 32: TextureManagerTest 32/34 Test #32: TextureManagerTest ............... Passed 0.02 sec Start 33: GpuProceduralTest 33/34 Test #33: GpuProceduralTest ................ Passed 0.18 sec Start 34: GpuCompositeTest 34/34 Test #34: GpuCompositeTest ................. Passed 0.20 sec 100% tests passed, 0 tests failed out of 34 Label Time Summary: 3d = 0.01 sec*proc (3 tests) assets = 0.02 sec*proc (2 tests) audio = 0.07 sec*proc (15 tests) gpu = 0.54 sec*proc (11 tests) util = 0.01 sec*proc (3 tests) Total Test time (real) = 0.67 sec Verifying tools compile... [ 9%] Built target procedural [ 18%] Built target tracker_compiler_host [ 18%] Built target tracker_compiler [ 18%] Built target generate_tracker_music [ 18%] Built target asset_packer [ 27%] Built target generate_demo_assets [ 27%] Built target generate_test_assets [ 36%] Built target util [ 81%] Built target audio [100%] Built target test_spectool --- Running Windows Cross-Compilation Build --- Building native tools... -- -- Build Configuration: -- DEMO_SIZE_OPT: OFF -- DEMO_STRIP_ALL: OFF -- DEMO_FINAL_STRIP: OFF -- DEMO_STRIP_EXTERNAL_LIBS: OFF -- DEMO_BUILD_TESTS: OFF -- DEMO_BUILD_TOOLS: OFF -- DEMO_ENABLE_COVERAGE: OFF -- DEMO_ENABLE_DEBUG_LOGS: OFF -- DEMO_HEADLESS: OFF -- DEMO_WORKSPACE: main -- -- Loaded workspace: Main Demo -- Timeline: timeline.seq -- Music: pop_punk_drums.track -- Assets: assets.txt -- Using workspace: main -- Configuring done (0.0s) -- Generating done (0.0s) -- Build files have been written to: /Users/skal/demo/build_native [ 50%] Built target procedural [100%] Built target asset_packer [100%] Built target seq_compiler [100%] Built target tracker_compiler_host Cross-compiling for Windows... -- -- Build Configuration: -- DEMO_SIZE_OPT: ON -- DEMO_STRIP_ALL: ON -- DEMO_FINAL_STRIP: OFF -- DEMO_STRIP_EXTERNAL_LIBS: OFF -- DEMO_BUILD_TESTS: OFF -- DEMO_BUILD_TOOLS: OFF -- DEMO_ENABLE_COVERAGE: OFF -- DEMO_ENABLE_DEBUG_LOGS: OFF -- DEMO_HEADLESS: OFF -- DEMO_WORKSPACE: main -- -- Loaded workspace: Main Demo -- Timeline: timeline.seq -- Music: pop_punk_drums.track -- Assets: assets.txt -- Using workspace: main -- Configuring done (0.0s) -- Generating done (0.0s) -- Build files have been written to: /Users/skal/demo/build_win [ 2%] Built target validate_uniforms_script [ 2%] Built target generate_timeline [ 4%] Built target generate_test_demo_timeline [ 4%] Built target generate_demo_assets [ 4%] Built target generate_test_assets [ 6%] Built target procedural [ 9%] Built target tracker_compiler_host [ 10%] Validating uniform buffer sizes and alignments... [ 11%] Built target generate_tracker_music [ 13%] Built target generate_test_demo_music [ 16%] Built target util [ 28%] Built target 3d [ 45%] Built target audio [ 49%] Building CXX object CMakeFiles/gpu.dir/src/effects/heptagon_effect.cc.obj [ 52%] Building CXX object CMakeFiles/gpu.dir/src/effects/gaussian_blur_effect.cc.obj [ 54%] Building CXX object CMakeFiles/gpu.dir/src/effects/particles_effect.cc.obj [ 54%] Building CXX object CMakeFiles/gpu.dir/src/effects/moving_ellipse_effect.cc.obj [ 54%] Building CXX object CMakeFiles/gpu.dir/src/gpu/effect.cc.obj [ 54%] Building CXX object CMakeFiles/gpu.dir/src/effects/passthrough_effect.cc.obj [ 54%] Building CXX object CMakeFiles/gpu.dir/src/effects/particle_spray_effect.cc.obj Validation Warning for 'CommonPostProcessUniforms': Matching WGSL struct not found. Validation OK for 'FadeParams': Size 16 matches C++ expected size. Validation OK for 'ThemeModulationParams': Size 16 matches C++ expected size. Validation OK for 'GaussianBlurParams': Size 8 matches C++ expected size. Validation OK for 'DistortParams': Size 8 matches C++ expected size. Validation OK for 'CircleMaskParams': Size 16 matches C++ expected size. [ 54%] Built target validate_uniforms [ 55%] Building CXX object CMakeFiles/gpu.dir/src/effects/solarize_effect.cc.obj [ 57%] Building CXX object CMakeFiles/gpu.dir/src/effects/scene1_effect.cc.obj [ 57%] Building CXX object CMakeFiles/gpu.dir/src/effects/chroma_aberration_effect.cc.obj [ 58%] Building CXX object CMakeFiles/gpu.dir/src/effects/vignette_effect.cc.obj [ 59%] Building CXX object CMakeFiles/gpu.dir/src/gpu/post_process_helper.cc.obj [ 60%] Building CXX object CMakeFiles/gpu.dir/src/gpu/shaders.cc.obj [ 62%] Linking CXX static library libgpu.a [ 77%] Built target gpu [ 79%] Building CXX object CMakeFiles/demo64k.dir/src/app/main.cc.obj [ 79%] Building CXX object CMakeFiles/test_demo.dir/src/app/test_demo.cc.obj [ 80%] Building CXX object CMakeFiles/demo64k.dir/src/generated/timeline.cc.obj [ 81%] Building CXX object CMakeFiles/test_demo.dir/src/generated/test_demo_timeline.cc.obj [ 82%] Linking CXX executable test_demo.exe [ 90%] Built target test_demo [ 91%] Linking CXX executable demo64k.exe [100%] Built target demo64k Copying MinGW DLLs... Crunching build_win/demo64k.exe... Ultimate Packer for eXecutables Copyright (C) 1996 - 2026 UPX 5.1.0 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 7th 2026 File size Ratio Format Name -------------------- ------ ----------- ----------- 7036416 -> 4680704 66.52% win64/pe demo64k_packed.exe Packed 1 file. ------------------------------------------------ Size Report: -rwxr-xr-x 1 skal 89939 6.7M Feb 14 14:55 build_win/demo64k.exe -rwxr-xr-x 1 skal 89939 6.7M Feb 14 14:55 build_win/demo64k_stripped.exe -rwxr-xr-x 1 skal 89939 4.5M Feb 14 14:55 build_win/demo64k_packed.exe ------------------------------------------------ Top 20 Largest Symbols (from unstripped): ------------------------------------------------ Build complete. Output: build_win/demo64k.exe All checks passed successfully. script completes without errors. This change streamlines the project's architecture without altering any functionality. --- src/gpu/effects/chroma_aberration_effect.cc | 38 --- src/gpu/effects/circle_mask_effect.cc | 219 ------------- src/gpu/effects/circle_mask_effect.h | 44 --- src/gpu/effects/cnn_effect.cc | 126 -------- src/gpu/effects/cnn_effect.h | 53 ---- src/gpu/effects/cnn_v2_effect.cc | 463 ---------------------------- src/gpu/effects/cnn_v2_effect.h | 88 ------ src/gpu/effects/distort_effect.cc | 36 --- src/gpu/effects/fade_effect.cc | 98 ------ src/gpu/effects/fade_effect.h | 20 -- src/gpu/effects/flash_cube_effect.cc | 104 ------- src/gpu/effects/flash_cube_effect.h | 28 -- src/gpu/effects/flash_effect.cc | 94 ------ src/gpu/effects/flash_effect.h | 45 --- src/gpu/effects/gaussian_blur_effect.cc | 38 --- src/gpu/effects/heptagon_effect.cc | 22 -- src/gpu/effects/hybrid_3d_effect.cc | 147 --------- src/gpu/effects/hybrid_3d_effect.h | 29 -- src/gpu/effects/moving_ellipse_effect.cc | 22 -- src/gpu/effects/particle_spray_effect.cc | 48 --- src/gpu/effects/particles_effect.cc | 47 --- src/gpu/effects/passthrough_effect.cc | 17 - src/gpu/effects/post_process_helper.cc | 58 ---- src/gpu/effects/post_process_helper.h | 37 --- src/gpu/effects/rotating_cube_effect.cc | 200 ------------ src/gpu/effects/rotating_cube_effect.h | 55 ---- src/gpu/effects/scene1_effect.cc | 20 -- src/gpu/effects/scene1_effect.h | 19 -- src/gpu/effects/shader_composer.cc | 120 ------- src/gpu/effects/shader_composer.h | 38 --- src/gpu/effects/shaders.cc | 146 --------- src/gpu/effects/shaders.h | 29 -- src/gpu/effects/solarize_effect.cc | 20 -- src/gpu/effects/theme_modulation_effect.cc | 105 ------- src/gpu/effects/theme_modulation_effect.h | 20 -- src/gpu/effects/vignette_effect.cc | 30 -- 36 files changed, 2723 deletions(-) delete mode 100644 src/gpu/effects/chroma_aberration_effect.cc delete mode 100644 src/gpu/effects/circle_mask_effect.cc delete mode 100644 src/gpu/effects/circle_mask_effect.h delete mode 100644 src/gpu/effects/cnn_effect.cc delete mode 100644 src/gpu/effects/cnn_effect.h delete mode 100644 src/gpu/effects/cnn_v2_effect.cc delete mode 100644 src/gpu/effects/cnn_v2_effect.h delete mode 100644 src/gpu/effects/distort_effect.cc delete mode 100644 src/gpu/effects/fade_effect.cc delete mode 100644 src/gpu/effects/fade_effect.h delete mode 100644 src/gpu/effects/flash_cube_effect.cc delete mode 100644 src/gpu/effects/flash_cube_effect.h delete mode 100644 src/gpu/effects/flash_effect.cc delete mode 100644 src/gpu/effects/flash_effect.h delete mode 100644 src/gpu/effects/gaussian_blur_effect.cc delete mode 100644 src/gpu/effects/heptagon_effect.cc delete mode 100644 src/gpu/effects/hybrid_3d_effect.cc delete mode 100644 src/gpu/effects/hybrid_3d_effect.h delete mode 100644 src/gpu/effects/moving_ellipse_effect.cc delete mode 100644 src/gpu/effects/particle_spray_effect.cc delete mode 100644 src/gpu/effects/particles_effect.cc delete mode 100644 src/gpu/effects/passthrough_effect.cc delete mode 100644 src/gpu/effects/post_process_helper.cc delete mode 100644 src/gpu/effects/post_process_helper.h delete mode 100644 src/gpu/effects/rotating_cube_effect.cc delete mode 100644 src/gpu/effects/rotating_cube_effect.h delete mode 100644 src/gpu/effects/scene1_effect.cc delete mode 100644 src/gpu/effects/scene1_effect.h delete mode 100644 src/gpu/effects/shader_composer.cc delete mode 100644 src/gpu/effects/shader_composer.h delete mode 100644 src/gpu/effects/shaders.cc delete mode 100644 src/gpu/effects/shaders.h delete mode 100644 src/gpu/effects/solarize_effect.cc delete mode 100644 src/gpu/effects/theme_modulation_effect.cc delete mode 100644 src/gpu/effects/theme_modulation_effect.h delete mode 100644 src/gpu/effects/vignette_effect.cc (limited to 'src/gpu/effects') diff --git a/src/gpu/effects/chroma_aberration_effect.cc b/src/gpu/effects/chroma_aberration_effect.cc deleted file mode 100644 index 3a51965..0000000 --- a/src/gpu/effects/chroma_aberration_effect.cc +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the ChromaAberrationEffect with parameterization. - -#include "gpu/demo_effects.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" - -// --- ChromaAberrationEffect --- - -// Backward compatibility constructor (delegates to parameterized constructor) -ChromaAberrationEffect::ChromaAberrationEffect(const GpuContext& ctx) - : ChromaAberrationEffect(ctx, ChromaAberrationParams{}) { -} - -// Parameterized constructor -ChromaAberrationEffect::ChromaAberrationEffect( - const GpuContext& ctx, const ChromaAberrationParams& params) - : PostProcessEffect(ctx), params_(params) { - pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, - chroma_aberration_shader_wgsl); - params_buffer_.init(ctx_.device); -} - -void ChromaAberrationEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - // Update uniforms with current state and parameters - uniforms_.update(ctx_.queue, uniforms); - params_buffer_.update(ctx_.queue, params_); - - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} - -void ChromaAberrationEffect::update_bind_group(WGPUTextureView input_view) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - uniforms_.get(), params_buffer_.get()); -} diff --git a/src/gpu/effects/circle_mask_effect.cc b/src/gpu/effects/circle_mask_effect.cc deleted file mode 100644 index dfe7d03..0000000 --- a/src/gpu/effects/circle_mask_effect.cc +++ /dev/null @@ -1,219 +0,0 @@ -// This file is part of the 64k demo project. -// It implements CircleMaskEffect for auxiliary texture masking demonstration. -// Generates circular mask and renders green background outside circle. - -#include "gpu/effects/circle_mask_effect.h" -#include "generated/assets.h" -#include "gpu/bind_group_builder.h" -#include "gpu/effects/shader_composer.h" - -CircleMaskEffect::CircleMaskEffect(const GpuContext& ctx, float radius) - : Effect(ctx), radius_(radius) { -} - -CircleMaskEffect::~CircleMaskEffect() { - if (mask_sampler_) - wgpuSamplerRelease(mask_sampler_); - if (render_bind_group_) - wgpuBindGroupRelease(render_bind_group_); - if (render_pipeline_) - wgpuRenderPipelineRelease(render_pipeline_); - if (compute_bind_group_) - wgpuBindGroupRelease(compute_bind_group_); - if (compute_pipeline_) - wgpuRenderPipelineRelease(compute_pipeline_); -} - -void CircleMaskEffect::init(MainSequence* demo) { - demo_ = demo; - - // Register auxiliary texture (width_/height_ set by resize() before init()) - demo_->register_auxiliary_texture("circle_mask", width_, height_); - - compute_params_.init(ctx_.device); - - // Initialize uniforms BEFORE bind group creation - uniforms_.update(ctx_.queue, get_common_uniforms()); - - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; - sampler_desc.maxAnisotropy = 1; - mask_sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - - size_t compute_size, render_size; - const char* compute_shader = (const char*)GetAsset( - AssetId::ASSET_CIRCLE_MASK_COMPUTE_SHADER, &compute_size); - const char* render_shader = (const char*)GetAsset( - AssetId::ASSET_CIRCLE_MASK_RENDER_SHADER, &render_size); - - // Compose shaders to resolve #include directives - std::string composed_compute = ShaderComposer::Get().Compose({}, compute_shader); - - WGPUShaderSourceWGSL compute_wgsl = {}; - compute_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL; - compute_wgsl.code = str_view(composed_compute.c_str()); - - WGPUShaderModuleDescriptor compute_desc = {}; - compute_desc.nextInChain = &compute_wgsl.chain; - WGPUShaderModule compute_module = - wgpuDeviceCreateShaderModule(ctx_.device, &compute_desc); - - const WGPUColorTargetState compute_target = { - .format = ctx_.format, // Match auxiliary texture format - .writeMask = WGPUColorWriteMask_All, - }; - WGPUFragmentState compute_frag = {}; - compute_frag.module = compute_module; - compute_frag.entryPoint = str_view("fs_main"); - compute_frag.targetCount = 1; - compute_frag.targets = &compute_target; - WGPURenderPipelineDescriptor compute_pipeline_desc = {}; - compute_pipeline_desc.label = label_view("CircleMaskEffect_compute"); - compute_pipeline_desc.vertex.module = compute_module; - compute_pipeline_desc.vertex.entryPoint = str_view("vs_main"); - compute_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; - compute_pipeline_desc.primitive.cullMode = WGPUCullMode_None; - compute_pipeline_desc.multisample.count = 1; - compute_pipeline_desc.multisample.mask = 0xFFFFFFFF; - compute_pipeline_desc.fragment = &compute_frag; - compute_pipeline_ = - wgpuDeviceCreateRenderPipeline(ctx_.device, &compute_pipeline_desc); - wgpuShaderModuleRelease(compute_module); - - WGPUBindGroupLayout compute_layout = - wgpuRenderPipelineGetBindGroupLayout(compute_pipeline_, 0); - compute_bind_group_ = - BindGroupBuilder() - .buffer(0, uniforms_.get().buffer, sizeof(CommonPostProcessUniforms)) - .buffer(1, compute_params_.get().buffer, sizeof(CircleMaskParams)) - .build(ctx_.device, compute_layout); - wgpuBindGroupLayoutRelease(compute_layout); - - std::string composed_render = ShaderComposer::Get().Compose({}, render_shader); - - WGPUShaderSourceWGSL render_wgsl = {}; - render_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL; - render_wgsl.code = str_view(composed_render.c_str()); - - WGPUShaderModuleDescriptor render_desc = {}; - render_desc.nextInChain = &render_wgsl.chain; - WGPUShaderModule render_module = - wgpuDeviceCreateShaderModule(ctx_.device, &render_desc); - - const WGPUColorTargetState render_target = { - .format = ctx_.format, - .writeMask = WGPUColorWriteMask_All, - }; - WGPUFragmentState render_frag = {}; - render_frag.module = render_module; - render_frag.entryPoint = str_view("fs_main"); - render_frag.targetCount = 1; - render_frag.targets = &render_target; - const WGPUDepthStencilState depth_stencil = { - .format = WGPUTextureFormat_Depth24Plus, - .depthWriteEnabled = WGPUOptionalBool_False, // Don't write depth - .depthCompare = WGPUCompareFunction_Always, // Always pass - }; - - WGPURenderPipelineDescriptor render_pipeline_desc = {}; - render_pipeline_desc.label = label_view("CircleMaskEffect_render"); - render_pipeline_desc.vertex.module = render_module; - render_pipeline_desc.vertex.entryPoint = str_view("vs_main"); - render_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; - render_pipeline_desc.primitive.cullMode = WGPUCullMode_None; - render_pipeline_desc.depthStencil = &depth_stencil; - render_pipeline_desc.multisample.count = 1; - render_pipeline_desc.multisample.mask = 0xFFFFFFFF; - render_pipeline_desc.fragment = &render_frag; - render_pipeline_ = - wgpuDeviceCreateRenderPipeline(ctx_.device, &render_pipeline_desc); - wgpuShaderModuleRelease(render_module); - - WGPUTextureView mask_view = demo_->get_auxiliary_view("circle_mask"); - const WGPUBindGroupEntry render_entries[] = { - {.binding = 0, .textureView = mask_view}, - {.binding = 1, .sampler = mask_sampler_}, - {.binding = 2, - .buffer = uniforms_.get().buffer, - .size = sizeof(CommonPostProcessUniforms)}, - }; - const WGPUBindGroupDescriptor render_bg_desc = { - .layout = wgpuRenderPipelineGetBindGroupLayout(render_pipeline_, 0), - .entryCount = 3, - .entries = render_entries, - }; - render_bind_group_ = wgpuDeviceCreateBindGroup(ctx_.device, &render_bg_desc); -} - -void CircleMaskEffect::resize(int width, int height) { - if (width == width_ && height == height_) - return; - - Effect::resize(width, height); - - if (!demo_) - return; - - // Resize auxiliary texture - demo_->resize_auxiliary_texture("circle_mask", width, height); - - // Recreate render bind group with new texture view - if (render_bind_group_) - wgpuBindGroupRelease(render_bind_group_); - - WGPUTextureView mask_view = demo_->get_auxiliary_view("circle_mask"); - WGPUBindGroupLayout render_layout = - wgpuRenderPipelineGetBindGroupLayout(render_pipeline_, 0); - render_bind_group_ = - BindGroupBuilder() - .texture(0, mask_view) - .sampler(1, mask_sampler_) - .buffer(2, uniforms_.get().buffer, sizeof(CommonPostProcessUniforms)) - .build(ctx_.device, render_layout); - wgpuBindGroupLayoutRelease(render_layout); -} - -void CircleMaskEffect::compute(WGPUCommandEncoder encoder, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - - const CircleMaskParams params = { - .radius = radius_, - }; - compute_params_.update(ctx_.queue, params); - - WGPUTextureView mask_view = demo_->get_auxiliary_view("circle_mask"); - WGPURenderPassColorAttachment color_attachment = {}; - color_attachment.view = mask_view; - color_attachment.loadOp = WGPULoadOp_Clear; - color_attachment.storeOp = WGPUStoreOp_Store; - color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; -#if !defined(DEMO_CROSS_COMPILE_WIN32) - color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; -#endif - - WGPURenderPassDescriptor pass_desc = {}; - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_attachment; - - WGPURenderPassEncoder pass = - wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, compute_pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, compute_bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); - wgpuRenderPassEncoderEnd(pass); - wgpuRenderPassEncoderRelease(pass); -} - -void CircleMaskEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - - wgpuRenderPassEncoderSetPipeline(pass, render_pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, render_bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} diff --git a/src/gpu/effects/circle_mask_effect.h b/src/gpu/effects/circle_mask_effect.h deleted file mode 100644 index 6ebaca1..0000000 --- a/src/gpu/effects/circle_mask_effect.h +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of the 64k demo project. -// It defines the CircleMaskEffect class for masking system demonstration. -// Creates a circular mask and renders green outside the circle. - -#ifndef CIRCLE_MASK_EFFECT_H_ -#define CIRCLE_MASK_EFFECT_H_ - -#include "gpu/effect.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/uniform_helper.h" - -class CircleMaskEffect : public Effect { - public: - CircleMaskEffect(const GpuContext& ctx, float radius = 0.4f); - ~CircleMaskEffect() override; - - void init(MainSequence* demo) override; - void resize(int width, int height) override; - void compute(WGPUCommandEncoder encoder, - const CommonPostProcessUniforms& uniforms) override; - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - - private: - struct CircleMaskParams { - float radius; - float _pad[3]; - }; - static_assert(sizeof(CircleMaskParams) == 16, - "CircleMaskParams must be 16 bytes for WGSL alignment"); - - MainSequence* demo_ = nullptr; - float radius_; - - WGPURenderPipeline compute_pipeline_ = nullptr; - WGPUBindGroup compute_bind_group_ = nullptr; - UniformBuffer compute_params_; - - WGPURenderPipeline render_pipeline_ = nullptr; - WGPUBindGroup render_bind_group_ = nullptr; - WGPUSampler mask_sampler_ = nullptr; -}; - -#endif /* CIRCLE_MASK_EFFECT_H_ */ diff --git a/src/gpu/effects/cnn_effect.cc b/src/gpu/effects/cnn_effect.cc deleted file mode 100644 index 83a3365..0000000 --- a/src/gpu/effects/cnn_effect.cc +++ /dev/null @@ -1,126 +0,0 @@ -// CNN post-processing effect implementation -// Neural network-based stylization with modular WGSL - -#include "gpu/effects/cnn_effect.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/effects/shaders.h" -#include "gpu/effects/shader_composer.h" -#include "gpu/effect.h" -#include "gpu/bind_group_builder.h" -#include "gpu/sampler_cache.h" -#include "gpu/pipeline_builder.h" - -// Create custom pipeline with 5 bindings (includes original texture) -static WGPURenderPipeline create_cnn_pipeline(WGPUDevice device, - WGPUTextureFormat format, - const char* shader_code) { - WGPUBindGroupLayout bgl = BindGroupLayoutBuilder() - .sampler(0, WGPUShaderStage_Fragment) - .texture(1, WGPUShaderStage_Fragment) - .uniform(2, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment) - .uniform(3, WGPUShaderStage_Fragment) - .texture(4, WGPUShaderStage_Fragment) - .build(device); - - WGPURenderPipeline pipeline = RenderPipelineBuilder(device) - .shader(shader_code) - .bind_group_layout(bgl) - .format(format) - .build(); - - wgpuBindGroupLayoutRelease(bgl); - return pipeline; -} - -CNNEffect::CNNEffect(const GpuContext& ctx) - : PostProcessEffect(ctx), layer_index_(0), total_layers_(1), - blend_amount_(1.0f), input_view_(nullptr), original_view_(nullptr), - bind_group_(nullptr) { - pipeline_ = create_cnn_pipeline(ctx_.device, ctx_.format, - cnn_layer_shader_wgsl); -} - -CNNEffect::CNNEffect(const GpuContext& ctx, const CNNEffectParams& params) - : PostProcessEffect(ctx), layer_index_(params.layer_index), - total_layers_(params.total_layers), blend_amount_(params.blend_amount), - input_view_(nullptr), original_view_(nullptr), bind_group_(nullptr) { - pipeline_ = create_cnn_pipeline(ctx_.device, ctx_.format, - cnn_layer_shader_wgsl); -} - -void CNNEffect::init(MainSequence* demo) { - PostProcessEffect::init(demo); - demo_ = demo; - params_buffer_.init(ctx_.device); - - // Register auxiliary texture for layer 0 (width_/height_ set by resize()) - if (layer_index_ == 0) { - demo_->register_auxiliary_texture("captured_frame", width_, height_); - } - - // Initialize uniforms BEFORE any bind group creation - uniforms_.update(ctx_.queue, get_common_uniforms()); - - CNNLayerParams params = {layer_index_, blend_amount_, {0.0f, 0.0f}}; - params_buffer_.update(ctx_.queue, params); -} - -void CNNEffect::resize(int width, int height) { - if (width == width_ && height == height_) - return; - - PostProcessEffect::resize(width, height); - - // Only layer 0 owns the captured_frame texture - if (layer_index_ == 0 && demo_) { - demo_->resize_auxiliary_texture("captured_frame", width, height); - } -} - -void CNNEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - if (!bind_group_) { - fprintf(stderr, "CNN render: no bind_group\n"); - return; - } - - float effective_blend = blend_amount_; - if (beat_modulated_) { - effective_blend = blend_amount_ * uniforms.beat_phase * beat_scale_; - } - - CNNLayerParams params = {layer_index_, effective_blend, {0.0f, 0.0f}}; - params_buffer_.update(ctx_.queue, params); - - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} - -void CNNEffect::update_bind_group(WGPUTextureView input_view) { - input_view_ = input_view; - - // Update common uniforms (CRITICAL for UV calculation!) - uniforms_.update(ctx_.queue, get_common_uniforms()); - - // All layers: get captured frame (original input from layer 0) - if (demo_) { - original_view_ = demo_->get_auxiliary_view("captured_frame"); - } - - // Create bind group with original texture - if (bind_group_) - wgpuBindGroupRelease(bind_group_); - - WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); - // Use clamp (not repeat) to match PyTorch Conv2d zero-padding behavior - WGPUSampler sampler = SamplerCache::Get().get_or_create(ctx_.device, SamplerCache::clamp()); - - bind_group_ = BindGroupBuilder() - .sampler(0, sampler) - .texture(1, input_view_) - .buffer(2, uniforms_.get().buffer, uniforms_.get().size) - .buffer(3, params_buffer_.get().buffer, params_buffer_.get().size) - .texture(4, original_view_ ? original_view_ : input_view_) - .build(ctx_.device, bgl); -} diff --git a/src/gpu/effects/cnn_effect.h b/src/gpu/effects/cnn_effect.h deleted file mode 100644 index 3e2b7ca..0000000 --- a/src/gpu/effects/cnn_effect.h +++ /dev/null @@ -1,53 +0,0 @@ -// CNN post-processing effect header -// Multi-layer neural network stylization - -#pragma once -#include "gpu/effect.h" -#include "gpu/uniform_helper.h" - -struct CNNLayerParams { - int layer_index; - float blend_amount; // Blend: mix(input, output, blend_amount) - float _pad[2]; -}; -static_assert(sizeof(CNNLayerParams) == 16); - -struct CNNEffectParams { - int layer_index = 0; // Which layer to render (0-based) - int total_layers = 1; // Total number of layers in the CNN - float blend_amount = 1.0f; // Final blend with original input -}; - -class CNNEffect : public PostProcessEffect { - public: - explicit CNNEffect(const GpuContext& ctx); - explicit CNNEffect(const GpuContext& ctx, const CNNEffectParams& params); - - void init(MainSequence* demo) override; - void resize(int width, int height) override; - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - void update_bind_group(WGPUTextureView input_view) override; - - // Layer 0 needs framebuffer capture for original input - bool needs_framebuffer_capture() const override { - return layer_index_ == 0; - } - - void set_beat_modulation(bool enabled, float scale = 1.0f) { - beat_modulated_ = enabled; - beat_scale_ = scale; - } - - private: - int layer_index_; - int total_layers_; - float blend_amount_; - bool beat_modulated_ = false; - float beat_scale_ = 1.0f; - WGPUTextureView input_view_; - WGPUTextureView original_view_; - UniformBuffer params_buffer_; - WGPUBindGroup bind_group_; - MainSequence* demo_ = nullptr; -}; diff --git a/src/gpu/effects/cnn_v2_effect.cc b/src/gpu/effects/cnn_v2_effect.cc deleted file mode 100644 index be856a4..0000000 --- a/src/gpu/effects/cnn_v2_effect.cc +++ /dev/null @@ -1,463 +0,0 @@ -// CNN v2 Effect Implementation - -#include "gpu/effects/cnn_v2_effect.h" - -#if defined(USE_TEST_ASSETS) -#include "test_assets.h" -#else -#include "generated/assets.h" -#endif - -#include "gpu/bind_group_builder.h" -#include "gpu/gpu.h" -#include "util/asset_manager.h" -#include "util/fatal_error.h" -#include - -CNNv2Effect::CNNv2Effect(const GpuContext& ctx) - : PostProcessEffect(ctx), - static_pipeline_(nullptr), - static_bind_group_(nullptr), - static_params_buffer_(nullptr), - static_features_tex_(nullptr), - static_features_view_(nullptr), - linear_sampler_(nullptr), - layer_pipeline_(nullptr), - weights_buffer_(nullptr), - input_mip_tex_(nullptr), - current_input_view_(nullptr), - blend_amount_(1.0f), - mip_level_(0), - initialized_(false) { - std::memset(input_mip_view_, 0, sizeof(input_mip_view_)); -} - -CNNv2Effect::CNNv2Effect(const GpuContext& ctx, const CNNv2EffectParams& params) - : PostProcessEffect(ctx), - static_pipeline_(nullptr), - static_bind_group_(nullptr), - static_params_buffer_(nullptr), - static_features_tex_(nullptr), - static_features_view_(nullptr), - linear_sampler_(nullptr), - layer_pipeline_(nullptr), - weights_buffer_(nullptr), - input_mip_tex_(nullptr), - current_input_view_(nullptr), - blend_amount_(params.blend_amount), - mip_level_(0), - initialized_(false) { - std::memset(input_mip_view_, 0, sizeof(input_mip_view_)); -} - -CNNv2Effect::~CNNv2Effect() { - cleanup(); -} - -void CNNv2Effect::init(MainSequence* demo) { - (void)demo; - if (initialized_) return; - - load_weights(); - create_textures(); - create_pipelines(); - - initialized_ = true; -} - -void CNNv2Effect::resize(int width, int height) { - PostProcessEffect::resize(width, height); - cleanup(); - create_textures(); - create_pipelines(); -} - -void CNNv2Effect::load_weights() { - // Load binary weights asset - size_t weights_size = 0; - const uint8_t* weights_data = (const uint8_t*)GetAsset(AssetId::ASSET_WEIGHTS_CNN_V2, &weights_size); - - if (!weights_data || weights_size < 20) { - // Weights not available - effect will skip - return; - } - - // Parse header - const uint32_t* header = (const uint32_t*)weights_data; - uint32_t magic = header[0]; - uint32_t version = header[1]; - uint32_t num_layers = header[2]; - uint32_t total_weights = header[3]; - - FATAL_CHECK(magic != 0x324e4e43, "Invalid CNN v2 weights magic\n"); // 'CNN2' - - // Support both version 1 (16-byte header) and version 2 (20-byte header with mip_level) - // TODO: Version 3 should include feature descriptor for arbitrary layout/ordering - if (version == 1) { - mip_level_ = 0; // Default for v1 - } else if (version == 2) { - mip_level_ = header[4]; - } else { - FATAL_ERROR("Unsupported CNN v2 weights version: %u\n", version); - } - - // Parse layer info (20 bytes per layer) - // Offset depends on version: v1=16 bytes (4 u32), v2=20 bytes (5 u32) - const uint32_t header_u32_count = (version == 1) ? 4 : 5; - const uint32_t* layer_data = header + header_u32_count; - for (uint32_t i = 0; i < num_layers; ++i) { - LayerInfo info; - info.kernel_size = layer_data[i * 5 + 0]; - info.in_channels = layer_data[i * 5 + 1]; - info.out_channels = layer_data[i * 5 + 2]; - info.weight_offset = layer_data[i * 5 + 3]; - info.weight_count = layer_data[i * 5 + 4]; - layer_info_.push_back(info); - } - - // Create GPU storage buffer for weights (skip header + layer info, upload only weights) - size_t header_size = 20; // 5 u32 - size_t layer_info_size = 20 * num_layers; // 5 u32 per layer - size_t weights_offset = header_size + layer_info_size; - size_t weights_only_size = weights_size - weights_offset; - - WGPUBufferDescriptor buffer_desc = {}; - buffer_desc.size = weights_only_size; - buffer_desc.usage = WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst; - buffer_desc.mappedAtCreation = false; - - weights_buffer_ = wgpuDeviceCreateBuffer(ctx_.device, &buffer_desc); - - // Upload only weights (skip header + layer info) - wgpuQueueWriteBuffer(ctx_.queue, weights_buffer_, 0, weights_data + weights_offset, weights_only_size); - - // Create uniform buffers for layer params (one per layer) - for (uint32_t i = 0; i < num_layers; ++i) { - WGPUBufferDescriptor params_desc = {}; - params_desc.size = sizeof(LayerParams); - params_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst; - params_desc.mappedAtCreation = false; - - WGPUBuffer buf = wgpuDeviceCreateBuffer(ctx_.device, ¶ms_desc); - layer_params_buffers_.push_back(buf); - } -} - -void CNNv2Effect::create_textures() { - // Static features texture (8×f16 packed as 4×u32) - TextureWithView static_tex = gpu_create_storage_texture_2d( - ctx_.device, width_, height_, WGPUTextureFormat_RGBA32Uint); - static_features_tex_ = static_tex.texture; - static_features_view_ = static_tex.view; - - // Input texture with mips (for multi-scale features) - TextureWithView input_mip = gpu_create_texture_2d( - ctx_.device, width_, height_, WGPUTextureFormat_RGBA8Unorm, - WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst, 3); - input_mip_tex_ = input_mip.texture; - - for (int i = 0; i < 3; ++i) { - input_mip_view_[i] = - gpu_create_mip_view(input_mip_tex_, WGPUTextureFormat_RGBA8Unorm, i); - } - - // Create 2 layer textures (ping-pong buffers for intermediate results) - // Each stores 8×f16 channels packed as 4×u32 - for (int i = 0; i < 2; ++i) { - TextureWithView layer = gpu_create_storage_texture_2d( - ctx_.device, width_, height_, WGPUTextureFormat_RGBA32Uint); - layer_textures_.push_back(layer.texture); - layer_views_.push_back(layer.view); - } - - // Create uniform buffer for static feature params - WGPUBufferDescriptor params_desc = {}; - params_desc.size = sizeof(StaticFeatureParams); - params_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst; - params_desc.mappedAtCreation = false; - static_params_buffer_ = wgpuDeviceCreateBuffer(ctx_.device, ¶ms_desc); -} - -void CNNv2Effect::create_pipelines() { - // Create linear sampler for bilinear interpolation - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; - sampler_desc.lodMinClamp = 0.0f; - sampler_desc.lodMaxClamp = 32.0f; - sampler_desc.maxAnisotropy = 1; - - linear_sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - - // Static features compute pipeline - size_t shader_size = 0; - const char* static_code = (const char*)GetAsset(AssetId::ASSET_SHADER_CNN_V2_STATIC, &shader_size); - - if (!static_code || shader_size == 0) { - // Shader not available (e.g., in test mode) - skip pipeline creation - return; - } - - WGPUShaderSourceWGSL wgsl_src = {}; - wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(static_code); - - WGPUShaderModuleDescriptor shader_desc = {}; - shader_desc.nextInChain = &wgsl_src.chain; - - // Create bind group layout for static features compute - // Bindings: 0=input_tex, 1=input_mip1, 2=input_mip2, 3=depth_tex, 4=output, 5=params, 6=linear_sampler - WGPUBindGroupLayout static_bgl = - BindGroupLayoutBuilder() - .texture(0, WGPUShaderStage_Compute) - .texture(1, WGPUShaderStage_Compute) - .texture(2, WGPUShaderStage_Compute) - .texture(3, WGPUShaderStage_Compute) - .storage_texture(4, WGPUShaderStage_Compute, - WGPUTextureFormat_RGBA32Uint) - .uniform(5, WGPUShaderStage_Compute, sizeof(StaticFeatureParams)) - .sampler(6, WGPUShaderStage_Compute) - .build(ctx_.device); - - // Update pipeline layout - WGPUPipelineLayoutDescriptor pl_desc = {}; - pl_desc.bindGroupLayoutCount = 1; - pl_desc.bindGroupLayouts = &static_bgl; - WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(ctx_.device, &pl_desc); - - // Recreate pipeline with proper layout - WGPUComputePipelineDescriptor pipeline_desc2 = {}; - pipeline_desc2.compute.module = wgpuDeviceCreateShaderModule(ctx_.device, &shader_desc); - pipeline_desc2.compute.entryPoint = str_view("main"); - pipeline_desc2.layout = pipeline_layout; - - if (static_pipeline_) wgpuComputePipelineRelease(static_pipeline_); - static_pipeline_ = wgpuDeviceCreateComputePipeline(ctx_.device, &pipeline_desc2); - - wgpuShaderModuleRelease(pipeline_desc2.compute.module); - wgpuPipelineLayoutRelease(pipeline_layout); - wgpuBindGroupLayoutRelease(static_bgl); - - // CNN layer compute pipeline (storage buffer version) - if (layer_info_.empty()) return; // No weights loaded - - size_t layer_shader_size = 0; - const char* layer_code = (const char*)GetAsset(AssetId::ASSET_SHADER_CNN_V2_COMPUTE, &layer_shader_size); - - if (!layer_code || layer_shader_size == 0) return; - - WGPUShaderSourceWGSL layer_wgsl = {}; - layer_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL; - layer_wgsl.code = str_view(layer_code); - - WGPUShaderModuleDescriptor layer_shader_desc = {}; - layer_shader_desc.nextInChain = &layer_wgsl.chain; - - WGPUShaderModule layer_module = wgpuDeviceCreateShaderModule(ctx_.device, &layer_shader_desc); - if (!layer_module) return; - - // Create bind group layout for layer compute - // 0=static_features, 1=layer_input, 2=output, 3=weights, 4=params, 5=original_input - WGPUBindGroupLayout layer_bgl = - BindGroupLayoutBuilder() - .uint_texture(0, WGPUShaderStage_Compute) - .uint_texture(1, WGPUShaderStage_Compute) - .storage_texture(2, WGPUShaderStage_Compute, - WGPUTextureFormat_RGBA32Uint) - .storage(3, WGPUShaderStage_Compute) - .uniform(4, WGPUShaderStage_Compute, sizeof(LayerParams)) - .texture(5, WGPUShaderStage_Compute) - .build(ctx_.device); - - WGPUPipelineLayoutDescriptor layer_pl_desc = {}; - layer_pl_desc.bindGroupLayoutCount = 1; - layer_pl_desc.bindGroupLayouts = &layer_bgl; - - WGPUPipelineLayout layer_pipeline_layout = wgpuDeviceCreatePipelineLayout(ctx_.device, &layer_pl_desc); - - WGPUComputePipelineDescriptor layer_pipeline_desc = {}; - layer_pipeline_desc.compute.module = layer_module; - layer_pipeline_desc.compute.entryPoint = str_view("main"); - layer_pipeline_desc.layout = layer_pipeline_layout; - - layer_pipeline_ = wgpuDeviceCreateComputePipeline(ctx_.device, &layer_pipeline_desc); - - wgpuShaderModuleRelease(layer_module); - wgpuPipelineLayoutRelease(layer_pipeline_layout); - wgpuBindGroupLayoutRelease(layer_bgl); -} - -void CNNv2Effect::update_bind_group(WGPUTextureView input_view) { - if (!static_pipeline_) return; - - // Cache input view - current_input_view_ = input_view; - - // Release old bind group - if (static_bind_group_) { - wgpuBindGroupRelease(static_bind_group_); - static_bind_group_ = nullptr; - } - - // Create bind group for static features compute (manual for storage texture binding) - WGPUBindGroupEntry bg_entries[7] = {}; - bg_entries[0].binding = 0; - bg_entries[0].textureView = input_view; - bg_entries[1].binding = 1; - bg_entries[1].textureView = input_mip_view_[0]; - bg_entries[2].binding = 2; - bg_entries[2].textureView = - input_mip_view_[1] ? input_mip_view_[1] : input_mip_view_[0]; - bg_entries[3].binding = 3; - bg_entries[3].textureView = input_view; - bg_entries[4].binding = 4; - bg_entries[4].textureView = static_features_view_; - bg_entries[5].binding = 5; - bg_entries[5].buffer = static_params_buffer_; - bg_entries[5].size = sizeof(StaticFeatureParams); - bg_entries[6].binding = 6; - bg_entries[6].sampler = linear_sampler_; - - WGPUBindGroupLayout layout = - wgpuComputePipelineGetBindGroupLayout(static_pipeline_, 0); - WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = layout; - bg_desc.entryCount = 7; - bg_desc.entries = bg_entries; - static_bind_group_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc); - wgpuBindGroupLayoutRelease(layout); - - // Create layer bind groups - if (!layer_pipeline_ || layer_info_.empty()) return; - - // Release old layer bind groups - for (auto bg : layer_bind_groups_) { - wgpuBindGroupRelease(bg); - } - layer_bind_groups_.clear(); - - // Get bind group layout from layer pipeline - WGPUBindGroupLayout layer_bgl = wgpuComputePipelineGetBindGroupLayout(layer_pipeline_, 0); - - // Create bind group for each layer - for (size_t i = 0; i < layer_info_.size(); ++i) { - WGPUTextureView layer_input = - (i == 0) ? static_features_view_ : layer_views_[i % 2]; - - WGPUBindGroup layer_bg = - BindGroupBuilder() - .texture(0, static_features_view_) - .texture(1, layer_input) - .texture(2, layer_views_[(i + 1) % 2]) - .buffer(3, weights_buffer_, wgpuBufferGetSize(weights_buffer_)) - .buffer(4, layer_params_buffers_[i], sizeof(LayerParams)) - .texture(5, input_view) - .build(ctx_.device, layer_bgl); - - layer_bind_groups_.push_back(layer_bg); - } - - wgpuBindGroupLayoutRelease(layer_bgl); -} - -void CNNv2Effect::compute(WGPUCommandEncoder encoder, - const CommonPostProcessUniforms& uniforms) { - if (!initialized_ || !static_pipeline_ || !static_bind_group_) return; - - float effective_blend = blend_amount_; - if (beat_modulated_) { - effective_blend = blend_amount_ * uniforms.beat_phase * beat_scale_; - } - - // Update static feature params - StaticFeatureParams static_params; - static_params.mip_level = mip_level_; - static_params.padding[0] = 0; - static_params.padding[1] = 0; - static_params.padding[2] = 0; - wgpuQueueWriteBuffer(ctx_.queue, static_params_buffer_, 0, &static_params, sizeof(static_params)); - - // Pass 1: Compute static features - WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr); - - wgpuComputePassEncoderSetPipeline(pass, static_pipeline_); - wgpuComputePassEncoderSetBindGroup(pass, 0, static_bind_group_, 0, nullptr); - - // Dispatch workgroups (8×8 threads per group) - uint32_t workgroups_x = (width_ + 7) / 8; - uint32_t workgroups_y = (height_ + 7) / 8; - wgpuComputePassEncoderDispatchWorkgroups(pass, workgroups_x, workgroups_y, 1); - - wgpuComputePassEncoderEnd(pass); - wgpuComputePassEncoderRelease(pass); - - // Execute CNN layer passes - if (!layer_pipeline_ || layer_bind_groups_.empty()) return; - - // Update layer params (each layer has own buffer) - for (size_t i = 0; i < layer_info_.size(); ++i) { - const LayerInfo& info = layer_info_[i]; - - LayerParams params; - params.kernel_size = info.kernel_size; - params.in_channels = info.in_channels; - params.out_channels = info.out_channels; - params.weight_offset = info.weight_offset; - params.is_output_layer = (i == layer_info_.size() - 1) ? 1 : 0; - params.blend_amount = effective_blend; - params.is_layer_0 = (i == 0) ? 1 : 0; - - wgpuQueueWriteBuffer(ctx_.queue, layer_params_buffers_[i], 0, ¶ms, sizeof(params)); - - WGPUComputePassEncoder layer_pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr); - - wgpuComputePassEncoderSetPipeline(layer_pass, layer_pipeline_); - wgpuComputePassEncoderSetBindGroup(layer_pass, 0, layer_bind_groups_[i], 0, nullptr); - - wgpuComputePassEncoderDispatchWorkgroups(layer_pass, workgroups_x, workgroups_y, 1); - - wgpuComputePassEncoderEnd(layer_pass); - wgpuComputePassEncoderRelease(layer_pass); - } -} - -void CNNv2Effect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - (void)pass; - (void)uniforms; - // Compute-only effect, rendering is done by default composite pass -} - -void CNNv2Effect::cleanup() { - if (static_features_view_) wgpuTextureViewRelease(static_features_view_); - if (static_features_tex_) wgpuTextureRelease(static_features_tex_); - if (static_bind_group_) wgpuBindGroupRelease(static_bind_group_); - if (static_params_buffer_) wgpuBufferRelease(static_params_buffer_); - if (static_pipeline_) wgpuComputePipelineRelease(static_pipeline_); - if (linear_sampler_) wgpuSamplerRelease(linear_sampler_); - - if (layer_pipeline_) wgpuComputePipelineRelease(layer_pipeline_); - if (weights_buffer_) wgpuBufferRelease(weights_buffer_); - for (auto buf : layer_params_buffers_) wgpuBufferRelease(buf); - layer_params_buffers_.clear(); - - for (int i = 0; i < 3; ++i) { - if (input_mip_view_[i]) wgpuTextureViewRelease(input_mip_view_[i]); - } - if (input_mip_tex_) wgpuTextureRelease(input_mip_tex_); - - for (auto view : layer_views_) wgpuTextureViewRelease(view); - for (auto tex : layer_textures_) wgpuTextureRelease(tex); - for (auto bg : layer_bind_groups_) wgpuBindGroupRelease(bg); - - layer_views_.clear(); - layer_textures_.clear(); - layer_bind_groups_.clear(); - layer_info_.clear(); - - initialized_ = false; -} diff --git a/src/gpu/effects/cnn_v2_effect.h b/src/gpu/effects/cnn_v2_effect.h deleted file mode 100644 index d530d3b..0000000 --- a/src/gpu/effects/cnn_v2_effect.h +++ /dev/null @@ -1,88 +0,0 @@ -// CNN v2 Effect - Parametric Static Features -// Multi-pass post-processing with 7D feature input -// Supports per-layer kernel sizes (e.g., 1×1, 3×3, 5×5) - -#pragma once -#include "gpu/effect.h" -#include - -struct CNNv2EffectParams { - float blend_amount = 1.0f; -}; - -class CNNv2Effect : public PostProcessEffect { -public: - explicit CNNv2Effect(const GpuContext& ctx); - explicit CNNv2Effect(const GpuContext& ctx, const CNNv2EffectParams& params); - ~CNNv2Effect(); - - void init(MainSequence* demo) override; - void resize(int width, int height) override; - void compute(WGPUCommandEncoder encoder, - const CommonPostProcessUniforms& uniforms) override; - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - void update_bind_group(WGPUTextureView input_view) override; - - void set_beat_modulation(bool enabled, float scale = 1.0f) { - beat_modulated_ = enabled; - beat_scale_ = scale; - } - -private: - struct LayerInfo { - uint32_t kernel_size; - uint32_t in_channels; - uint32_t out_channels; - uint32_t weight_offset; - uint32_t weight_count; - }; - - struct LayerParams { - uint32_t kernel_size; - uint32_t in_channels; - uint32_t out_channels; - uint32_t weight_offset; - uint32_t is_output_layer; - float blend_amount; - uint32_t is_layer_0; - }; - - struct StaticFeatureParams { - uint32_t mip_level; - uint32_t padding[3]; - }; - - void create_textures(); - void create_pipelines(); - void load_weights(); - void cleanup(); - - // Static features compute - WGPUComputePipeline static_pipeline_; - WGPUBindGroup static_bind_group_; - WGPUBuffer static_params_buffer_; - WGPUTexture static_features_tex_; - WGPUTextureView static_features_view_; - WGPUSampler linear_sampler_; - - // CNN layers (storage buffer architecture) - WGPUComputePipeline layer_pipeline_; // Single pipeline for all layers - WGPUBuffer weights_buffer_; // Storage buffer for weights - std::vector layer_params_buffers_; // Uniform buffers (one per layer) - std::vector layer_info_; // Layer metadata - std::vector layer_bind_groups_; // Per-layer bind groups - std::vector layer_textures_; // Ping-pong buffers - std::vector layer_views_; - - // Input mips - WGPUTexture input_mip_tex_; - WGPUTextureView input_mip_view_[3]; - WGPUTextureView current_input_view_; - - float blend_amount_ = 1.0f; - bool beat_modulated_ = false; - float beat_scale_ = 1.0f; - uint32_t mip_level_ = 0; - bool initialized_; -}; diff --git a/src/gpu/effects/distort_effect.cc b/src/gpu/effects/distort_effect.cc deleted file mode 100644 index 97622b2..0000000 --- a/src/gpu/effects/distort_effect.cc +++ /dev/null @@ -1,36 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the DistortEffect. - -#include "gpu/demo_effects.h" -#include "gpu/gpu.h" - -// --- DistortEffect --- -DistortEffect::DistortEffect(const GpuContext& ctx) - : DistortEffect(ctx, DistortParams()) { -} - -DistortEffect::DistortEffect(const GpuContext& ctx, const DistortParams& params) - : PostProcessEffect(ctx), params_(params) { - params_buffer_.init(ctx_.device); - pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, - distort_shader_wgsl); -} - -void DistortEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - // Populate CommonPostProcessUniforms - uniforms_.update(ctx_.queue, uniforms); - - // Populate DistortParams - const DistortParams distort_p = { - .strength = params_.strength, - .speed = params_.speed, - }; - params_buffer_.update(ctx_.queue, distort_p); - - PostProcessEffect::render(pass, uniforms); -} - -void DistortEffect::update_bind_group(WGPUTextureView v) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, uniforms_.get(), params_buffer_); -} \ No newline at end of file diff --git a/src/gpu/effects/fade_effect.cc b/src/gpu/effects/fade_effect.cc deleted file mode 100644 index 93684d8..0000000 --- a/src/gpu/effects/fade_effect.cc +++ /dev/null @@ -1,98 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the FadeEffect - fades to/from black based on time. - -#include "gpu/effects/fade_effect.h" -#include "gpu/effects/post_process_helper.h" -#include - -struct FadeParams { - float fade_amount; - float _pad[3]; -}; -static_assert(sizeof(FadeParams) == 16, "FadeParams must be 16 bytes for WGSL alignment"); - -FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) { - const char* shader_code = R"( - struct VertexOutput { - @builtin(position) position: vec4, - @location(0) uv: vec2, - }; - - struct CommonUniforms { - resolution: vec2, - _pad0: f32, - _pad1: f32, - aspect_ratio: f32, - time: f32, - beat: f32, - audio_intensity: f32, - }; - - struct FadeParams { - fade_amount: f32, - _pad0: f32, - _pad1: f32, - _pad2: f32, - }; - - @group(0) @binding(0) var inputSampler: sampler; - @group(0) @binding(1) var inputTexture: texture_2d; - @group(0) @binding(2) var uniforms: CommonUniforms; - @group(0) @binding(3) var params: FadeParams; - - @vertex - fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { - var output: VertexOutput; - var pos = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - output.position = vec4(pos[vertexIndex], 0.0, 1.0); - output.uv = pos[vertexIndex] * 0.5 + 0.5; - return output; - } - - @fragment - fn fs_main(input: VertexOutput) -> @location(0) vec4 { - let color = textureSample(inputTexture, inputSampler, input.uv); - // Fade to black: 0.0 = black, 1.0 = full color - return vec4(color.rgb * params.fade_amount, color.a); - } - )"; - - pipeline_ = - create_post_process_pipeline(ctx_.device, ctx_.format, shader_code); - params_buffer_ = gpu_create_buffer( - ctx_.device, 16, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst); -} - -void FadeEffect::update_bind_group(WGPUTextureView input_view) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - uniforms_.get(), params_buffer_); -} - -void FadeEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - - // Example fade pattern: fade in at start, fade out at end - // Customize this based on your needs - float fade_amount = 1.0f; - if (uniforms.time < 2.0f) { - // Fade in from black over first 2 seconds - fade_amount = uniforms.time / 2.0f; - } else if (uniforms.time > 36.0f) { - // Fade out to black after 36 seconds - fade_amount = 1.0f - ((uniforms.time - 36.0f) / 4.0f); - fade_amount = fmaxf(fade_amount, 0.0f); - } - - FadeParams params = {fade_amount, {0.0f, 0.0f, 0.0f}}; - wgpuQueueWriteBuffer(ctx_.queue, params_buffer_.buffer, 0, ¶ms, - sizeof(params)); - - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} diff --git a/src/gpu/effects/fade_effect.h b/src/gpu/effects/fade_effect.h deleted file mode 100644 index 3360a5f..0000000 --- a/src/gpu/effects/fade_effect.h +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of the 64k demo project. -// It declares the FadeEffect - fades to/from black. - -#pragma once - -#include "gpu/effect.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" -#include "gpu/uniform_helper.h" - -class FadeEffect : public PostProcessEffect { - public: - FadeEffect(const GpuContext& ctx); - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - void update_bind_group(WGPUTextureView input_view) override; - - private: - GpuBuffer params_buffer_; -}; diff --git a/src/gpu/effects/flash_cube_effect.cc b/src/gpu/effects/flash_cube_effect.cc deleted file mode 100644 index 506f11c..0000000 --- a/src/gpu/effects/flash_cube_effect.cc +++ /dev/null @@ -1,104 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the FlashCubeEffect - a flashing background cube with Perlin -// noise. - -#include "gpu/effects/flash_cube_effect.h" -#include "generated/assets.h" -#include "util/asset_manager_utils.h" -#include -#include - -FlashCubeEffect::FlashCubeEffect(const GpuContext& ctx) : Effect(ctx) { -} - -void FlashCubeEffect::resize(int width, int height) { - if (width == width_ && height == height_) - return; - - Effect::resize(width, height); - - if (!initialized_) - return; - - renderer_.resize(width_, height_); -} - -void FlashCubeEffect::init(MainSequence* demo) { - (void)demo; - WGPUTextureFormat format = demo->gpu_ctx.format; - - renderer_.init(ctx_.device, ctx_.queue, ctx_.format); - renderer_.resize(width_, height_); - initialized_ = true; - - // Texture Manager - texture_manager_.init(ctx_.device, ctx_.queue); - - // Load Perlin noise texture - TextureAsset noise_tex = GetTextureAsset(AssetId::ASSET_NOISE_TEX); - if (noise_tex.pixels && noise_tex.width == 256 && noise_tex.height == 256) { - texture_manager_.create_texture("noise", noise_tex.width, noise_tex.height, - noise_tex.pixels); - renderer_.set_noise_texture(texture_manager_.get_texture_view("noise")); - } else { - std::cerr << "Failed to load NOISE_TEX asset for FlashCubeEffect." - << std::endl; - } - - // Create a very large background cube - // Scale and distance ensure it's clearly behind foreground objects - scene_.clear(); - Object3D cube(ObjectType::BOX); - cube.position = vec3(0, 0, 0); - cube.scale = vec3(100.0f, 100.0f, 100.0f); // Much larger cube - cube.color = vec4(0.3f, 0.3f, 0.5f, 1.0f); // Dark blue base color - scene_.add_object(cube); -} - -void FlashCubeEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - // Detect beat changes for flash trigger (using intensity as proxy for beat - // hits) Intensity spikes on beats, so we can use it to trigger flashes - if (uniforms.audio_intensity > 0.5f && - flash_intensity_ < 0.3f) { // High intensity + flash cooled down - flash_intensity_ = 1.0f; // Trigger full flash - } - - // Exponential decay of flash - flash_intensity_ *= 0.90f; // Slower fade for more visible effect - - // Always have base brightness, add flash on top - float base_brightness = 0.2f; - float flash_boost = - base_brightness + flash_intensity_ * 0.8f; // 0.2 to 1.0 range - - scene_.objects[0].color = - vec4(0.4f * flash_boost, // Reddish tint - 0.6f * flash_boost, // More green - 1.0f * flash_boost, // Strong blue for background feel - 1.0f); - - // Slowly rotate the cube for visual interest - scene_.objects[0].rotation = - quat::from_axis(vec3(0.3f, 1, 0.2f), uniforms.time * 0.05f); - - // Position camera OUTSIDE the cube looking at it from a distance - // This way we see the cube as a background element - float cam_distance = 150.0f; // Much farther to ensure it's behind everything - float orbit_angle = uniforms.time * 0.1f; - - camera_.set_look_at( - vec3(std::sin(orbit_angle) * cam_distance, - std::cos(orbit_angle * 0.3f) * 30.0f, - std::cos(orbit_angle) * cam_distance), // Camera orbits around - vec3(0, 0, 0), // Look at cube center - vec3(0, 1, 0)); - - camera_.aspect_ratio = uniforms.aspect_ratio; - // Extend far plane to accommodate distant camera position (150 units + cube - // size) - camera_.far_plane = 300.0f; - - // Draw the cube - renderer_.draw(pass, scene_, camera_, uniforms.time); -} diff --git a/src/gpu/effects/flash_cube_effect.h b/src/gpu/effects/flash_cube_effect.h deleted file mode 100644 index df30b5b..0000000 --- a/src/gpu/effects/flash_cube_effect.h +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the 64k demo project. -// It implements a flashing cube effect with Perlin noise texture. -// The cube is large and we're inside it, flashing in sync with the beat. - -#pragma once -#include "3d/camera.h" -#include "3d/renderer.h" -#include "3d/scene.h" -#include "gpu/effect.h" -#include "gpu/texture_manager.h" - -class FlashCubeEffect : public Effect { - public: - FlashCubeEffect(const GpuContext& ctx); - void init(MainSequence* demo) override; - void resize(int width, int height) override; - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - - private: - Renderer3D renderer_; - TextureManager texture_manager_; - Scene scene_; - Camera camera_; - float last_beat_ = 0.0f; - float flash_intensity_ = 0.0f; - bool initialized_ = false; -}; diff --git a/src/gpu/effects/flash_effect.cc b/src/gpu/effects/flash_effect.cc deleted file mode 100644 index e53cbce..0000000 --- a/src/gpu/effects/flash_effect.cc +++ /dev/null @@ -1,94 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the FlashEffect - brief flash on u.beat hits. -// Now supports parameterized color with per-frame animation. - -#include "gpu/effects/flash_effect.h" -#include "gpu/effects/post_process_helper.h" -#include - -// Backward compatibility constructor (delegates to parameterized constructor) -FlashEffect::FlashEffect(const GpuContext& ctx) - : FlashEffect(ctx, FlashEffectParams{}) { -} - -// Parameterized constructor -FlashEffect::FlashEffect(const GpuContext& ctx, const FlashEffectParams& params) - : PostProcessEffect(ctx), params_(params) { - const char* shader_code = R"( - struct VertexOutput { - @builtin(position) position: vec4, - @location(0) uv: vec2, - }; - - struct Uniforms { - flash_intensity: f32, - audio_intensity: f32, - flash_color: vec3, // Parameterized color - _pad: f32, - }; - - @group(0) @binding(0) var inputSampler: sampler; - @group(0) @binding(1) var inputTexture: texture_2d; - @group(0) @binding(2) var uniforms: Uniforms; - - @vertex - fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { - var output: VertexOutput; - var pos = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - output.position = vec4(pos[vertexIndex], 0.0, 1.0); - output.uv = pos[vertexIndex] * 0.5 + 0.5; - return output; - } - - @fragment - fn fs_main(input: VertexOutput) -> @location(0) vec4 { - let color = textureSample(inputTexture, inputSampler, input.uv); - // Use parameterized flash color instead of hardcoded white - var flashed = mix(color.rgb, uniforms.flash_color, uniforms.flash_intensity); - return vec4(flashed, color.a); - } - )"; - - pipeline_ = - create_post_process_pipeline(ctx_.device, ctx_.format, shader_code); - flash_uniforms_.init(ctx_.device); -} - -void FlashEffect::update_bind_group(WGPUTextureView input_view) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - flash_uniforms_.get(), {}); -} - -void FlashEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - // Trigger flash based on configured threshold - if (uniforms.audio_intensity > params_.trigger_threshold && flash_intensity_ < 0.2f) { - flash_intensity_ = 0.8f; // Trigger flash - } - - // Decay based on configured rate - flash_intensity_ *= params_.decay_rate; - - // *** PER-FRAME PARAMETER COMPUTATION *** - // Animate color based on time and beat - const float r = params_.color[0] * (0.5f + 0.5f * sinf(uniforms.time * 0.5f)); - const float g = params_.color[1] * (0.5f + 0.5f * cosf(uniforms.time * 0.7f)); - const float b = params_.color[2] * (1.0f + 0.3f * uniforms.beat_phase); - - // Update uniforms with computed (animated) values - const FlashUniforms u = { - .flash_intensity = flash_intensity_, - .intensity = uniforms.audio_intensity, - ._pad1 = {0.0f, 0.0f}, // Padding for vec3 alignment - .color = {r, g, b}, // Time-dependent, computed every frame - ._pad2 = 0.0f}; - flash_uniforms_.update(ctx_.queue, u); - - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} diff --git a/src/gpu/effects/flash_effect.h b/src/gpu/effects/flash_effect.h deleted file mode 100644 index 1ac75a4..0000000 --- a/src/gpu/effects/flash_effect.h +++ /dev/null @@ -1,45 +0,0 @@ -// This file is part of the 64k demo project. -// It declares the FlashEffect - brief white flash on beat hits. - -#pragma once - -#include "gpu/effect.h" -#include "gpu/gpu.h" -#include "gpu/uniform_helper.h" - -// Parameters for FlashEffect (set at construction time) -struct FlashEffectParams { - float color[3] = {1.0f, 1.0f, 1.0f}; // Default: white - float decay_rate = 0.98f; // Default: fast decay - float trigger_threshold = 0.7f; // Default: trigger on strong beats -}; - -// Uniform data sent to GPU shader -// IMPORTANT: Must match WGSL struct layout with proper alignment -// vec3 in WGSL has 16-byte alignment, not 12-byte! -struct FlashUniforms { - float flash_intensity; // offset 0 - float intensity; // offset 4 - float _pad1[2]; // offset 8-15 (padding for vec3 alignment) - float color[3]; // offset 16-27 (vec3 aligned to 16 bytes) - float _pad2; // offset 28-31 -}; -static_assert(sizeof(FlashUniforms) == 32, - "FlashUniforms must be 32 bytes for WGSL alignment"); - -class FlashEffect : public PostProcessEffect { - public: - // Backward compatibility constructor (uses default params) - FlashEffect(const GpuContext& ctx); - // New parameterized constructor - FlashEffect(const GpuContext& ctx, const FlashEffectParams& params); - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - void update_bind_group(WGPUTextureView input_view) override; - - private: - FlashEffectParams params_; - UniformBuffer flash_uniforms_; - UniformBuffer params_buffer_; - float flash_intensity_ = 0.0f; -}; diff --git a/src/gpu/effects/gaussian_blur_effect.cc b/src/gpu/effects/gaussian_blur_effect.cc deleted file mode 100644 index 4421e33..0000000 --- a/src/gpu/effects/gaussian_blur_effect.cc +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the GaussianBlurEffect with parameterization. - -#include "gpu/demo_effects.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" - -// --- GaussianBlurEffect --- - -// Backward compatibility constructor (delegates to parameterized constructor) -GaussianBlurEffect::GaussianBlurEffect(const GpuContext& ctx) - : GaussianBlurEffect(ctx, GaussianBlurParams{}) { -} - -// Parameterized constructor -GaussianBlurEffect::GaussianBlurEffect(const GpuContext& ctx, - const GaussianBlurParams& params) - : PostProcessEffect(ctx), params_(params) { - pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, - gaussian_blur_shader_wgsl); - params_buffer_.init(ctx_.device); -} - -void GaussianBlurEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - // Update uniforms with current state and parameters - uniforms_.update(ctx_.queue, uniforms); - params_buffer_.update(ctx_.queue, params_); - - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} - -void GaussianBlurEffect::update_bind_group(WGPUTextureView input_view) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - uniforms_.get(), params_buffer_.get()); -} diff --git a/src/gpu/effects/heptagon_effect.cc b/src/gpu/effects/heptagon_effect.cc deleted file mode 100644 index 724eabb..0000000 --- a/src/gpu/effects/heptagon_effect.cc +++ /dev/null @@ -1,22 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the HeptagonEffect. - -#include "gpu/demo_effects.h" -#include "gpu/gpu.h" -#include "util/mini_math.h" - -// --- HeptagonEffect --- -HeptagonEffect::HeptagonEffect(const GpuContext& ctx) : Effect(ctx) { - // uniforms_ is initialized by Effect base class - ResourceBinding bindings[] = {{uniforms_.get(), WGPUBufferBindingType_Uniform}}; - pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, main_shader_wgsl, - bindings, 1); - pass_.vertex_count = 21; -} -void HeptagonEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline); - wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, pass_.vertex_count, 1, 0, 0); -} diff --git a/src/gpu/effects/hybrid_3d_effect.cc b/src/gpu/effects/hybrid_3d_effect.cc deleted file mode 100644 index a46ab4c..0000000 --- a/src/gpu/effects/hybrid_3d_effect.cc +++ /dev/null @@ -1,147 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the Hybrid3DEffect. - -#include "gpu/effects/hybrid_3d_effect.h" -#include "generated/assets.h" -#include "util/asset_manager_utils.h" -#include -#include -#include - -Hybrid3DEffect::Hybrid3DEffect(const GpuContext& ctx) : Effect(ctx) { -} - -void Hybrid3DEffect::resize(int width, int height) { - if (width == width_ && height == height_) - return; - - Effect::resize(width, height); - - if (!initialized_) - return; - - renderer_.resize(width_, height_); -} - -void Hybrid3DEffect::init(MainSequence* demo) { - (void)demo; - WGPUTextureFormat format = - demo->gpu_ctx.format; // Get current format from MainSequence (might be - // different than constructor if resized) - - renderer_.init(ctx_.device, ctx_.queue, ctx_.format); - renderer_.resize(width_, height_); - initialized_ = true; - - // Texture Manager - texture_manager_.init(ctx_.device, ctx_.queue); - - // Load Noise Asset - TextureAsset noise_tex = GetTextureAsset(AssetId::ASSET_NOISE_TEX); - if (noise_tex.pixels && noise_tex.width == 256 && noise_tex.height == 256) { - texture_manager_.create_texture("noise", noise_tex.width, noise_tex.height, - noise_tex.pixels); - renderer_.set_noise_texture(texture_manager_.get_texture_view("noise")); - } else { - std::cerr << "Failed to load NOISE_TEX asset." << std::endl; - } - - // Setup Scene - scene_.clear(); - Object3D center(ObjectType::BOX); // Use BOX for bumps - center.position = vec3(0, 0, 0); - center.color = vec4(1, 0, 0, 1); - scene_.add_object(center); - - for (int i = 0; i < 8; ++i) { - ObjectType type = ObjectType::SPHERE; - if (i % 3 == 1) - type = ObjectType::TORUS; - if (i % 3 == 2) - type = ObjectType::BOX; - - Object3D obj(type); - - float angle = (i / 8.0f) * 6.28318f; - - obj.position = vec3(std::cos(angle) * 4.0f, 0, std::sin(angle) * 4.0f); - - obj.scale = vec3(0.7f, 0.7f, 0.7f); // Increased scale by 40% - - if (type == ObjectType::SPHERE) - obj.color = vec4(0, 1, 0, 1); - - else if (type == ObjectType::TORUS) - obj.color = vec4(0, 0.5, 1, 1); - else - obj.color = vec4(1, 1, 0, 1); - - scene_.add_object(obj); - } -} - -// Cubic ease-in/out function for non-linear motion - -static float ease_in_out_cubic(float t) { - t *= 2.0f; - - if (t < 1.0f) { - return 0.5f * t * t * t; - } - - t -= 2.0f; - - return 0.5f * (t * t * t + 2.0f); -} - -void Hybrid3DEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - // Animate Objects - - for (size_t i = 1; i < scene_.objects.size(); ++i) { - scene_.objects[i].rotation = - quat::from_axis(vec3(0, 1, 0), uniforms.time * 2.0f + i); - - scene_.objects[i].position.y = std::sin(uniforms.time * 3.0f + i) * 1.5f; - } - - // Camera jumps every other pattern (2 seconds) for dramatic effect - int pattern_num = (int)(uniforms.time / 2.0f); - int camera_preset = pattern_num % 4; // Cycle through 4 different angles - - vec3 cam_pos, cam_target; - - switch (camera_preset) { - case 0: // High angle, orbiting - { - float angle = uniforms.time * 0.5f; - cam_pos = vec3(std::sin(angle) * 12.0f, 8.0f, std::cos(angle) * 12.0f); - cam_target = vec3(0, 0, 0); - } break; - case 1: // Low angle, close-up - { - float angle = uniforms.time * 0.3f + 1.57f; // Offset angle - cam_pos = vec3(std::sin(angle) * 6.0f, 2.0f, std::cos(angle) * 6.0f); - cam_target = vec3(0, 1, 0); - } break; - case 2: // Side view, sweeping - { - float sweep = std::sin(uniforms.time * 0.4f) * 10.0f; - cam_pos = vec3(sweep, 5.0f, 8.0f); - cam_target = vec3(0, 0, 0); - } break; - case 3: // Top-down, rotating - { - float angle = uniforms.time * 0.6f; - cam_pos = vec3(std::sin(angle) * 5.0f, 12.0f, std::cos(angle) * 5.0f); - cam_target = vec3(0, 0, 0); - } break; - } - - camera_.set_look_at(cam_pos, cam_target, vec3(0, 1, 0)); - camera_.aspect_ratio = uniforms.aspect_ratio; - - // Draw - - renderer_.draw(pass, scene_, camera_, uniforms.time); -} diff --git a/src/gpu/effects/hybrid_3d_effect.h b/src/gpu/effects/hybrid_3d_effect.h deleted file mode 100644 index 818b65c..0000000 --- a/src/gpu/effects/hybrid_3d_effect.h +++ /dev/null @@ -1,29 +0,0 @@ -// This file is part of the 64k demo project. -// It defines the Hybrid3DEffect, integrating the 3D renderer into the demo -// timeline. - -#pragma once - -#include "3d/camera.h" -#include "3d/renderer.h" -#include "3d/scene.h" -#include "gpu/effect.h" -#include "gpu/texture_manager.h" - -class Hybrid3DEffect : public Effect { - public: - Hybrid3DEffect(const GpuContext& ctx); - virtual ~Hybrid3DEffect() override = default; - - void init(MainSequence* demo) override; - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - void resize(int width, int height) override; - - private: - Renderer3D renderer_; - TextureManager texture_manager_; - Scene scene_; - Camera camera_; - bool initialized_ = false; -}; diff --git a/src/gpu/effects/moving_ellipse_effect.cc b/src/gpu/effects/moving_ellipse_effect.cc deleted file mode 100644 index bbd3c08..0000000 --- a/src/gpu/effects/moving_ellipse_effect.cc +++ /dev/null @@ -1,22 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the MovingEllipseEffect. - -#include "gpu/demo_effects.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" - -// --- MovingEllipseEffect --- -MovingEllipseEffect::MovingEllipseEffect(const GpuContext& ctx) : Effect(ctx) { - // uniforms_ is initialized by Effect base class - ResourceBinding bindings[] = {{uniforms_.get(), WGPUBufferBindingType_Uniform}}; - pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, ellipse_shader_wgsl, - bindings, 1); - pass_.vertex_count = 3; -} -void MovingEllipseEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline); - wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} diff --git a/src/gpu/effects/particle_spray_effect.cc b/src/gpu/effects/particle_spray_effect.cc deleted file mode 100644 index 9b615d0..0000000 --- a/src/gpu/effects/particle_spray_effect.cc +++ /dev/null @@ -1,48 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the ParticleSprayEffect. - -#include "gpu/demo_effects.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" -#include - -// --- ParticleSprayEffect --- -ParticleSprayEffect::ParticleSprayEffect(const GpuContext& ctx) : Effect(ctx) { - std::vector init_p(NUM_PARTICLES); - for (Particle& p : init_p) - p.pos[3] = 0.0f; - particles_buffer_ = gpu_create_buffer( - ctx_.device, sizeof(Particle) * NUM_PARTICLES, - WGPUBufferUsage_Storage | WGPUBufferUsage_Vertex, init_p.data()); - ResourceBinding cb[] = {{particles_buffer_, WGPUBufferBindingType_Storage}, - {uniforms_.get(), WGPUBufferBindingType_Uniform}}; - compute_pass_ = - gpu_create_compute_pass(ctx_.device, particle_spray_compute_wgsl, cb, 2); - compute_pass_.workgroup_size_x = (NUM_PARTICLES + 63) / 64; - ResourceBinding rb[] = { - {particles_buffer_, WGPUBufferBindingType_ReadOnlyStorage}, - {uniforms_.get(), WGPUBufferBindingType_Uniform}}; - render_pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, - particle_render_wgsl, rb, 2); - render_pass_.vertex_count = 6; - render_pass_.instance_count = NUM_PARTICLES; -} -void ParticleSprayEffect::compute(WGPUCommandEncoder e, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(e, nullptr); - wgpuComputePassEncoderSetPipeline(pass, compute_pass_.pipeline); - wgpuComputePassEncoderSetBindGroup(pass, 0, compute_pass_.bind_group, 0, - nullptr); - wgpuComputePassEncoderDispatchWorkgroups(pass, compute_pass_.workgroup_size_x, - 1, 1); - wgpuComputePassEncoderEnd(pass); -} -void ParticleSprayEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - (void)uniforms; - wgpuRenderPassEncoderSetPipeline(pass, render_pass_.pipeline); - wgpuRenderPassEncoderSetBindGroup(pass, 0, render_pass_.bind_group, 0, - nullptr); - wgpuRenderPassEncoderDraw(pass, 6, NUM_PARTICLES, 0, 0); -} diff --git a/src/gpu/effects/particles_effect.cc b/src/gpu/effects/particles_effect.cc deleted file mode 100644 index f8c18f0..0000000 --- a/src/gpu/effects/particles_effect.cc +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the ParticlesEffect. - -#include "gpu/demo_effects.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" -#include - -// --- ParticlesEffect --- -ParticlesEffect::ParticlesEffect(const GpuContext& ctx) : Effect(ctx) { - std::vector init_p(NUM_PARTICLES); - particles_buffer_ = gpu_create_buffer( - ctx_.device, sizeof(Particle) * NUM_PARTICLES, - WGPUBufferUsage_Storage | WGPUBufferUsage_Vertex, init_p.data()); - ResourceBinding cb[] = {{particles_buffer_, WGPUBufferBindingType_Storage}, - {uniforms_.get(), WGPUBufferBindingType_Uniform}}; - compute_pass_ = - gpu_create_compute_pass(ctx_.device, particle_compute_wgsl, cb, 2); - compute_pass_.workgroup_size_x = (NUM_PARTICLES + 63) / 64; - ResourceBinding rb[] = { - {particles_buffer_, WGPUBufferBindingType_ReadOnlyStorage}, - {uniforms_.get(), WGPUBufferBindingType_Uniform}}; - render_pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, - particle_render_wgsl, rb, 2); - render_pass_.vertex_count = 6; - render_pass_.instance_count = NUM_PARTICLES; -} -void ParticlesEffect::compute(WGPUCommandEncoder e, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(e, nullptr); - wgpuComputePassEncoderSetPipeline(pass, compute_pass_.pipeline); - wgpuComputePassEncoderSetBindGroup(pass, 0, compute_pass_.bind_group, 0, - nullptr); - wgpuComputePassEncoderDispatchWorkgroups(pass, compute_pass_.workgroup_size_x, - 1, 1); - wgpuComputePassEncoderEnd(pass); -} -void ParticlesEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - (void)uniforms; - wgpuRenderPassEncoderSetPipeline(pass, render_pass_.pipeline); - wgpuRenderPassEncoderSetBindGroup(pass, 0, render_pass_.bind_group, 0, - nullptr); - wgpuRenderPassEncoderDraw(pass, render_pass_.vertex_count, - render_pass_.instance_count, 0, 0); -} diff --git a/src/gpu/effects/passthrough_effect.cc b/src/gpu/effects/passthrough_effect.cc deleted file mode 100644 index aedb387..0000000 --- a/src/gpu/effects/passthrough_effect.cc +++ /dev/null @@ -1,17 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the PassthroughEffect. - -#include "gpu/demo_effects.h" -#include "gpu/gpu.h" - -// --- PassthroughEffect --- -PassthroughEffect::PassthroughEffect(const GpuContext& ctx) - : PostProcessEffect(ctx) { - pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, - passthrough_shader_wgsl); -} -void PassthroughEffect::update_bind_group(WGPUTextureView input_view) { - uniforms_.update(ctx_.queue, get_common_uniforms()); - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - uniforms_.get(), {}); -} diff --git a/src/gpu/effects/post_process_helper.cc b/src/gpu/effects/post_process_helper.cc deleted file mode 100644 index 0c339c7..0000000 --- a/src/gpu/effects/post_process_helper.cc +++ /dev/null @@ -1,58 +0,0 @@ -// This file is part of the 64k demo project. -// It implements helper functions for post-processing effects. - -#include "post_process_helper.h" -#include "../demo_effects.h" -#include "gpu/gpu.h" -#include "gpu/effects/shader_composer.h" -#include "gpu/bind_group_builder.h" -#include "gpu/sampler_cache.h" -#include "gpu/pipeline_builder.h" -#include - -// Helper to create a standard post-processing pipeline -WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, - WGPUTextureFormat format, - const char* shader_code) { - WGPUBindGroupLayout bgl = BindGroupLayoutBuilder() - .sampler(PP_BINDING_SAMPLER, WGPUShaderStage_Fragment) - .texture(PP_BINDING_TEXTURE, WGPUShaderStage_Fragment) - .uniform(PP_BINDING_UNIFORMS, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment) - .uniform(PP_BINDING_EFFECT_PARAMS, WGPUShaderStage_Fragment) - .build(device); - - WGPURenderPipeline pipeline = RenderPipelineBuilder(device) - .shader(shader_code) - .bind_group_layout(bgl) - .format(format) - .build(); - - wgpuBindGroupLayoutRelease(bgl); - return pipeline; -} - -// --- PostProcess Implementation Helper --- -static GpuBuffer g_dummy_buffer = {nullptr, 0}; - -void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, - WGPUBindGroup* bind_group, WGPUTextureView input_view, - GpuBuffer uniforms, GpuBuffer effect_params) { - if (!g_dummy_buffer.buffer) { - g_dummy_buffer = gpu_create_buffer(device, 32, WGPUBufferUsage_Uniform); - } - - if (*bind_group) - wgpuBindGroupRelease(*bind_group); - - WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline, 0); - WGPUSampler sampler = SamplerCache::Get().get_or_create(device, SamplerCache::linear()); - - *bind_group = BindGroupBuilder() - .sampler(PP_BINDING_SAMPLER, sampler) - .texture(PP_BINDING_TEXTURE, input_view) - .buffer(PP_BINDING_UNIFORMS, uniforms.buffer, uniforms.size) - .buffer(PP_BINDING_EFFECT_PARAMS, - effect_params.buffer ? effect_params.buffer : g_dummy_buffer.buffer, - effect_params.buffer ? effect_params.size : g_dummy_buffer.size) - .build(device, bgl); -} diff --git a/src/gpu/effects/post_process_helper.h b/src/gpu/effects/post_process_helper.h deleted file mode 100644 index 1c649e7..0000000 --- a/src/gpu/effects/post_process_helper.h +++ /dev/null @@ -1,37 +0,0 @@ -// This file is part of the 64k demo project. -// It declares helper functions for post-processing effects. - -#pragma once - -#include "gpu/gpu.h" -#include "util/mini_math.h" - -// Uniform data common to all post-processing effects -struct CommonPostProcessUniforms { - vec2 resolution; // Screen dimensions - float aspect_ratio; // Width/height ratio - float time; // Physical time in seconds (unaffected by tempo) - float beat_time; // Musical time in beats (absolute, tempo-scaled) - float beat_phase; // Fractional beat (0.0-1.0 within current beat) - float audio_intensity;// Audio peak for beat sync - float _pad; // Padding for 16-byte alignment -}; -static_assert(sizeof(CommonPostProcessUniforms) == 32, - "CommonPostProcessUniforms must be 32 bytes for WGSL alignment"); - -// Standard post-process bind group layout (group 0): -#define PP_BINDING_SAMPLER 0 // Sampler for input texture -#define PP_BINDING_TEXTURE 1 // Input texture (previous render pass) -#define PP_BINDING_UNIFORMS 2 // Custom uniforms buffer -#define PP_BINDING_EFFECT_PARAMS 3 // Effect-specific parameters - -// Helper to create a standard post-processing pipeline -// Uniforms are accessible to both vertex and fragment shaders -WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, - WGPUTextureFormat format, - const char* shader_code); - -// Helper to update bind group for post-processing effects -void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, - WGPUBindGroup* bind_group, WGPUTextureView input_view, - GpuBuffer uniforms, GpuBuffer effect_params); diff --git a/src/gpu/effects/rotating_cube_effect.cc b/src/gpu/effects/rotating_cube_effect.cc deleted file mode 100644 index 96b02f1..0000000 --- a/src/gpu/effects/rotating_cube_effect.cc +++ /dev/null @@ -1,200 +0,0 @@ -// This file is part of the 64k demo project. -// It implements RotatingCubeEffect for bump-mapped rotating cube rendering. -// Uses auxiliary texture masking to render only inside a circular region. - -#include "gpu/effects/rotating_cube_effect.h" -#include "generated/assets.h" -#include "gpu/bind_group_builder.h" -#include "gpu/effects/shader_composer.h" -#include "gpu/gpu.h" -#include "gpu/sampler_cache.h" -#include "util/asset_manager_utils.h" - -RotatingCubeEffect::RotatingCubeEffect(const GpuContext& ctx) : Effect(ctx) { -} - -RotatingCubeEffect::~RotatingCubeEffect() { - // Samplers owned by SamplerCache - don't release - if (noise_view_) - wgpuTextureViewRelease(noise_view_); - if (noise_texture_) - wgpuTextureRelease(noise_texture_); - if (bind_group_1_) - wgpuBindGroupRelease(bind_group_1_); - if (bind_group_0_) - wgpuBindGroupRelease(bind_group_0_); - if (pipeline_) - wgpuRenderPipelineRelease(pipeline_); -} - -void RotatingCubeEffect::init(MainSequence* demo) { - demo_ = demo; - - uniform_buffer_ = - gpu_create_buffer(ctx_.device, sizeof(Uniforms), - WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst); - object_buffer_ = - gpu_create_buffer(ctx_.device, sizeof(ObjectData), - WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst); - - TextureWithView noise = gpu_create_texture_2d( - ctx_.device, 1, 1, WGPUTextureFormat_RGBA8Unorm, - WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment, 1); - noise_texture_ = noise.texture; - noise_view_ = noise.view; - - noise_sampler_ = SamplerCache::Get().get_or_create(ctx_.device, SamplerCache::linear()); - mask_sampler_ = SamplerCache::Get().get_or_create(ctx_.device, SamplerCache::clamp()); - - size_t shader_size; - const char* shader_code = - (const char*)GetAsset(AssetId::ASSET_MASKED_CUBE_SHADER, &shader_size); - - ShaderComposer::CompositionMap composition_map; - composition_map["render/scene_query_mode"] = "render/scene_query_linear"; - composed_shader_ = ShaderComposer::Get().Compose( - {}, std::string(shader_code, shader_size), composition_map); - - WGPUShaderSourceWGSL wgsl_src = {}; - wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(composed_shader_.c_str()); - - WGPUShaderModuleDescriptor shader_desc = {}; - shader_desc.nextInChain = &wgsl_src.chain; - WGPUShaderModule shader_module = - wgpuDeviceCreateShaderModule(ctx_.device, &shader_desc); - - WGPUBindGroupLayout bgl_0 = - BindGroupLayoutBuilder() - .uniform(0, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment, - sizeof(Uniforms)) - .storage(1, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment, - sizeof(ObjectData)) - .texture(3, WGPUShaderStage_Fragment) - .sampler(4, WGPUShaderStage_Fragment) - .build(ctx_.device); - - WGPUBindGroupLayout bgl_1 = BindGroupLayoutBuilder() - .texture(0, WGPUShaderStage_Fragment) - .sampler(1, WGPUShaderStage_Fragment) - .build(ctx_.device); - - const WGPUBindGroupLayout bgls[] = {bgl_0, bgl_1}; - const WGPUPipelineLayoutDescriptor pl_desc = { - .bindGroupLayoutCount = 2, - .bindGroupLayouts = bgls, - }; - WGPUPipelineLayout pipeline_layout = - wgpuDeviceCreatePipelineLayout(ctx_.device, &pl_desc); - - const WGPUColorTargetState color_target = { - .format = ctx_.format, - .writeMask = WGPUColorWriteMask_All, - }; - - const WGPUDepthStencilState depth_stencil = { - .format = WGPUTextureFormat_Depth24Plus, - .depthWriteEnabled = WGPUOptionalBool_True, - .depthCompare = WGPUCompareFunction_Less, - }; - - WGPUFragmentState fragment = {}; - fragment.module = shader_module; - fragment.entryPoint = str_view("fs_main"); - fragment.targetCount = 1; - fragment.targets = &color_target; - - WGPURenderPipelineDescriptor pipeline_desc = {}; - pipeline_desc.layout = pipeline_layout; - pipeline_desc.vertex.module = shader_module; - pipeline_desc.vertex.entryPoint = str_view("vs_main"); - pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; - pipeline_desc.primitive.cullMode = WGPUCullMode_None; - pipeline_desc.depthStencil = &depth_stencil; - pipeline_desc.multisample.count = 1; - pipeline_desc.multisample.mask = 0xFFFFFFFF; - pipeline_desc.fragment = &fragment; - - pipeline_ = wgpuDeviceCreateRenderPipeline(ctx_.device, &pipeline_desc); - wgpuShaderModuleRelease(shader_module); - wgpuPipelineLayoutRelease(pipeline_layout); - - const WGPUBindGroupEntry entries_0[] = { - {.binding = 0, - .buffer = uniform_buffer_.buffer, - .size = sizeof(Uniforms)}, - {.binding = 1, - .buffer = object_buffer_.buffer, - .size = sizeof(ObjectData)}, - {.binding = 3, .textureView = noise_view_}, - {.binding = 4, .sampler = noise_sampler_}, - }; - - const WGPUBindGroupDescriptor bg_desc_0 = { - .layout = bgl_0, - .entryCount = 4, - .entries = entries_0, - }; - bind_group_0_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc_0); - wgpuBindGroupLayoutRelease(bgl_0); - - WGPUTextureView mask_view = demo_->get_auxiliary_view("circle_mask"); - const WGPUBindGroupEntry entries_1[] = { - {.binding = 0, .textureView = mask_view}, - {.binding = 1, .sampler = mask_sampler_}, - }; - - const WGPUBindGroupDescriptor bg_desc_1 = { - .layout = bgl_1, - .entryCount = 2, - .entries = entries_1, - }; - bind_group_1_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc_1); - wgpuBindGroupLayoutRelease(bgl_1); -} - -void RotatingCubeEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& u) { - rotation_ += 0.016f * 1.5f; - - const vec3 camera_pos = vec3(0, 0, 5); - const vec3 target = vec3(0, 0, 0); - const vec3 up = vec3(0, 1, 0); - - const mat4 view = mat4::look_at(camera_pos, target, up); - const float fov = 60.0f * 3.14159f / 180.0f; - const mat4 proj = mat4::perspective(fov, u.aspect_ratio, 0.1f, 100.0f); - const mat4 view_proj = proj * view; - - const quat rot = quat::from_axis(vec3(0.3f, 1.0f, 0.2f), rotation_); - const mat4 T = mat4::translate(vec3(0, 0, 0)); - const mat4 R = rot.to_mat(); - const mat4 S = mat4::scale(vec3(1.5f, 1.5f, 1.5f)); - const mat4 model = T * R * S; - const mat4 inv_model = model.inverse(); - - const Uniforms uniforms = { - .view_proj = view_proj, - .inv_view_proj = view_proj.inverse(), - .camera_pos_time = vec4(camera_pos.x, camera_pos.y, camera_pos.z, u.time), - .params = vec4(1.0f, 0.0f, 0.0f, 0.0f), - .resolution = u.resolution, - }; - - const ObjectData obj_data = { - .model = model, - .inv_model = inv_model, - .color = vec4(0.8f, 0.4f, 0.2f, 1.0f), - .params = vec4(1.0f, 0.0f, 0.0f, 0.0f), - }; - - wgpuQueueWriteBuffer(ctx_.queue, uniform_buffer_.buffer, 0, &uniforms, - sizeof(Uniforms)); - wgpuQueueWriteBuffer(ctx_.queue, object_buffer_.buffer, 0, &obj_data, - sizeof(ObjectData)); - - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_0_, 0, nullptr); - wgpuRenderPassEncoderSetBindGroup(pass, 1, bind_group_1_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 36, 1, 0, 0); -} diff --git a/src/gpu/effects/rotating_cube_effect.h b/src/gpu/effects/rotating_cube_effect.h deleted file mode 100644 index fdf67ab..0000000 --- a/src/gpu/effects/rotating_cube_effect.h +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the 64k demo project. -// It defines RotatingCubeEffect for rendering a bump-mapped rotating cube. -// Uses auxiliary texture masking to render only inside a circular region. - -#ifndef ROTATING_CUBE_EFFECT_H_ -#define ROTATING_CUBE_EFFECT_H_ - -#include "gpu/effect.h" -#include "gpu/gpu.h" -#include "util/mini_math.h" -#include - -class RotatingCubeEffect : public Effect { - public: - RotatingCubeEffect(const GpuContext& ctx); - ~RotatingCubeEffect() override; - - void init(MainSequence* demo) override; - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - - private: - struct Uniforms { - mat4 view_proj; - mat4 inv_view_proj; - vec4 camera_pos_time; - vec4 params; - vec2 resolution; - vec2 padding; - }; - - struct ObjectData { - mat4 model; - mat4 inv_model; - vec4 color; - vec4 params; - }; - - MainSequence* demo_ = nullptr; - WGPURenderPipeline pipeline_ = nullptr; - WGPUBindGroup bind_group_0_ = nullptr; - WGPUBindGroup bind_group_1_ = nullptr; - GpuBuffer uniform_buffer_; - GpuBuffer object_buffer_; - WGPUTexture noise_texture_ = nullptr; - WGPUTextureView noise_view_ = nullptr; - WGPUSampler noise_sampler_ = nullptr; - WGPUSampler mask_sampler_ = nullptr; - float rotation_ = 0.0f; - - // Store composed shader to keep it alive for WebGPU - std::string composed_shader_; -}; - -#endif /* ROTATING_CUBE_EFFECT_H_ */ diff --git a/src/gpu/effects/scene1_effect.cc b/src/gpu/effects/scene1_effect.cc deleted file mode 100644 index c75e511..0000000 --- a/src/gpu/effects/scene1_effect.cc +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of the 64k demo project. -// Scene1 effect - ShaderToy conversion (raymarching scene) - -#include "gpu/demo_effects.h" -#include "gpu/gpu.h" - -Scene1Effect::Scene1Effect(const GpuContext& ctx) : Effect(ctx) { - ResourceBinding bindings[] = {{uniforms_.get(), WGPUBufferBindingType_Uniform}}; - pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, scene1_shader_wgsl, - bindings, 1); - pass_.vertex_count = 3; -} - -void Scene1Effect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline); - wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, pass_.vertex_count, 1, 0, 0); -} diff --git a/src/gpu/effects/scene1_effect.h b/src/gpu/effects/scene1_effect.h deleted file mode 100644 index 190ffa9..0000000 --- a/src/gpu/effects/scene1_effect.h +++ /dev/null @@ -1,19 +0,0 @@ -// This file is part of the 64k demo project. -// Scene1 effect - ShaderToy conversion (raymarching scene) - -#ifndef SCENE1_EFFECT_H_ -#define SCENE1_EFFECT_H_ - -#include "gpu/effect.h" - -class Scene1Effect : public Effect { - public: - Scene1Effect(const GpuContext& ctx); - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - - private: - RenderPass pass_; -}; - -#endif /* SCENE1_EFFECT_H_ */ diff --git a/src/gpu/effects/shader_composer.cc b/src/gpu/effects/shader_composer.cc deleted file mode 100644 index 9234b7a..0000000 --- a/src/gpu/effects/shader_composer.cc +++ /dev/null @@ -1,120 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the ShaderComposer class. - -#include "gpu/effects/shader_composer.h" -#include -#include - -ShaderComposer& ShaderComposer::Get() { - static ShaderComposer instance; - return instance; -} - -void ShaderComposer::RegisterSnippet(const std::string& name, - const std::string& code) { - snippets_[name] = code; -} - -void ShaderComposer::ResolveRecursive(const std::string& source, - std::stringstream& ss, - std::set& included, - const CompositionMap& substitutions) { - std::istringstream stream(source); - std::string line; - while (std::getline(stream, line)) { - // Check for #include "snippet_name" - if (line.compare(0, 9, "#include ") == 0) { - size_t start = line.find('"'); - size_t end = line.find('"', start + 1); - if (start != std::string::npos && end != std::string::npos) { - std::string name = line.substr(start + 1, end - start - 1); - - // Apply substitution if available - auto sub_it = substitutions.find(name); - if (sub_it != substitutions.end()) { - name = sub_it->second; - } - - if (included.find(name) == included.end()) { - included.insert(name); - auto it = snippets_.find(name); - if (it != snippets_.end()) { - ss << "// --- Included: " << name << " ---\n"; - ResolveRecursive(it->second, ss, included, substitutions); - ss << "// --- End Include: " << name << " ---\n"; - } else { - ss << "// ERROR: Snippet not found: " << name << "\n"; - } - } - } - } else { - ss << line << "\n"; - } - } -} - -std::string -ShaderComposer::Compose(const std::vector& dependencies, - const std::string& main_code, - const CompositionMap& substitutions) { - std::stringstream ss; - ss << "// Generated by ShaderComposer\n\n"; - - std::set included; - - // Process explicit dependencies first - for (const auto& dep : dependencies) { - std::string name = dep; - auto sub_it = substitutions.find(name); - if (sub_it != substitutions.end()) { - name = sub_it->second; - } - - if (included.find(name) == included.end()) { - included.insert(name); - auto it = snippets_.find(name); - if (it != snippets_.end()) { - ss << "// --- Dependency: " << name << " ---\n"; - ResolveRecursive(it->second, ss, included, substitutions); - ss << "\n"; - } - } - } - - ss << "// --- Main Code ---\n"; - ResolveRecursive(main_code, ss, included, substitutions); - - return ss.str(); -} - -void ShaderComposer::VerifyIncludes() const { -#if !defined(STRIP_ALL) - // Known placeholders that get substituted at composition time - std::set known_placeholders = {"render/scene_query_mode"}; - - std::set missing; - for (const auto& [name, code] : snippets_) { - std::istringstream stream(code); - std::string line; - while (std::getline(stream, line)) { - if (line.compare(0, 9, "#include ") == 0) { - size_t start = line.find('"'); - size_t end = line.find('"', start + 1); - if (start != std::string::npos && end != std::string::npos) { - std::string included = line.substr(start + 1, end - start - 1); - if (snippets_.find(included) == snippets_.end() && - known_placeholders.find(included) == known_placeholders.end()) { - missing.insert(included); - } - } - } - } - } - if (!missing.empty()) { - fprintf(stderr, "WARNING: Unregistered shader snippets:\n"); - for (const auto& name : missing) { - fprintf(stderr, " - %s\n", name.c_str()); - } - } -#endif -} diff --git a/src/gpu/effects/shader_composer.h b/src/gpu/effects/shader_composer.h deleted file mode 100644 index d0972f2..0000000 --- a/src/gpu/effects/shader_composer.h +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the 64k demo project. -// It defines the ShaderComposer class for managing WGSL snippets. - -#pragma once - -#include -#include -#include -#include - -class ShaderComposer { - public: - static ShaderComposer& Get(); - - // Register a snippet (e.g. "common_math", "sdf_primitives") - void RegisterSnippet(const std::string& name, const std::string& code); - - using CompositionMap = std::map; - - // Assemble a final shader string by prepending required snippets - // and recursively resolving #include "snippet_name" directives. - // Optional substitutions: map "placeholder_name" -> "actual_snippet_name" - std::string Compose(const std::vector& dependencies, - const std::string& main_code, - const CompositionMap& substitutions = {}); - - // Verify all #include directives reference registered snippets - void VerifyIncludes() const; - - private: - ShaderComposer() = default; - - void ResolveRecursive(const std::string& source, std::stringstream& ss, - std::set& included, - const CompositionMap& substitutions); - - std::map snippets_; -}; diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc deleted file mode 100644 index d79f3d3..0000000 --- a/src/gpu/effects/shaders.cc +++ /dev/null @@ -1,146 +0,0 @@ -// This file is part of the 64k demo project. -// It defines WGSL shader code for various effects. - -#include "../demo_effects.h" - -#if defined(USE_TEST_ASSETS) - -#include "test_assets.h" - -#else - -#include "generated/assets.h" - -#endif - -#include "gpu/effects/shader_composer.h" - -#include "util/asset_manager.h" - -void InitShaderComposer() { - auto& sc = ShaderComposer::Get(); - - auto register_if_exists = [&](const char* name, AssetId id) { - size_t size; - - const char* data = (const char*)GetAsset(id, &size); - - if (data) { - sc.RegisterSnippet(name, std::string(data, size)); - } - }; - - register_if_exists("common_uniforms", AssetId::ASSET_SHADER_COMMON_UNIFORMS); - register_if_exists("math/sdf_shapes", AssetId::ASSET_SHADER_MATH_SDF_SHAPES); - register_if_exists("math/sdf_utils", AssetId::ASSET_SHADER_MATH_SDF_UTILS); - register_if_exists("math/common_utils", - AssetId::ASSET_SHADER_MATH_COMMON_UTILS); - register_if_exists("math/noise", AssetId::ASSET_SHADER_MATH_NOISE); - register_if_exists("render/shadows", AssetId::ASSET_SHADER_RENDER_SHADOWS); - register_if_exists("render/scene_query_bvh", - AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_BVH); - register_if_exists("render/scene_query_linear", - AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_LINEAR); - register_if_exists("render/lighting_utils", - AssetId::ASSET_SHADER_RENDER_LIGHTING_UTILS); - register_if_exists("render/mesh", AssetId::ASSET_SHADER_MESH); - - register_if_exists("sdf_primitives", AssetId::ASSET_SHADER_SDF_PRIMITIVES); - - register_if_exists("lighting", AssetId::ASSET_SHADER_LIGHTING); - - register_if_exists("ray_box", AssetId::ASSET_SHADER_RAY_BOX); - register_if_exists("ray_triangle", AssetId::ASSET_SHADER_RAY_TRIANGLE); - - register_if_exists("cnn_activation", AssetId::ASSET_SHADER_CNN_ACTIVATION); - register_if_exists("cnn_conv1x1", AssetId::ASSET_SHADER_CNN_CONV1X1); - register_if_exists("cnn_conv3x3", AssetId::ASSET_SHADER_CNN_CONV3X3); - register_if_exists("cnn_conv5x5", AssetId::ASSET_SHADER_CNN_CONV5X5); - register_if_exists("cnn_conv7x7", AssetId::ASSET_SHADER_CNN_CONV7X7); - register_if_exists("cnn_weights_generated", - AssetId::ASSET_SHADER_CNN_WEIGHTS); - -#if !defined(STRIP_ALL) - sc.VerifyIncludes(); -#endif -} - -// Helper to get asset string or empty string - -static const char* SafeGetAsset(AssetId id) { - const uint8_t* data = GetAsset(id); - - return data ? (const char*)data : ""; -} - -const char* main_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_MAIN); - -const char* particle_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_COMPUTE); - -const char* particle_render_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_RENDER); - -const char* passthrough_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_PASSTHROUGH); - -const char* ellipse_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_ELLIPSE); - -const char* particle_spray_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_SPRAY_COMPUTE); - -const char* gaussian_blur_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_GAUSSIAN_BLUR); - -const char* solarize_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_SOLARIZE); - -const char* scene1_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_SCENE1); - -const char* distort_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_DISTORT); - -const char* chroma_aberration_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_CHROMA_ABERRATION); - -const char* cnn_layer_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_CNN_LAYER); - -const char* gen_noise_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_NOISE); - -const char* gen_perlin_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_PERLIN); - -const char* gen_grid_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_GRID); - -#if !defined(STRIP_GPU_COMPOSITE) -const char* gen_blend_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_BLEND); - -const char* gen_mask_compute_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_MASK); -#endif - -const char* vignette_shader_wgsl = - - SafeGetAsset(AssetId::ASSET_SHADER_VIGNETTE); diff --git a/src/gpu/effects/shaders.h b/src/gpu/effects/shaders.h deleted file mode 100644 index 03fa48c..0000000 --- a/src/gpu/effects/shaders.h +++ /dev/null @@ -1,29 +0,0 @@ -// This file is part of the 64k demo project. -// It declares the WGSL shader strings and initialization function. - -#pragma once - -// Initializes the ShaderComposer with snippet assets. -void InitShaderComposer(); - -// Shader declarations (defined in shaders.cc) -extern const char* main_shader_wgsl; -extern const char* particle_compute_wgsl; -extern const char* particle_render_wgsl; -extern const char* passthrough_shader_wgsl; -extern const char* ellipse_shader_wgsl; -extern const char* particle_spray_compute_wgsl; -extern const char* gaussian_blur_shader_wgsl; -extern const char* solarize_shader_wgsl; -extern const char* scene1_shader_wgsl; -extern const char* distort_shader_wgsl; -extern const char* chroma_aberration_shader_wgsl; -extern const char* vignette_shader_wgsl; -extern const char* cnn_layer_shader_wgsl; -extern const char* gen_noise_compute_wgsl; -extern const char* gen_perlin_compute_wgsl; -extern const char* gen_grid_compute_wgsl; -#if !defined(STRIP_GPU_COMPOSITE) -extern const char* gen_blend_compute_wgsl; -extern const char* gen_mask_compute_wgsl; -#endif diff --git a/src/gpu/effects/solarize_effect.cc b/src/gpu/effects/solarize_effect.cc deleted file mode 100644 index cdb9354..0000000 --- a/src/gpu/effects/solarize_effect.cc +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the SolarizeEffect. - -#include "gpu/demo_effects.h" -#include "gpu/gpu.h" - -// --- SolarizeEffect --- -SolarizeEffect::SolarizeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) { - pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, - solarize_shader_wgsl); -} -void SolarizeEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - PostProcessEffect::render(pass, uniforms); -} -void SolarizeEffect::update_bind_group(WGPUTextureView v) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, uniforms_.get(), - {}); -} diff --git a/src/gpu/effects/theme_modulation_effect.cc b/src/gpu/effects/theme_modulation_effect.cc deleted file mode 100644 index aff6bce..0000000 --- a/src/gpu/effects/theme_modulation_effect.cc +++ /dev/null @@ -1,105 +0,0 @@ -// This file is part of the 64k demo project. -// It implements theme modulation (bright/dark alternation). - -#include "gpu/effects/theme_modulation_effect.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/effects/shaders.h" -#include - -struct ThemeModulationParams { - float theme_brightness; - float _pad[3]; -}; -static_assert(sizeof(ThemeModulationParams) == 16, "ThemeModulationParams must be 16 bytes for WGSL alignment"); - -ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx) - : PostProcessEffect(ctx) { - const char* shader_code = R"( - struct VertexOutput { - @builtin(position) position: vec4, - @location(0) uv: vec2, - }; - - struct CommonUniforms { - resolution: vec2, - _pad0: f32, - _pad1: f32, - aspect_ratio: f32, - time: f32, - beat: f32, - audio_intensity: f32, - }; - - struct ThemeModulationParams { - theme_brightness: f32, - _pad0: f32, - _pad1: f32, - _pad2: f32, - }; - - @group(0) @binding(0) var inputSampler: sampler; - @group(0) @binding(1) var inputTexture: texture_2d; - @group(0) @binding(2) var uniforms: CommonUniforms; - @group(0) @binding(3) var params: ThemeModulationParams; - - @vertex - fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { - var output: VertexOutput; - // Large triangle trick for fullscreen coverage - var pos = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - output.position = vec4(pos[vertexIndex], 0.0, 1.0); - output.uv = pos[vertexIndex] * 0.5 + 0.5; - return output; - } - - @fragment - fn fs_main(input: VertexOutput) -> @location(0) vec4 { - let color = textureSample(inputTexture, inputSampler, input.uv); - // Apply theme brightness modulation - return vec4(color.rgb * params.theme_brightness, color.a); - } - )"; - - pipeline_ = - create_post_process_pipeline(ctx_.device, ctx_.format, shader_code); - - params_buffer_ = gpu_create_buffer( - ctx_.device, 16, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst); -} - -void ThemeModulationEffect::update_bind_group(WGPUTextureView input_view) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - uniforms_.get(), params_buffer_); -} - -void ThemeModulationEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - - // Alternate between bright and dark every 4 seconds (2 pattern changes) - // Music patterns change every 2 seconds at 120 BPM - float cycle_time = fmodf(uniforms.time, 8.0f); // 8 second cycle (4 patterns) - bool is_dark_section = (cycle_time >= 4.0f); // Dark for second half - - // Smooth transition between themes using a sine wave - float transition = - (std::sin(uniforms.time * 3.14159f / 4.0f) + 1.0f) * 0.5f; // 0.0 to 1.0 - float bright_value = 1.0f; - float dark_value = 0.35f; - float theme_brightness = - bright_value + (dark_value - bright_value) * transition; - - // Update params buffer - ThemeModulationParams params = {theme_brightness, {0.0f, 0.0f, 0.0f}}; - wgpuQueueWriteBuffer(ctx_.queue, params_buffer_.buffer, 0, ¶ms, - sizeof(params)); - - // Render - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); -} diff --git a/src/gpu/effects/theme_modulation_effect.h b/src/gpu/effects/theme_modulation_effect.h deleted file mode 100644 index 31e96e6..0000000 --- a/src/gpu/effects/theme_modulation_effect.h +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of the 64k demo project. -// It implements a theme modulation effect that alternates between bright and -// dark. Pattern changes every 2 seconds, so we alternate every 4 seconds (2 -// patterns). - -#pragma once -#include "gpu/effect.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/uniform_helper.h" - -class ThemeModulationEffect : public PostProcessEffect { - public: - ThemeModulationEffect(const GpuContext& ctx); - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - void update_bind_group(WGPUTextureView input_view) override; - - private: - GpuBuffer params_buffer_; -}; diff --git a/src/gpu/effects/vignette_effect.cc b/src/gpu/effects/vignette_effect.cc deleted file mode 100644 index 4b0ffe2..0000000 --- a/src/gpu/effects/vignette_effect.cc +++ /dev/null @@ -1,30 +0,0 @@ -// This file is part of the 64k demo project. -// It implements the VignetteEffect. - -#include "gpu/demo_effects.h" -#include "gpu/effects/post_process_helper.h" -#include "gpu/gpu.h" - -VignetteEffect::VignetteEffect(const GpuContext& ctx) - : VignetteEffect(ctx, VignetteParams()) { -} - -VignetteEffect::VignetteEffect(const GpuContext& ctx, - const VignetteParams& params) - : PostProcessEffect(ctx), params_(params) { - params_buffer_.init(ctx_.device); - pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, - vignette_shader_wgsl); -} - -void VignetteEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); - params_buffer_.update(ctx_.queue, params_); - PostProcessEffect::render(pass, uniforms); -} - -void VignetteEffect::update_bind_group(WGPUTextureView v) { - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, uniforms_.get(), - params_buffer_.get()); -} -- cgit v1.2.3