# Handoff: Shader Parametrization System (February 8, 2026) ## Summary Completed comprehensive shader parametrization system enabling dynamic per-frame parameters for visual effects via uniform buffers and .seq file syntax. ## Work Completed ✅ ### Shader Parametrization System (Task #73 Phase 0) **Goal**: Enable artists to configure visual effects with parameters (color, intensity, decay rates) via .seq files without touching C++ code. **Use Cases**: Flash effect with custom colors, chromatic aberration strength control, blur radius adjustment, distortion parameters. **Implementation**: #### Phase 1: UniformHelper Template - Created `src/gpu/uniform_helper.h` - Generic type-safe wrapper for WebGPU uniform buffers - Template class handles buffer creation, updates, and lifetime management - Zero-overhead abstraction over `gpu_create_buffer()` and `wgpuQueueWriteBuffer()` ```cpp template class UniformBuffer { void init(WGPUDevice device); void update(WGPUQueue queue, const T& data); GpuBuffer& get(); }; ``` #### Phase 2: FlashEffect Parameter Structs - Added `FlashEffectParams` (constructor-time base parameters): - `color[3]` - RGB flash color (default: white) - `decay_rate` - Flash fade rate per frame (default: 0.98) - `trigger_threshold` - Intensity threshold to trigger flash (default: 0.7) - Added `FlashUniforms` (GPU buffer layout with WGSL alignment): - `flash_intensity` (4 bytes, offset 0) - `intensity` (4 bytes, offset 4) - `_pad1[2]` (8 bytes, offset 8-15) - **Padding for vec3 alignment** - `color[3]` (12 bytes, offset 16-27) - **Aligned to 16 bytes** - `_pad2` (4 bytes, offset 28-31) - Total: 32 bytes (enforced with `static_assert`) #### Phase 3: Parameterized WGSL Shader - Updated `flash_effect.cc` shader to use `uniforms.flash_color` instead of hardcoded white - Shader bindings: sampler (0), texture (1), uniforms (2) - Fragment shader mixes input color with parameterized flash color based on flash intensity ```wgsl struct Uniforms { flash_intensity: f32, intensity: f32, flash_color: vec3, // Parameterized color _pad: f32, }; fn fs_main(input: VertexOutput) -> @location(0) vec4 { let color = textureSample(inputTexture, inputSampler, input.uv); var flashed = mix(color.rgb, uniforms.flash_color, uniforms.flash_intensity); return vec4(flashed, color.a); } ``` #### Phase 4: Per-Frame Parameter Computation - Implemented dynamic parameter animation in `FlashEffect::render()` method - Parameters computed each frame based on time, beat, and intensity - Example: Color modulated by sinusoidal functions of time and beat ```cpp // Animate color based on time and beat const float r = params_.color[0] * (0.5f + 0.5f * sinf(time * 0.5f)); const float g = params_.color[1] * (0.5f + 0.5f * cosf(time * 0.7f)); const float b = params_.color[2] * (1.0f + 0.3f * beat); const FlashUniforms u = { .flash_intensity = flash_intensity_, .intensity = intensity, ._pad1 = {0.0f, 0.0f}, .color = {r, g, b}, .pad2 = 0.0f }; uniforms_.update(ctx_.queue, u); ``` #### Phase 5: .seq Syntax Extension - Extended `seq_compiler.cc` to parse `key=value` parameters after effect class name - Added `parse_parameters()` function to extract parameter pairs - Code generation creates `FlashEffectParams` struct initialization for parameterized effects - Backward compatibility: Effects without parameters use default constructor **Syntax Example**: ``` EFFECT + FlashEffect 0 1 color=1.0,0.5,0.5 decay=0.95 ``` **Generated Code**: ```cpp { FlashEffectParams p; p.color[0] = 1.0f; p.color[1] = 0.5f; p.color[2] = 0.5f; p.decay_rate = 0.95f; seq->add_effect(std::make_shared(ctx, p), 0.0f, 1.f, 0); } ``` --- ### Critical Bugfix: WGSL Alignment **Problem**: WebGPU validation error "Buffer is bound with size 24 where the shader expects 32" **Root Cause**: WGSL alignment rules require `vec3` to be 16-byte aligned despite having 12-byte size. Initial `FlashUniforms` struct layout was: ```cpp struct FlashUniforms { float flash_intensity; // 0-3 float intensity; // 4-7 float color[3]; // 8-19 (WRONG - no padding) float _pad; // 20-23 }; // Total: 24 bytes (WRONG) ``` **Solution**: Added explicit padding to align `color` array to 16-byte boundary: ```cpp struct FlashUniforms { float flash_intensity; // 0-3 float intensity; // 4-7 float _pad1[2]; // 8-15 (padding) float color[3]; // 16-27 (aligned to 16 bytes) float _pad2; // 28-31 }; // Total: 32 bytes (CORRECT) static_assert(sizeof(FlashUniforms) == 32, "FlashUniforms must be 32 bytes"); ``` **Impact**: Demo now runs without validation errors, all 32/32 tests pass. --- ## Files Modified **New Files (3)**: - `src/gpu/uniform_helper.h` - UniformBuffer template class - `src/tests/test_uniform_helper.cc` - Unit test for template compilation - `doc/SHADER_PARAMETRIZATION_PLAN.md` - Complete design document **Modified Files (11)**: - `src/gpu/effects/flash_effect.h` - Added FlashEffectParams, FlashUniforms structs, parameterized constructor - `src/gpu/effects/flash_effect.cc` - Implemented per-frame computation, updated shader with parameterized color - `src/gpu/demo_effects.h` - Removed duplicate FlashEffect definition, added include - `tools/seq_compiler.cc` - Added parameter parsing and code generation - `assets/demo.seq` - Added example with parameters - `src/generated/timeline.cc` - Generated code with struct initialization - `CMakeLists.txt` - Added test_uniform_helper target - `TODO.md` - Added Task #73 (extend to other effects), moved completion to summary - `PROJECT_CONTEXT.md` - Updated "Recently Completed" section - `doc/COMPLETED.md` - Added detailed completion entry - `doc/HANDOFF_2026-02-08.md` - This document --- ## Commits Made **c7d1dd7** - `feat(gpu): Implement shader parametrization system` - Phases 1-5 implementation - 11 files changed (808+ / 40-) - UniformHelper template + FlashEffect parametrization + .seq syntax **775c0ea** - `fix(gpu): Correct FlashUniforms struct alignment for WGSL` - Critical alignment bugfix (24 → 32 bytes) - Added static_assert for future safety - Fixed validation error --- ## Test Results **All 32/32 tests passing (100%)** **Test Coverage**: - `test_uniform_helper.cc` - Verifies UniformBuffer template compiles correctly - `test_demo_effects.cc` - All post-process and scene effects instantiate successfully - Full shader compilation tests pass with new parameterized shaders **Verification**: - Demo runs without WebGPU validation errors - Visual effects render with parameterized colors - .seq file parsing generates correct parameter initialization code - Per-frame animation produces time-dependent color modulation --- ## Current Status **Completed:** - ✅ Full shader parametrization infrastructure - ✅ FlashEffect fully parameterized with color/decay control - ✅ .seq syntax extension with key=value parsing - ✅ Per-frame dynamic parameter computation - ✅ Critical WGSL alignment bug fixed - ✅ Documentation updated (TODO.md, PROJECT_CONTEXT.md, COMPLETED.md) **Ready for:** - Task #73: Extend parametrization to other effects (ChromaAberrationEffect, GaussianBlurEffect, DistortEffect, SolarizeEffect) - Additional parameter types (vec2, vec4, enums) - Parameter curve system for keyframe animation (future) --- ## Technical Notes ### WGSL Alignment Rules **Critical for future implementations:** - `vec3` has **size = 12 bytes** but **alignment = 16 bytes** - Always add padding before vec3 fields to align to 16-byte boundary - Use `static_assert(sizeof(YourStruct) == expected_size)` to catch misalignment - Reference: [WebGPU WGSL Spec - Alignment and Size](https://www.w3.org/TR/WGSL/#alignment-and-size) ### UniformHelper Pattern **Reusable template for any effect parameters:** ```cpp // 1. Define parameter structs struct YourEffectParams { /* constructor-time params */ }; struct YourEffectUniforms { /* GPU buffer layout with padding */ }; static_assert(sizeof(YourEffectUniforms) == expected_size); // 2. Add UniformBuffer member UniformBuffer uniforms_; // 3. Initialize in constructor uniforms_.init(ctx_.device); // 4. Update in render() const YourEffectUniforms u = { /* compute values */ }; uniforms_.update(ctx_.queue, u); // 5. Bind in shader pipeline_ = create_pipeline(...); bind_group_ = create_bind_group(..., uniforms_.get().buffer, ...); ``` ### Backward Compatibility **All existing effects continue to work:** - Default parameter constructor delegates to parameterized constructor - Effects without .seq parameters use default values - No changes required to existing timeline code --- ## Design Document **Complete design**: See `doc/SHADER_PARAMETRIZATION_PLAN.md` **Key Sections**: - Motivation and use cases - 5-phase implementation plan - WGSL alignment rules and solutions - Per-frame vs constructor-time parameters - .seq syntax specification - Migration path for existing effects - Size budget analysis (~400-500 bytes) - Alternative approaches considered --- ## Size Impact **Binary Size**: +400-500 bytes net - UniformHelper template code: ~200 bytes - FlashEffectParams/Uniforms structs: ~100 bytes - Per-effect parameter overhead: ~50-100 bytes each - .seq compiler parameter parsing: ~100 bytes **Trade-off**: Acceptable size increase for significant artist workflow improvement (no C++ recompilation for parameter tweaks). --- ## Next Steps (Recommendations) ### Immediate (Task #73) Extend parametrization to other effects using FlashEffect as template: 1. **ChromaAberrationEffect**: - Parameters: `offset` (vec2), `strength` (float) - Syntax: `EFFECT + ChromaAberrationEffect 0 5 offset=0.01,0.02 strength=1.5` 2. **GaussianBlurEffect**: - Parameters: `radius` (float), `sigma` (float) - Syntax: `EFFECT + GaussianBlurEffect 2 8 radius=5.0 sigma=2.0` 3. **DistortEffect** (if exists): - Parameters: `amount` (float), `speed` (float), `scale` (vec2) 4. **SolarizeEffect**: - Parameters: `threshold` (float), `intensity` (float) ### Future Enhancements - Parameter curve system (keyframe animation via .seq timeline) - vec4/mat4 parameter support (color with alpha, transformation matrices) - Enum parameters (blend modes, filter types) - Parameter validation and clamping (min/max ranges) --- ## Context for Next Session ### What Works - Shader parametrization infrastructure fully functional - FlashEffect demonstrates complete implementation pattern - .seq syntax parsing robust with backward compatibility - All tests passing, demo stable ### Known Limitations - Only FlashEffect parametrized (other effects use hardcoded values) - No parameter validation or range clamping - No keyframe animation support (future feature) ### Migration Pattern for Other Effects 1. Copy `FlashEffectParams` / `FlashUniforms` pattern 2. Add `UniformBuffer` member 3. Update shader to use uniform values 4. Implement per-frame computation in `render()` 5. Extend `seq_compiler.cc` with effect-specific parameter parsing 6. Test with .seq file, verify alignment with static_assert --- ## User Feedback Summary **Session Start**: User requested shader parametrization with use cases (flash colors, aberration strength) **Key Clarifications**: - User confirmed parameters will be "generated in the code (manually, in effect's C++ code)" - User emphasized "precision: user change parameter values on a per-frame basis. time-dependent" - Critical feedback: Per-frame computation required, not just static constructor params **Critical Bug Report**: User provided log.txt showing GPU validation error (buffer size mismatch) **Outcome**: User satisfied, requested commit + documentation update (completed) --- ## Handoff Checklist - [x] All tests passing (32/32) - [x] Working tree clean - [x] Documentation updated (TODO.md, PROJECT_CONTEXT.md, COMPLETED.md) - [x] Commits created with detailed messages (c7d1dd7, 775c0ea) - [x] No known regressions - [x] Design document complete (SHADER_PARAMETRIZATION_PLAN.md) - [x] Extension task noted (Task #73) - [x] Ready for next session --- **handoff(Claude):** Shader parametrization system complete. FlashEffect fully parameterized with .seq syntax. Critical alignment bug fixed. 32/32 tests passing. Ready for extension to other effects (Task #73). --- *Generated: February 8, 2026* *Claude Sonnet 4.5*