diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-09 10:34:01 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-09 10:34:01 +0100 |
| commit | e50db1721f67a6c1b565b0ac151ba32fcae21cbf (patch) | |
| tree | 306bf626fb0283e1c3b173eb6f3abf556679d1ec | |
| parent | df39c7e3efa70376fac579b178c803eb319d517f (diff) | |
fix: Resolve WebGPU uniform buffer alignment issues (Task #74)
Fixed multiple WGSL/C++ struct alignment mismatches causing validation errors:
Padding fixes:
- fade_effect.cc: Changed EffectParams padding from vec3<f32> to _pad0/1/2
- theme_modulation_effect.cc: Same padding fix for EffectParams
- Root cause: WGSL vec3<f32> has 16-byte alignment, creating 32-byte structs
ODR violation fix:
- demo_effects.h: Added includes for fade_effect.h, theme_modulation_effect.h
- Removed incomplete forward declarations (88 bytes) conflicting with
complete definitions (96 bytes), causing heap buffer overflow in make_shared
Member shadowing cleanup:
- Renamed Effect::uniforms_ shadowing members to descriptive names:
- FadeEffect: uniforms_ -> common_uniforms_
- FlashEffect: uniforms_ -> flash_uniforms_
- ThemeModulationEffect: uniforms_ -> common_uniforms_
Results:
- demo64k runs without crashes
- 33/33 tests passing (100%)
- Added Task #75: WGSL uniform validation tool
handoff(Claude): Uniform buffer alignment debugged and fixed
| -rw-r--r-- | TODO.md | 74 | ||||
| -rw-r--r-- | src/gpu/demo_effects.h | 20 | ||||
| -rw-r--r-- | src/gpu/effects/fade_effect.cc | 10 | ||||
| -rw-r--r-- | src/gpu/effects/fade_effect.h | 2 | ||||
| -rw-r--r-- | src/gpu/effects/flash_effect.cc | 6 | ||||
| -rw-r--r-- | src/gpu/effects/flash_effect.h | 2 | ||||
| -rw-r--r-- | src/gpu/effects/theme_modulation_effect.cc | 10 | ||||
| -rw-r--r-- | src/gpu/effects/theme_modulation_effect.h | 2 |
8 files changed, 95 insertions, 31 deletions
@@ -6,7 +6,13 @@ This file tracks prioritized tasks with detailed attack plans. ## Recently Completed (February 9, 2026) -- [x] **Uniform Buffer Alignment (Task #74)**: Fixed WGSL struct alignment issues. Changed `vec3<f32>` padding to individual `f32` fields in circle_mask_compute.wgsl. Demo runs with 0 validation errors, 32/33 tests passing. +- [x] **Uniform Buffer Alignment (Task #74)**: Fixed WGSL struct alignment issues across multiple shaders: + - `circle_mask_compute.wgsl`: Changed `_pad: vec3<f32>` to three `f32` fields + - `fade_effect.cc`: Changed EffectParams padding from `vec3<f32>` to `_pad0/1/2: f32` + - `theme_modulation_effect.cc`: Same padding fix for EffectParams + - Fixed ODR violation in `demo_effects.h` (incomplete FadeEffect forward declaration) + - Renamed shadowing `uniforms_` members to `common_uniforms_`/`flash_uniforms_` + - Result: demo64k runs without crashes, 33/33 tests passing (100%) ## Previously Completed (February 8, 2026) @@ -37,6 +43,72 @@ This file tracks prioritized tasks with detailed attack plans. --- +## Priority 1: WGSL Uniform Buffer Validation & Consolidation (Task #75) + +**Goal**: Prevent alignment bugs by consolidating uniform buffer patterns and creating automated validation. + +**Background**: Recent bugs (Task #74) revealed WGSL `vec3<f32>` alignment issues causing 16-byte padding where 12 bytes expected. Need systematic approach to prevent recurrence. + +**Attack Plan**: + +### Phase 1: Audit & Document (1-2 hours) +- [ ] **1.1**: Audit all WGSL shaders for uniform struct definitions + - List all uniform structs, their sizes, and padding strategies + - Identify inconsistencies (vec3 padding vs individual f32 fields) + - Document in `doc/UNIFORM_BUFFER_GUIDELINES.md` +- [ ] **1.2**: Audit C++ struct definitions (CommonPostProcessUniforms, etc.) + - Verify static_assert size checks exist for all uniform structs + - Check for missing size validation + +### Phase 2: Consolidation (2-3 hours) +- [ ] **2.1**: Standardize on CommonUniforms pattern + - All post-process effects should use CommonPostProcessUniforms for binding 2 + - Effect-specific params at binding 3 (16 or 32 bytes, properly padded) +- [ ] **2.2**: Eliminate `vec3<f32>` in padding fields + - Replace all `_pad: vec3<f32>` with `_pad0/1/2: f32` + - Apply to: FadeEffect, ThemeModulationEffect, any other effects +- [ ] **2.3**: Add C++ wrapper structs with static_assert + - Every WGSL uniform struct should have matching C++ struct + - All structs require `static_assert(sizeof(...) == EXPECTED_SIZE)` + +### Phase 3: Validation Tool (3-4 hours) +- [ ] **3.1**: Create `tools/validate_uniforms.py` + - Parse WGSL shader files for uniform struct definitions + - Calculate expected size using WGSL alignment rules: + - `f32`: 4-byte aligned + - `vec2<f32>`: 8-byte aligned + - `vec3<f32>`: **16-byte aligned** (not 12!) + - `vec4<f32>`: 16-byte aligned + - Struct size: rounded to largest member alignment +- [ ] **3.2**: Parse C++ headers for matching structs + - Extract `sizeof()` from static_assert statements + - Match WGSL struct names to C++ struct names +- [ ] **3.3**: Report mismatches + - Exit non-zero if C++ size != WGSL size + - Print detailed alignment breakdown for debugging +- [ ] **3.4**: Integrate into CI/build system + - Add CMake custom command to run validation + - Fail build if validation fails (development builds only) + - Add to `scripts/check_all.sh` + +### Phase 4: Documentation (1 hour) +- [ ] **4.1**: Write `doc/UNIFORM_BUFFER_GUIDELINES.md` + - Explain WGSL alignment rules (with examples) + - Document standard patterns (CommonUniforms, effect params) + - Show correct padding techniques + - Add examples of common mistakes +- [ ] **4.2**: Update CONTRIBUTING.md + - Add "Uniform Buffer Checklist" section + - Require validation tool passes before commit + +**Size Impact**: Negligible (consolidation may save 50-100 bytes) + +**Priority**: High (prevents entire class of subtle bugs) + +**Dependencies**: None + +--- + ## Priority 1: Spectral Brush Editor (Task #5) [IN PROGRESS] **Goal:** Create a web-based tool for procedurally tracing audio spectrograms. Replaces large `.spec` binary assets with tiny procedural C++ code (50-100× compression). diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index 7ad8261..54bf657 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -7,10 +7,12 @@ #include "3d/scene.h" #include "effect.h" #include "gpu/effects/circle_mask_effect.h" +#include "gpu/effects/fade_effect.h" // FadeEffect with full definition #include "gpu/effects/flash_effect.h" // FlashEffect with params support #include "gpu/effects/post_process_helper.h" #include "gpu/effects/rotating_cube_effect.h" #include "gpu/effects/shaders.h" +#include "gpu/effects/theme_modulation_effect.h" // ThemeModulationEffect with full definition #include "gpu/gpu.h" #include "gpu/texture_manager.h" #include "gpu/uniform_helper.h" @@ -197,22 +199,8 @@ class FlashCubeEffect : public Effect { float flash_intensity_; }; -class ThemeModulationEffect : public PostProcessEffect { - public: - ThemeModulationEffect(const GpuContext& ctx); - void render(WGPURenderPassEncoder pass, float time, float beat, - float intensity, float aspect_ratio) override; - void update_bind_group(WGPUTextureView input_view) override; -}; - -class FadeEffect : public PostProcessEffect { - public: - FadeEffect(const GpuContext& ctx); - void render(WGPURenderPassEncoder pass, float time, float beat, - float intensity, float aspect_ratio) override; - void update_bind_group(WGPUTextureView input_view) override; -}; - +// ThemeModulationEffect now defined in gpu/effects/theme_modulation_effect.h (included above) +// FadeEffect now defined in gpu/effects/fade_effect.h (included above) // FlashEffect now defined in gpu/effects/flash_effect.h (included above) // Auto-generated functions diff --git a/src/gpu/effects/fade_effect.cc b/src/gpu/effects/fade_effect.cc index c70177e..3efc583 100644 --- a/src/gpu/effects/fade_effect.cc +++ b/src/gpu/effects/fade_effect.cc @@ -24,7 +24,9 @@ FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) { struct EffectParams { fade_amount: f32, - _pad: vec3<f32>, + _pad0: f32, + _pad1: f32, + _pad2: f32, }; @group(0) @binding(0) var inputSampler: sampler; @@ -55,14 +57,14 @@ FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) { pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, shader_code); - uniforms_.init(ctx_.device); + common_uniforms_.init(ctx_.device); 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_); + common_uniforms_.get(), params_buffer_); } void FadeEffect::render(WGPURenderPassEncoder pass, float time, float beat, @@ -74,7 +76,7 @@ void FadeEffect::render(WGPURenderPassEncoder pass, float time, float beat, .beat = beat, .audio_intensity = intensity, }; - uniforms_.update(ctx_.queue, u); + common_uniforms_.update(ctx_.queue, u); // Example fade pattern: fade in at start, fade out at end // Customize this based on your needs diff --git a/src/gpu/effects/fade_effect.h b/src/gpu/effects/fade_effect.h index 485cc01..22b8f76 100644 --- a/src/gpu/effects/fade_effect.h +++ b/src/gpu/effects/fade_effect.h @@ -16,6 +16,6 @@ class FadeEffect : public PostProcessEffect { void update_bind_group(WGPUTextureView input_view) override; private: - UniformBuffer<CommonPostProcessUniforms> uniforms_; + UniformBuffer<CommonPostProcessUniforms> common_uniforms_; GpuBuffer params_buffer_; }; diff --git a/src/gpu/effects/flash_effect.cc b/src/gpu/effects/flash_effect.cc index ccf0756..57c1d73 100644 --- a/src/gpu/effects/flash_effect.cc +++ b/src/gpu/effects/flash_effect.cc @@ -55,12 +55,12 @@ FlashEffect::FlashEffect(const GpuContext& ctx, const FlashEffectParams& params) pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, shader_code); - uniforms_.init(ctx_.device); + flash_uniforms_.init(ctx_.device); } void FlashEffect::update_bind_group(WGPUTextureView input_view) { pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, - uniforms_.get(), {}); + flash_uniforms_.get(), {}); } void FlashEffect::render(WGPURenderPassEncoder pass, float time, float beat, @@ -88,7 +88,7 @@ void FlashEffect::render(WGPURenderPassEncoder pass, float time, float beat, ._pad1 = {0.0f, 0.0f}, // Padding for vec3 alignment .color = {r, g, b}, // Time-dependent, computed every frame ._pad2 = 0.0f}; - uniforms_.update(ctx_.queue, u); + flash_uniforms_.update(ctx_.queue, u); wgpuRenderPassEncoderSetPipeline(pass, pipeline_); wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); diff --git a/src/gpu/effects/flash_effect.h b/src/gpu/effects/flash_effect.h index d7a438b..55bad7b 100644 --- a/src/gpu/effects/flash_effect.h +++ b/src/gpu/effects/flash_effect.h @@ -39,7 +39,7 @@ class FlashEffect : public PostProcessEffect { private: FlashEffectParams params_; - UniformBuffer<FlashUniforms> uniforms_; + UniformBuffer<FlashUniforms> flash_uniforms_; UniformBuffer<FlashEffectParams> params_buffer_; float flash_intensity_ = 0.0f; }; diff --git a/src/gpu/effects/theme_modulation_effect.cc b/src/gpu/effects/theme_modulation_effect.cc index fc52b62..f9ae636 100644 --- a/src/gpu/effects/theme_modulation_effect.cc +++ b/src/gpu/effects/theme_modulation_effect.cc @@ -26,7 +26,9 @@ ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx) struct EffectParams { theme_brightness: f32, - _pad: vec3<f32>, + _pad0: f32, + _pad1: f32, + _pad2: f32, }; @group(0) @binding(0) var inputSampler: sampler; @@ -59,14 +61,14 @@ ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx) pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format, shader_code); - uniforms_.init(ctx_.device); + common_uniforms_.init(ctx_.device); 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_); + common_uniforms_.get(), params_buffer_); } void ThemeModulationEffect::render(WGPURenderPassEncoder pass, float time, @@ -79,7 +81,7 @@ void ThemeModulationEffect::render(WGPURenderPassEncoder pass, float time, .beat = beat, .audio_intensity = intensity, }; - uniforms_.update(ctx_.queue, u); + common_uniforms_.update(ctx_.queue, u); // Alternate between bright and dark every 4 seconds (2 pattern changes) // Music patterns change every 2 seconds at 120 BPM diff --git a/src/gpu/effects/theme_modulation_effect.h b/src/gpu/effects/theme_modulation_effect.h index b972509..107529b 100644 --- a/src/gpu/effects/theme_modulation_effect.h +++ b/src/gpu/effects/theme_modulation_effect.h @@ -16,6 +16,6 @@ class ThemeModulationEffect : public PostProcessEffect { void update_bind_group(WGPUTextureView input_view) override; private: - UniformBuffer<CommonPostProcessUniforms> uniforms_; + UniformBuffer<CommonPostProcessUniforms> common_uniforms_; GpuBuffer params_buffer_; }; |
