summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-03-07 19:02:07 +0100
committerskal <pascal.massimino@gmail.com>2026-03-07 19:02:07 +0100
commite4851ae9f310b44dab25eb979733281002c8953d (patch)
treeee93eb4f2890acfac37a52ca204081783fa46b2e /doc
parent95802739b8ccaf9112fe4fe6e496ba7ae4158aae (diff)
refactor(effects): introduce WgslEffect for shader-only post-process effects
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 <noreply@anthropic.com>
Diffstat (limited to 'doc')
-rw-r--r--doc/AI_RULES.md18
-rw-r--r--doc/ARCHITECTURE.md2
-rw-r--r--doc/AUXILIARY_TEXTURE_INIT.md2
-rw-r--r--doc/CMAKE_MODULES.md2
-rw-r--r--doc/CONTRIBUTING.md36
-rw-r--r--doc/EFFECT_WORKFLOW.md277
-rw-r--r--doc/RECIPE.md2
-rw-r--r--doc/UNIFORM_BUFFER_GUIDELINES.md12
8 files changed, 218 insertions, 133 deletions
diff --git a/doc/AI_RULES.md b/doc/AI_RULES.md
index 1a4ee78..5500a9a 100644
--- a/doc/AI_RULES.md
+++ b/doc/AI_RULES.md
@@ -10,17 +10,27 @@
**IMPORTANT:** When adding new visual effects, follow the complete workflow in `doc/EFFECT_WORKFLOW.md`.
-**Required steps (must complete ALL):**
+**Simple post-process (WgslEffect path — no .cc, no CMake):**
+1. Create `src/effects/<name>.wgsl`
+2. Add shader to `workspaces/main/assets.txt`
+3. Create `src/effects/<name>_effect.h` as thin wrapper over `WgslEffect`
+4. Add extern to `shaders.h` / `shaders.cc`
+5. Include header in `src/gpu/demo_effects.h`
+6. Add to test list in `src/tests/gpu/test_demo_effects.cc`
+7. Add to timeline with `EFFECT +` (priority modifier REQUIRED)
+8. Build and verify: `cmake --build build -j4 && cd build && ./test_demo_effects`
+
+**Complex effects (custom uniforms / 3D / compute — full class):**
1. Create effect files (.h, .cc, .wgsl)
2. Add shader to `workspaces/main/assets.txt`
-3. Add `.cc` to CMakeLists.txt GPU_SOURCES (BOTH sections: headless and normal)
+3. Add `.cc` to `cmake/DemoSourceLists.cmake` COMMON_GPU_EFFECTS
4. Include header in `src/gpu/demo_effects.h`
-5. Add to timeline with `EFFECT +` (priority modifier is REQUIRED)
+5. Add to timeline with `EFFECT +` (priority modifier REQUIRED)
6. Add to test list in `src/tests/gpu/test_demo_effects.cc`
7. Build and verify: `cmake --build build -j4 && cd build && ./test_demo_effects`
**Common mistakes to avoid:**
- Missing priority modifier in timeline (`EFFECT` must be `EFFECT +`, `EFFECT =`, or `EFFECT -`)
-- Adding `.cc` to only one CMakeLists.txt section (need BOTH headless and normal)
+- Adding `.cc` to CMake for WgslEffect thin wrappers (not needed)
- Wrong asset ID (check assets.txt entry name → `ASSET_SHADER_<NAME>`)
- Forgetting to add to test file
diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md
index 582903b..47f6927 100644
--- a/doc/ARCHITECTURE.md
+++ b/doc/ARCHITECTURE.md
@@ -40,7 +40,7 @@ Detailed system architecture for the 64k demo project.
**Effect**: Abstract base for visual elements. Supports `compute` and `render` phases.
-**PostProcessEffect**: Subclass for full-screen post-processing effects.
+**WgslEffect**: Generic subclass for single-pass post-processing effects driven by a WGSL shader string. Use thin wrapper headers (no `.cc`, no CMake entry) for simple effects.
**SDFEffect**: Subclass for SDF raymarching effects with camera management (see SDF Camera System below).
diff --git a/doc/AUXILIARY_TEXTURE_INIT.md b/doc/AUXILIARY_TEXTURE_INIT.md
index 036cbf7..23d111e 100644
--- a/doc/AUXILIARY_TEXTURE_INIT.md
+++ b/doc/AUXILIARY_TEXTURE_INIT.md
@@ -132,7 +132,7 @@ void MyEffect::init(MainSequence* demo) {
## Testing
-All 36 tests pass. Verified:
+All 35 tests pass. Verified:
- Auxiliary textures at correct resolution
- Renderer3D effects don't crash on resize-before-init
- No hardcoded resolution artifacts
diff --git a/doc/CMAKE_MODULES.md b/doc/CMAKE_MODULES.md
index 9f71d91..7cd3e26 100644
--- a/doc/CMAKE_MODULES.md
+++ b/doc/CMAKE_MODULES.md
@@ -17,7 +17,7 @@ The build system is split into 10 specialized modules under `cmake/`:
- **DemoTools.cmake** - Build tools (asset_packer, seq_compiler, tracker_compiler)
- **DemoCodegen.cmake** - Code generation (assets, timeline, music)
- **DemoExecutables.cmake** - Main binaries (demo64k, test_demo)
-- **DemoTests.cmake** - Test infrastructure (36 tests)
+- **DemoTests.cmake** - Test infrastructure (35 tests)
- **Validation.cmake** - Uniform buffer validation
---
diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
index 7fbfd64..c077342 100644
--- a/doc/CONTRIBUTING.md
+++ b/doc/CONTRIBUTING.md
@@ -66,22 +66,30 @@ See `doc/CODING_STYLE.md` for detailed examples.
### Adding Visual Effect
-**For SDF/raymarching effects:** Use `SDFEffect` base class (see `doc/SDF_EFFECT_GUIDE.md`).
+**Full workflow:** See `doc/EFFECT_WORKFLOW.md`.
-**For standard effects:**
-1. Create effect class files (each effect should have its own `.h` and `.cc` file, e.g., `src/effects/my_effect.h` and `src/effects/my_effect.cc`). Use `tools/shadertoy/convert_shadertoy.py` or templates.
-2. Add shader to `workspaces/main/assets.txt`
-3. Add effect `.cc` file to `CMakeLists.txt` GPU_SOURCES (both sections)
-4. Include header in `src/gpu/demo_effects.h` (which now serves as a central include for all individual effect headers)
-5. Add to workspace `timeline.seq` (e.g., `workspaces/main/timeline.seq`)
-6. **Update `src/tests/gpu/test_demo_effects.cc`**:
- - Add to `post_process_effects` list (lines 80-93) or `scene_effects` list (lines 125-137)
- - Example: `{"MyEffect", std::make_shared<MyEffect>(fixture.ctx())},`
-7. Verify:
+**Simple post-process (shader only) — WgslEffect path:**
+1. Write `src/effects/<name>.wgsl`
+2. Add to `workspaces/main/assets.txt`
+3. Create `src/effects/<name>_effect.h` as a thin wrapper (no `.cc`, no CMake entry):
+ ```cpp
+ struct MyEffect : public WgslEffect {
+ MyEffect(const GpuContext& ctx, const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs, float s, float e)
+ : WgslEffect(ctx, inputs, outputs, s, e, my_shader_wgsl) {}
+ };
+ ```
+4. Add `extern const char* my_shader_wgsl;` to `shaders.h`, definition to `shaders.cc`
+5. Include in `src/gpu/demo_effects.h`, add test entry in `test_demo_effects.cc`
+6. Add to `workspaces/main/timeline.seq`
+
+**Complex effects (custom uniforms / 3D / compute):** follow all 7 steps in `doc/EFFECT_WORKFLOW.md`.
+
+**For SDF/raymarching effects:** See `doc/SDF_EFFECT_GUIDE.md`.
+
+Verify:
```bash
-cmake -S . -B build -DDEMO_BUILD_TESTS=ON
-cmake --build build -j4 --target test_demo_effects
-cd build && ./test_demo_effects
+cmake --build build -j4 && cd build && ./test_demo_effects
```
### Audio Initialization
diff --git a/doc/EFFECT_WORKFLOW.md b/doc/EFFECT_WORKFLOW.md
index b71e76d..5d6f017 100644
--- a/doc/EFFECT_WORKFLOW.md
+++ b/doc/EFFECT_WORKFLOW.md
@@ -8,30 +8,139 @@ Checklist for adding visual effects.
## Quick Reference
-**ShaderToy:** `tools/shadertoy/convert_shadertoy.py` then follow steps below
-**SDF/Raymarching:** See `doc/SDF_EFFECT_GUIDE.md`
-**Custom effects:** Follow all steps 1-6
+| Effect type | Path |
+|---|---|
+| Simple post-process (shader only) | **WgslEffect** — 3 files, see below |
+| Custom uniforms / multi-pass / compute / 3D | **Full Effect class** — 6 steps |
+| SDF / Raymarching | See `doc/SDF_EFFECT_GUIDE.md` |
+| ShaderToy port | `tools/shadertoy/convert_shadertoy.py` then Full Effect |
---
-## Workflow
+## Path A: WgslEffect (simple post-process)
+
+Use when the effect needs only a WGSL shader and standard per-frame uniforms
+(`time`, `beat_phase`, `audio_intensity`, `resolution`). No custom C++ logic.
+
+**3 files to touch:**
+
+### 1. Write the shader
+
+`src/effects/<name>.wgsl`
+
+Standard bindings (all provided automatically):
+```wgsl
+#include "sequence_uniforms" // or "common_uniforms"
+#include "render/fullscreen_vs"
+
+@group(0) @binding(0) var smplr: sampler;
+@group(0) @binding(1) var txt: texture_2d<f32>;
+@group(0) @binding(2) var<uniform> uniforms: UniformsSequenceParams;
+
+// Optional: generic params from WgslEffectParams (see below)
+// struct WgslEffectParams { p: vec4f, c: vec4f }
+// @group(0) @binding(3) var<uniform> effect_params: WgslEffectParams;
+
+@fragment fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
+ let uv = pos.xy / uniforms.resolution;
+ // ... your effect
+ return textureSample(txt, smplr, uv);
+}
+```
+
+### 2. Register shader as asset
+
+`workspaces/main/assets.txt`:
+```
+SHADER_<UPPER_NAME>, NONE, ../../src/effects/<name>.wgsl, "Description"
+```
+
+### 3. Add to timeline
+
+`workspaces/main/timeline.seq`:
+```
+EFFECT + WgslEffect source -> sink 0.0 4.0
+```
+
+Wait — the seq_compiler instantiates effects by class name. For a named
+effect (so it appears in test_demo_effects and can be referenced by name),
+create a **thin wrapper header** instead:
+
+`src/effects/<name>_effect.h`:
+```cpp
+#pragma once
+#include "effects/shaders.h"
+#include "gpu/wgsl_effect.h"
+
+struct MyEffect : public WgslEffect {
+ MyEffect(const GpuContext& ctx, const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs, float start_time,
+ float end_time)
+ : WgslEffect(ctx, inputs, outputs, start_time, end_time,
+ my_shader_wgsl) {}
+};
+```
+
+Then add `extern const char* my_shader_wgsl;` to `src/effects/shaders.h`
+and `const char* my_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_<UPPER_NAME>);`
+to `src/effects/shaders.cc`.
+
+Finally include the header in `src/gpu/demo_effects.h` and add a test entry
+in `src/tests/gpu/test_demo_effects.cc`. **No `.cc` file and no CMake entry needed.**
+
+### WgslEffectParams (optional static or dynamic params)
+
+`WgslEffect` has a public `effect_params` member (`WgslEffectParams`) uploaded
+to binding 3 each frame. Use it for static configuration or per-frame modulation.
+
+```cpp
+struct WgslEffectParams {
+ float p[4]; // vec4: generic floats (strength, scale, etc.)
+ float c[4]; // vec4: color or secondary params
+};
+```
+
+**Static** (set at construction via thin wrapper):
+```cpp
+: WgslEffect(ctx, inputs, outputs, start, end, my_shader_wgsl,
+ WGPULoadOp_Clear,
+ WgslEffectParams{{8.0f, 0.5f, 1.0f, 0.0f}, {}})
+```
+
+**Dynamic** (per-frame, from generated Sequence subclass or external code):
+```cpp
+WgslEffect* fx = ...; // store typed ptr alongside shared_ptr<Effect>
+// each frame before render:
+fx->effect_params.p[0] = 8.0f + seq_params.audio_intensity * 4.0f;
+```
+
+Prefer computing animated values in WGSL via `uniforms.time/beat_phase/audio_intensity`
+when possible — no CPU→GPU upload needed.
+
+**loadOp** (default `WGPULoadOp_Clear`): pass `WGPULoadOp_Load` to overlay
+on the existing buffer (e.g. HUD overlays like PeakMeter).
+
+---
+
+## Path B: Full Effect Class (complex effects)
+
+Use when the effect needs: custom uniforms (binding 3 typed struct), multi-pass,
+compute shaders, 3D depth buffers, or non-trivial C++ render logic.
### 1. Create Effect Files
-**Files**:
- Header: `src/effects/<name>_effect.h`
- Implementation: `src/effects/<name>_effect.cc`
- Shader: `src/effects/<name>.wgsl`
-**Class name**: `<Name>Effect` (e.g., `TunnelEffect`)
-
-**Base class**: `Effect` (all effects)
+**Base class**: `Effect`
**Constructor**:
```cpp
MyEffect(const GpuContext& ctx,
- const std::vector<std::string>& inputs,
- const std::vector<std::string>& outputs);
+ const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs,
+ float start_time, float end_time);
```
**Required methods**:
@@ -40,64 +149,52 @@ void render(WGPUCommandEncoder encoder,
const UniformsSequenceParams& params,
NodeRegistry& nodes) override;
-// Optional: for effects needing temp nodes (depth buffers, intermediate textures)
+// Optional: for effects needing temp nodes (depth buffers, etc.)
void declare_nodes(NodeRegistry& registry) override;
```
**Uniforms** (auto-updated by base class):
```cpp
-// Available in render() via params:
params.time; // Physical seconds
params.beat_time; // Musical beats
params.beat_phase; // Fractional beat 0.0-1.0
params.audio_intensity; // Audio peak
params.resolution; // vec2(width, height)
params.aspect_ratio; // width/height
-
-// uniforms_buffer_ automatically initialized and updated
-// No need to call init_uniforms_buffer() or uniforms_buffer_.update()
```
### 2. Add Shader to Assets
-**File**: `workspaces/main/assets.txt`
-
+`workspaces/main/assets.txt`:
```
SHADER_<UPPER_NAME>, NONE, ../../src/effects/<name>.wgsl, "Description"
```
-Asset ID: `AssetId::ASSET_SHADER_<UPPER_NAME>`
-
### 3. Add to DemoSourceLists.cmake
-**File**: `cmake/DemoSourceLists.cmake`
-
-Add `src/effects/<name>_effect.cc` to the `COMMON_GPU_EFFECTS` list.
+`cmake/DemoSourceLists.cmake` — add to `COMMON_GPU_EFFECTS`:
+```cmake
+src/effects/<name>_effect.cc
+```
### 4. Include in demo_effects.h
-**File**: `src/gpu/demo_effects.h`
-
+`src/gpu/demo_effects.h`:
```cpp
#include "effects/<name>_effect.h"
```
### 5. Add to test_demo_effects.cc
-**File**: `src/tests/gpu/test_demo_effects.cc`
-
-Add entry to the effects vector:
+`src/tests/gpu/test_demo_effects.cc`:
```cpp
{"MyEffect", std::make_shared<MyEffect>(
fixture.ctx(), inputs, outputs, 0.0f, 1000.0f)},
```
-Run with: `./build/test_demo_effects`
-
### 6. Add to Timeline
-**File**: `workspaces/main/timeline.seq`
-
+`workspaces/main/timeline.seq`:
```
SEQUENCE <start> <priority> "name"
EFFECT + MyEffect source -> sink 0.0 4.0
@@ -105,98 +202,78 @@ SEQUENCE <start> <priority> "name"
**Priority modifiers** (REQUIRED): `+` (increment), `=` (same), `-` (decrement)
-### 7. Regenerate and Build
+### 7. Build and Verify
```bash
-# Regenerate timeline.cc
-python3 tools/seq_compiler.py workspaces/main/timeline.seq \
- --output src/generated/timeline.cc
-
-# Build
cmake --build build -j4
-
-# Test
-./build/demo64k
+cd build && ./test_demo_effects
+./demo64k
```
---
## Templates
-### Standard Post-Process Effect
+### WgslEffect thin wrapper
```cpp
-// my_effect.h
+// src/effects/my_effect.h
#pragma once
-#include "gpu/effect.h"
-#include "gpu/uniform_helper.h"
-
-class MyEffect : public Effect {
- public:
- MyEffect(const GpuContext& ctx,
- const std::vector<std::string>& inputs,
- const std::vector<std::string>& outputs);
- ~MyEffect() override;
+#include "effects/shaders.h"
+#include "gpu/wgsl_effect.h"
- void render(WGPUCommandEncoder encoder,
- const UniformsSequenceParams& params,
- NodeRegistry& nodes) override;
-
- private:
- WGPURenderPipeline pipeline_;
- WGPUBindGroup bind_group_;
- UniformBuffer<UniformsSequenceParams> uniforms_buffer_;
+struct MyEffect : public WgslEffect {
+ MyEffect(const GpuContext& ctx, const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs, float start_time,
+ float end_time)
+ : WgslEffect(ctx, inputs, outputs, start_time, end_time,
+ my_shader_wgsl) {}
};
```
+No `.cc` file. No CMake entry.
+
+### Full post-process effect
+
```cpp
// my_effect.cc
#include "effects/my_effect.h"
#include "gpu/post_process_helper.h"
-#include "gpu/shaders.h"
+#include "effects/shaders.h"
MyEffect::MyEffect(const GpuContext& ctx,
- const std::vector<std::string>& inputs,
- const std::vector<std::string>& outputs,
- float start_time, float end_time)
+ const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs,
+ float start_time, float end_time)
: Effect(ctx, inputs, outputs, start_time, end_time) {
HEADLESS_RETURN_IF_NULL(ctx_.device);
-
- // uniforms_buffer_ already initialized by base class
+ create_linear_sampler();
pipeline_.set(create_post_process_pipeline(ctx_.device,
- WGPUTextureFormat_RGBA8Unorm,
- my_shader_wgsl));
+ WGPUTextureFormat_RGBA8Unorm,
+ my_shader_wgsl));
}
void MyEffect::render(WGPUCommandEncoder encoder,
- const UniformsSequenceParams& params,
- NodeRegistry& nodes) {
+ const UniformsSequenceParams& params,
+ NodeRegistry& nodes) {
WGPUTextureView input_view = nodes.get_view(input_nodes_[0]);
WGPUTextureView output_view = nodes.get_view(output_nodes_[0]);
- // uniforms_buffer_ already updated by base class dispatch_render()
pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(),
input_view, uniforms_buffer_.get(), {nullptr, 0});
- WGPURenderPassColorAttachment color_attachment = {
- .view = output_view,
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
- .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED,
-#endif
- .loadOp = WGPULoadOp_Clear,
- .storeOp = WGPUStoreOp_Store,
- .clearValue = {0.0, 0.0, 0.0, 1.0}
- };
+ WGPURenderPassColorAttachment color_attachment = {};
+ gpu_init_color_attachment(color_attachment, output_view);
- WGPURenderPassDescriptor pass_desc = {
- .colorAttachmentCount = 1,
- .colorAttachments = &color_attachment
- };
+ 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); // Fullscreen triangle
+ 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);
}
@@ -212,7 +289,6 @@ class My3DEffect : public Effect {
: Effect(ctx, inputs, outputs, start_time, end_time),
depth_node_(outputs[0] + "_depth") {
HEADLESS_RETURN_IF_NULL(ctx_.device);
- // Custom uniforms if needed (don't use base uniforms_buffer_)
}
void declare_nodes(NodeRegistry& registry) override {
@@ -224,19 +300,6 @@ class My3DEffect : public Effect {
NodeRegistry& nodes) override {
WGPUTextureView color_view = nodes.get_view(output_nodes_[0]);
WGPUTextureView depth_view = nodes.get_view(depth_node_);
-
- WGPURenderPassDepthStencilAttachment depth_attachment = {
- .view = depth_view,
- .depthLoadOp = WGPULoadOp_Clear,
- .depthStoreOp = WGPUStoreOp_Discard,
- .depthClearValue = 1.0f
- };
-
- WGPURenderPassDescriptor pass_desc = {
- .colorAttachmentCount = 1,
- .colorAttachments = &color_attachment,
- .depthStencilAttachment = &depth_attachment
- };
// ... render 3D scene
}
};
@@ -251,7 +314,8 @@ class My3DEffect : public Effect {
- Asset ID is `ASSET_` + uppercase entry name
**Build Error: "undefined symbol"**
-- Effect not in `cmake/DemoSourceLists.cmake` COMMON_GPU_EFFECTS
+- Full effect `.cc` not in `cmake/DemoSourceLists.cmake` COMMON_GPU_EFFECTS
+- WgslEffect thin wrappers do NOT need a CMake entry
**Runtime Error: "Node not found"**
- Forgot `declare_nodes()` for temp nodes
@@ -265,6 +329,9 @@ class My3DEffect : public Effect {
## See Also
-- `doc/SEQUENCE.md` - Timeline syntax and architecture
-- `tools/seq_compiler.py` - Compiler implementation
-- `src/effects/*.{h,cc}` - Example effects
+- `src/gpu/wgsl_effect.h` — WgslEffect and WgslEffectParams definitions
+- `src/effects/scratch_effect.h` — minimal WgslEffect thin wrapper example
+- `src/effects/gaussian_blur_effect.h` — WgslEffect with static params example
+- `doc/SEQUENCE.md` — Timeline syntax and architecture
+- `doc/SDF_EFFECT_GUIDE.md` — SDF/raymarching effects
+- `tools/seq_compiler.py` — Compiler implementation
diff --git a/doc/RECIPE.md b/doc/RECIPE.md
index f17afde..93c59d3 100644
--- a/doc/RECIPE.md
+++ b/doc/RECIPE.md
@@ -161,7 +161,7 @@ EFFECT + MyEffect 0.0 10.0 strength=0.5 speed=3.0
EFFECT = MyEffect 10.0 20.0 strength=2.0 # speed keeps previous value
```
-**Example:** `src/effects/flash_effect.cc`, `src/effects/chroma_aberration_effect.cc`
+**Example:** `src/effects/chroma_aberration_effect.cc`
## Uniform Buffer Alignment
diff --git a/doc/UNIFORM_BUFFER_GUIDELINES.md b/doc/UNIFORM_BUFFER_GUIDELINES.md
index c6cf9c8..45c7df3 100644
--- a/doc/UNIFORM_BUFFER_GUIDELINES.md
+++ b/doc/UNIFORM_BUFFER_GUIDELINES.md
@@ -97,15 +97,15 @@ static_assert(sizeof(CommonPostProcessUniforms) == 32,
"CommonPostProcessUniforms must be 32 bytes for WGSL alignment");
```
-**Example (C++ GaussianBlurParams):**
+**Example (C++ WgslEffectParams — generic params for WgslEffect thin wrappers):**
```cpp
-struct GaussianBlurParams {
- float strength = 2.0f;
- float _pad = 0.0f;
+struct WgslEffectParams {
+ float p[4]; // vec4: generic float params (strength, scale, etc.)
+ float c[4]; // vec4: color or secondary params
};
-static_assert(sizeof(GaussianBlurParams) == 8,
- "GaussianBlurParams must be 8 bytes for WGSL alignment");
+static_assert(sizeof(WgslEffectParams) == 32,
+ "WgslEffectParams must be 32 bytes");
```
**Example (C++ CameraParams):**