From e4851ae9f310b44dab25eb979733281002c8953d Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 7 Mar 2026 19:02:07 +0100 Subject: refactor(effects): introduce WgslEffect for shader-only post-process effects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace boilerplate .h/.cc pairs for simple single-pass effects with a generic WgslEffect base class that takes a shader string + optional WgslEffectParams (binding 3). Port Flash, Passthrough, Heptagon, Scratch, and GaussianBlur to thin header-only wrappers — no .cc files, no CMake entries needed. Removes 5 .cc files (-243 lines). Update EFFECT_WORKFLOW.md, CONTRIBUTING.md, and AI_RULES.md to document the WgslEffect (Path A) vs full class (Path B) workflow. Doc cleanup: fix stale GaussianBlurParams/PostProcessEffect references and test counts. handoff(Claude): WgslEffect landed; 5 effects ported; docs updated. Co-Authored-By: Claude Sonnet 4.6 --- src/effects/flash_effect.cc | 47 ------------------------ src/effects/flash_effect.h | 22 ++++-------- src/effects/gaussian_blur.wgsl | 17 ++++----- src/effects/gaussian_blur_effect.cc | 71 ------------------------------------- src/effects/gaussian_blur_effect.h | 32 +++++------------ src/effects/heptagon_effect.cc | 49 ------------------------- src/effects/heptagon_effect.h | 20 ++++------- src/effects/passthrough_effect.cc | 60 ------------------------------- src/effects/passthrough_effect.h | 20 ++++------- src/effects/scratch_effect.cc | 44 ----------------------- src/effects/scratch_effect.h | 19 ++++------ src/gpu/demo_effects.h | 1 + src/gpu/wgsl_effect.cc | 54 ++++++++++++++++++++++++++++ src/gpu/wgsl_effect.h | 38 ++++++++++++++++++++ 14 files changed, 132 insertions(+), 362 deletions(-) delete mode 100644 src/effects/flash_effect.cc delete mode 100644 src/effects/gaussian_blur_effect.cc delete mode 100644 src/effects/heptagon_effect.cc delete mode 100644 src/effects/passthrough_effect.cc delete mode 100644 src/effects/scratch_effect.cc create mode 100644 src/gpu/wgsl_effect.cc create mode 100644 src/gpu/wgsl_effect.h (limited to 'src') diff --git a/src/effects/flash_effect.cc b/src/effects/flash_effect.cc deleted file mode 100644 index 7064e9c..0000000 --- a/src/effects/flash_effect.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Flash effect for visual sync testing -// Pulses white based on beat timing - -#include "effects/flash_effect.h" -#include "effects/shaders.h" -#include "gpu/post_process_helper.h" -#include "util/fatal_error.h" - -Flash::Flash(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs, float start_time, - float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time) { - HEADLESS_RETURN_IF_NULL(ctx_.device); - - create_nearest_sampler(); - create_dummy_scene_texture(); - - pipeline_.set(create_post_process_pipeline( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, flash_shader_wgsl)); -} - -void Flash::render(WGPUCommandEncoder encoder, - const UniformsSequenceParams& params, NodeRegistry& nodes) { - // Get output view (scene effects typically write to output, ignore input) - WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - - // Update bind group (use dummy texture for scene effect) - pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), - dummy_texture_view_.get(), uniforms_buffer_.get(), - {nullptr, 0}); - - // Render pass - WGPURenderPassColorAttachment color_attachment = {}; - gpu_init_color_attachment(color_attachment, output_view); - - WGPURenderPassDescriptor pass_desc = {}; - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_attachment; - - WGPURenderPassEncoder pass = - wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); - wgpuRenderPassEncoderEnd(pass); - wgpuRenderPassEncoderRelease(pass); -} diff --git a/src/effects/flash_effect.h b/src/effects/flash_effect.h index 052957d..1cfe1ee 100644 --- a/src/effects/flash_effect.h +++ b/src/effects/flash_effect.h @@ -1,21 +1,13 @@ -// Flash effect for visual sync testing -// Pulses white based on beat timing +// Flash effect - beat-synchronized white flash #pragma once -#include "gpu/effect.h" -#include "gpu/uniform_helper.h" -#include "gpu/wgpu_resource.h" +#include "effects/shaders.h" +#include "gpu/wgsl_effect.h" -class Flash : public Effect { - public: +struct Flash : public WgslEffect { Flash(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, float start_time, - float end_time); - - void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, - NodeRegistry& nodes) override; - - private: - RenderPipeline pipeline_; - BindGroup bind_group_; + float end_time) + : WgslEffect(ctx, inputs, outputs, start_time, end_time, + flash_shader_wgsl) {} }; diff --git a/src/effects/gaussian_blur.wgsl b/src/effects/gaussian_blur.wgsl index 68e0720..de0f2cf 100644 --- a/src/effects/gaussian_blur.wgsl +++ b/src/effects/gaussian_blur.wgsl @@ -6,21 +6,16 @@ #include "common_uniforms" #include "render/fullscreen_vs" -struct GaussianBlurParams { - strength: f32, - strength_audio: f32, - stretch: f32, // dir_y / dir_x - _pad: f32, -}; +// effect_params.p: x=strength, y=strength_audio, z=stretch +struct WgslEffectParams { p: vec4f, c: vec4f } @group(0) @binding(2) var uniforms: CommonUniforms; -@group(0) @binding(3) var params: GaussianBlurParams; +@group(0) @binding(3) var effect_params: WgslEffectParams; @fragment fn fs_main(@builtin(position) p: vec4f) -> @location(0) vec4f { - // Parameterized strength + dramatic beat pulsation - let pulse = 1.0 + uniforms.audio_intensity * params.strength_audio; // Pulsate beat - let size = params.strength * pulse; - let dir = vec2f(1., params.stretch) * size / uniforms.resolution.x; + let pulse = 1.0 + uniforms.audio_intensity * effect_params.p.y; + let size = effect_params.p.x * pulse; + let dir = vec2f(1., effect_params.p.z) * size / uniforms.resolution.x; let uv = p.xy / uniforms.resolution; var res = vec4f(0.0); diff --git a/src/effects/gaussian_blur_effect.cc b/src/effects/gaussian_blur_effect.cc deleted file mode 100644 index 15d4d0a..0000000 --- a/src/effects/gaussian_blur_effect.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Gaussian blur effect implementation - -#include "effects/gaussian_blur_effect.h" -#include "effects/shaders.h" -#include "gpu/post_process_helper.h" -#include "util/fatal_error.h" - -GaussianBlur::GaussianBlur(const GpuContext& ctx, - const std::vector& inputs, - const std::vector& outputs, - float start_time, float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), - bind_group_(nullptr) { - HEADLESS_RETURN_IF_NULL(ctx_.device); - - create_linear_sampler(); - params_buffer_.init(ctx_.device); - - pipeline_ = create_post_process_pipeline( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, gaussian_blur_shader_wgsl); -} - -void GaussianBlur::render(WGPUCommandEncoder encoder, - const UniformsSequenceParams& params, - NodeRegistry& nodes) { - // Get input/output views - WGPUTextureView input_view = nodes.get_view(input_nodes_[0]); - WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - - // Update effect-specific params - params_buffer_.update(ctx_.queue, blur_params_); - - // Update bind group - WGPUBindGroupEntry entries[4] = {}; - entries[0].binding = PP_BINDING_SAMPLER; - entries[0].sampler = sampler_.get(); - entries[1].binding = PP_BINDING_TEXTURE; - entries[1].textureView = input_view; - entries[2].binding = PP_BINDING_UNIFORMS; - entries[2].buffer = uniforms_buffer_.get().buffer; - entries[2].size = sizeof(UniformsSequenceParams); - entries[3].binding = PP_BINDING_EFFECT_PARAMS; - entries[3].buffer = params_buffer_.get().buffer; - entries[3].size = sizeof(GaussianBlurParams); - - WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); - bg_desc.entryCount = 4; - bg_desc.entries = entries; - - if (bind_group_) { - wgpuBindGroupRelease(bind_group_); - } - bind_group_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc); - - // Render pass - WGPURenderPassColorAttachment color_attachment = {}; - gpu_init_color_attachment(color_attachment, output_view); - - WGPURenderPassDescriptor pass_desc = {}; - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_attachment; - - WGPURenderPassEncoder pass = - wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); - wgpuRenderPassEncoderEnd(pass); - wgpuRenderPassEncoderRelease(pass); -} diff --git a/src/effects/gaussian_blur_effect.h b/src/effects/gaussian_blur_effect.h index f4b8fcf..0c528c5 100644 --- a/src/effects/gaussian_blur_effect.h +++ b/src/effects/gaussian_blur_effect.h @@ -1,31 +1,15 @@ // Gaussian blur effect v2 - single-pass blur +// effect_params.p: x=strength(8.0), y=strength_audio(0.5), z=stretch(1.0) #pragma once +#include "effects/shaders.h" +#include "gpu/wgsl_effect.h" -#include "gpu/effect.h" -#include "gpu/uniform_helper.h" - -struct GaussianBlurParams { - float strength = 8.0f; - float strength_audio = 0.5f; - float stretch = 1.0f; - float _pad = 0.0f; -}; -static_assert(sizeof(GaussianBlurParams) == 16, - "GaussianBlurParams must be 16 bytes"); - -class GaussianBlur : public Effect { - public: +struct GaussianBlur : public WgslEffect { GaussianBlur(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, float start_time, - float end_time); - - void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, - NodeRegistry& nodes) override; - - private: - WGPURenderPipeline pipeline_; - WGPUBindGroup bind_group_; - GaussianBlurParams blur_params_; - UniformBuffer params_buffer_; + float end_time) + : WgslEffect(ctx, inputs, outputs, start_time, end_time, + gaussian_blur_shader_wgsl, WGPULoadOp_Clear, + WgslEffectParams{{8.0f, 0.5f, 1.0f, 0.0f}, {}}) {} }; diff --git a/src/effects/heptagon_effect.cc b/src/effects/heptagon_effect.cc deleted file mode 100644 index ac688df..0000000 --- a/src/effects/heptagon_effect.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Heptagon effect implementation - -#include "effects/heptagon_effect.h" -#include "effects/shaders.h" -#include "gpu/gpu.h" -#include "gpu/post_process_helper.h" -#include "util/fatal_error.h" - -Heptagon::Heptagon(const GpuContext& ctx, - const std::vector& inputs, - const std::vector& outputs, float start_time, - float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time) { - HEADLESS_RETURN_IF_NULL(ctx_.device); - - create_nearest_sampler(); - create_dummy_scene_texture(); - - pipeline_.set(create_post_process_pipeline( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, heptagon_shader_wgsl)); -} - -void Heptagon::render(WGPUCommandEncoder encoder, - const UniformsSequenceParams& params, - NodeRegistry& nodes) { - // Get output view (scene effects typically write to output, ignore input) - WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - - // Create bind group (use dummy texture for scene effect) - pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), - dummy_texture_view_.get(), uniforms_buffer_.get(), - {nullptr, 0}); - - // Render pass - WGPURenderPassColorAttachment color_attachment = {}; - gpu_init_color_attachment(color_attachment, output_view); - - WGPURenderPassDescriptor pass_desc = {}; - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_attachment; - - WGPURenderPassEncoder pass = - wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); // Fullscreen triangle - wgpuRenderPassEncoderEnd(pass); - wgpuRenderPassEncoderRelease(pass); -} diff --git a/src/effects/heptagon_effect.h b/src/effects/heptagon_effect.h index ab7db8d..3762202 100644 --- a/src/effects/heptagon_effect.h +++ b/src/effects/heptagon_effect.h @@ -1,21 +1,13 @@ // Heptagon effect v2 - scene rendering effect #pragma once +#include "effects/shaders.h" +#include "gpu/wgsl_effect.h" -#include "gpu/effect.h" -#include "gpu/uniform_helper.h" -#include "gpu/wgpu_resource.h" - -class Heptagon : public Effect { - public: +struct Heptagon : public WgslEffect { Heptagon(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, float start_time, - float end_time); - - void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, - NodeRegistry& nodes) override; - - private: - RenderPipeline pipeline_; - BindGroup bind_group_; + float end_time) + : WgslEffect(ctx, inputs, outputs, start_time, end_time, + heptagon_shader_wgsl) {} }; diff --git a/src/effects/passthrough_effect.cc b/src/effects/passthrough_effect.cc deleted file mode 100644 index 217b5d2..0000000 --- a/src/effects/passthrough_effect.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Passthrough effect implementation - -#include "effects/passthrough_effect.h" -#include "effects/shaders.h" -#include "gpu/post_process_helper.h" -#include "util/fatal_error.h" - -Passthrough::Passthrough(const GpuContext& ctx, - const std::vector& inputs, - const std::vector& outputs, - float start_time, float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time) { - HEADLESS_RETURN_IF_NULL(ctx_.device); - - create_linear_sampler(); - - pipeline_.set(create_post_process_pipeline_simple( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, passthrough_shader_wgsl)); -} - -void Passthrough::render(WGPUCommandEncoder encoder, - const UniformsSequenceParams& params, - NodeRegistry& nodes) { - // Get input/output views - WGPUTextureView input_view = nodes.get_view(input_nodes_[0]); - WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - - // Manually create bind group with only 3 entries (no effect params needed) - WGPUBindGroupEntry entries[3] = {}; - entries[0].binding = PP_BINDING_SAMPLER; - entries[0].sampler = sampler_.get(); - entries[1].binding = PP_BINDING_TEXTURE; - entries[1].textureView = input_view; - entries[2].binding = PP_BINDING_UNIFORMS; - entries[2].buffer = uniforms_buffer_.get().buffer; - entries[2].size = sizeof(UniformsSequenceParams); - - WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_.get(), 0); - bg_desc.entryCount = 3; - bg_desc.entries = entries; - - bind_group_.replace(wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc)); - - // Render pass - WGPURenderPassColorAttachment color_attachment = {}; - gpu_init_color_attachment(color_attachment, output_view); - - WGPURenderPassDescriptor pass_desc = {}; - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_attachment; - - WGPURenderPassEncoder pass = - wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); // Fullscreen triangle - wgpuRenderPassEncoderEnd(pass); - wgpuRenderPassEncoderRelease(pass); -} diff --git a/src/effects/passthrough_effect.h b/src/effects/passthrough_effect.h index 1c60e02..285663f 100644 --- a/src/effects/passthrough_effect.h +++ b/src/effects/passthrough_effect.h @@ -1,21 +1,13 @@ // Passthrough effect v2 - simple copy input to output #pragma once +#include "effects/shaders.h" +#include "gpu/wgsl_effect.h" -#include "gpu/effect.h" -#include "gpu/uniform_helper.h" -#include "gpu/wgpu_resource.h" - -class Passthrough : public Effect { - public: +struct Passthrough : public WgslEffect { Passthrough(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, float start_time, - float end_time); - - void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, - NodeRegistry& nodes) override; - - private: - RenderPipeline pipeline_; - BindGroup bind_group_; + float end_time) + : WgslEffect(ctx, inputs, outputs, start_time, end_time, + passthrough_shader_wgsl) {} }; diff --git a/src/effects/scratch_effect.cc b/src/effects/scratch_effect.cc deleted file mode 100644 index f49e601..0000000 --- a/src/effects/scratch_effect.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Scratch effect - film scratch overlay post-process - -#include "effects/scratch_effect.h" -#include "effects/shaders.h" -#include "gpu/post_process_helper.h" -#include "util/fatal_error.h" - -Scratch::Scratch(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs, float start_time, - float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time) { - HEADLESS_RETURN_IF_NULL(ctx_.device); - - create_linear_sampler(); - - pipeline_.set(create_post_process_pipeline(ctx_.device, - WGPUTextureFormat_RGBA8Unorm, - scratch_shader_wgsl)); -} - -void Scratch::render(WGPUCommandEncoder encoder, - const UniformsSequenceParams& params, - NodeRegistry& nodes) { - WGPUTextureView input_view = nodes.get_view(input_nodes_[0]); - WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - - pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), - input_view, uniforms_buffer_.get(), {nullptr, 0}); - - WGPURenderPassColorAttachment color_attachment = {}; - gpu_init_color_attachment(color_attachment, output_view); - - WGPURenderPassDescriptor pass_desc = {}; - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_attachment; - - WGPURenderPassEncoder pass = - wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); - wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); - wgpuRenderPassEncoderEnd(pass); - wgpuRenderPassEncoderRelease(pass); -} diff --git a/src/effects/scratch_effect.h b/src/effects/scratch_effect.h index fb36a96..6c0e0cb 100644 --- a/src/effects/scratch_effect.h +++ b/src/effects/scratch_effect.h @@ -1,20 +1,13 @@ // Scratch effect - film scratch overlay post-process -// Reads input buffer, additively blends scratch_lines() on top. #pragma once -#include "gpu/effect.h" -#include "gpu/wgpu_resource.h" +#include "effects/shaders.h" +#include "gpu/wgsl_effect.h" -class Scratch : public Effect { - public: +struct Scratch : public WgslEffect { Scratch(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, float start_time, - float end_time); - - void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, - NodeRegistry& nodes) override; - - private: - RenderPipeline pipeline_; - BindGroup bind_group_; + float end_time) + : WgslEffect(ctx, inputs, outputs, start_time, end_time, + scratch_shader_wgsl) {} }; diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index ecc37df..a2500e7 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -15,6 +15,7 @@ #include "gpu/sequence.h" #include "gpu/texture_manager.h" #include "gpu/uniform_helper.h" +#include "gpu/wgsl_effect.h" // Individual Effect Headers #include "effects/flash_effect.h" diff --git a/src/gpu/wgsl_effect.cc b/src/gpu/wgsl_effect.cc new file mode 100644 index 0000000..0ce4730 --- /dev/null +++ b/src/gpu/wgsl_effect.cc @@ -0,0 +1,54 @@ +// WgslEffect: generic shader-only post-process effect. + +#include "gpu/wgsl_effect.h" +#include "gpu/gpu.h" +#include "gpu/post_process_helper.h" +#include "util/fatal_error.h" + +WgslEffect::WgslEffect(const GpuContext& ctx, + const std::vector& inputs, + const std::vector& outputs, + float start_time, float end_time, + const char* shader_code, WGPULoadOp load_op, + WgslEffectParams initial_params) + : Effect(ctx, inputs, outputs, start_time, end_time), + effect_params(initial_params), + load_op_(load_op) { + HEADLESS_RETURN_IF_NULL(ctx_.device); + + create_linear_sampler(); + params_buffer_.init(ctx_.device); + + pipeline_.set(create_post_process_pipeline(ctx_.device, + WGPUTextureFormat_RGBA8Unorm, + shader_code)); +} + +void WgslEffect::render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) { + WGPUTextureView input_view = nodes.get_view(input_nodes_[0]); + WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); + + params_buffer_.update(ctx_.queue, effect_params); + + pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), + input_view, uniforms_buffer_.get(), + params_buffer_.get()); + + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_view); + color_attachment.loadOp = load_op_; + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; + + WGPURenderPassEncoder pass = + wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(pass); + wgpuRenderPassEncoderRelease(pass); +} diff --git a/src/gpu/wgsl_effect.h b/src/gpu/wgsl_effect.h new file mode 100644 index 0000000..062f885 --- /dev/null +++ b/src/gpu/wgsl_effect.h @@ -0,0 +1,38 @@ +// WgslEffect: generic shader-only post-process effect. +// Replaces boilerplate .h/.cc pairs for simple single-pass effects. + +#pragma once +#include "gpu/effect.h" +#include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" + +// Generic per-effect uniform params (binding 3, 32 bytes). +// Matches WgslEffectParams in WGSL: struct { p: vec4f, c: vec4f } +// effect_params.p — 4 generic floats (strength, scale, etc.) +// effect_params.c — color or secondary vec4 +struct WgslEffectParams { + float p[4]; // vec4: generic float params + float c[4]; // vec4: color / secondary params +}; +static_assert(sizeof(WgslEffectParams) == 32, "WgslEffectParams must be 32 bytes"); + +class WgslEffect : public Effect { + public: + // Mutate per-frame for dynamic parameter modulation. + WgslEffectParams effect_params; + + WgslEffect(const GpuContext& ctx, const std::vector& inputs, + const std::vector& outputs, float start_time, + float end_time, const char* shader_code, + WGPULoadOp load_op = WGPULoadOp_Clear, + WgslEffectParams initial_params = {}); + + void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, + NodeRegistry& nodes) override; + + private: + RenderPipeline pipeline_; + BindGroup bind_group_; + UniformBuffer params_buffer_; + WGPULoadOp load_op_; +}; -- cgit v1.2.3