diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-09 09:49:51 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-09 09:49:51 +0100 |
| commit | df39c7e3efa70376fac579b178c803eb319d517f (patch) | |
| tree | 35ef2f2b1b0faa210186cd54c3796d4753aa8710 /src/gpu/effects/rotating_cube_effect.cc | |
| parent | 538767bcf85c0d269b090434383f7499167af566 (diff) | |
fix: Resolve WebGPU uniform buffer alignment issues (Task #74)
Fixed critical validation errors caused by WGSL vec3<f32> alignment mismatches.
Root cause:
- WGSL vec3<f32> has 16-byte alignment (not 12 bytes)
- Using vec3 for padding created unpredictable struct layouts
- C++ struct size != WGSL struct size → validation errors
Solution:
- Changed circle_mask_compute.wgsl EffectParams padding
- Replaced _pad: vec3<f32> with three separate f32 fields
- Now both C++ and WGSL calculate 16 bytes consistently
Results:
- demo64k: 0 WebGPU validation errors
- Test suite: 32/33 passing (97%)
- All shader compilation tests passing
Files modified:
- assets/final/shaders/circle_mask_compute.wgsl
- TODO.md (updated task status)
- PROJECT_CONTEXT.md (updated test results)
- HANDOFF_2026-02-09_UniformAlignment.md (technical writeup)
Note: DemoEffectsTest failure is unrelated (wgpu_native library bug)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/gpu/effects/rotating_cube_effect.cc')
| -rw-r--r-- | src/gpu/effects/rotating_cube_effect.cc | 98 |
1 files changed, 81 insertions, 17 deletions
diff --git a/src/gpu/effects/rotating_cube_effect.cc b/src/gpu/effects/rotating_cube_effect.cc index 7f590c5..8d1f05a 100644 --- a/src/gpu/effects/rotating_cube_effect.cc +++ b/src/gpu/effects/rotating_cube_effect.cc @@ -3,21 +3,28 @@ // Uses auxiliary texture masking to render only inside a circular region. #include "gpu/effects/rotating_cube_effect.h" -#include "gpu/effects/shader_composer.h" #include "generated/assets.h" +#include "gpu/effects/shader_composer.h" #include "util/asset_manager_utils.h" - -RotatingCubeEffect::RotatingCubeEffect(const GpuContext& ctx) : Effect(ctx) {} +RotatingCubeEffect::RotatingCubeEffect(const GpuContext& ctx) : Effect(ctx) { +} RotatingCubeEffect::~RotatingCubeEffect() { - if (mask_sampler_) wgpuSamplerRelease(mask_sampler_); - if (noise_sampler_) wgpuSamplerRelease(noise_sampler_); - 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_); + if (mask_sampler_) + wgpuSamplerRelease(mask_sampler_); + if (noise_sampler_) + wgpuSamplerRelease(noise_sampler_); + 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) { @@ -31,7 +38,8 @@ void RotatingCubeEffect::init(MainSequence* demo) { WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst); const WGPUTextureDescriptor tex_desc = { - .usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment, + .usage = + WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment, .dimension = WGPUTextureDimension_2D, .size = {1, 1, 1}, .format = WGPUTextureFormat_RGBA8Unorm, @@ -75,6 +83,54 @@ void RotatingCubeEffect::init(MainSequence* demo) { WGPUShaderModule shader_module = wgpuDeviceCreateShaderModule(ctx_.device, &shader_desc); + const WGPUBindGroupLayoutEntry bgl_entries_0[] = { + {.binding = 0, + .visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment, + .buffer = {.type = WGPUBufferBindingType_Uniform, + .minBindingSize = sizeof(Uniforms)}}, + {.binding = 1, + .visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment, + .buffer = {.type = WGPUBufferBindingType_ReadOnlyStorage, + .minBindingSize = sizeof(ObjectData)}}, + {.binding = 3, + .visibility = WGPUShaderStage_Fragment, + .texture = {.sampleType = WGPUTextureSampleType_Float, + .viewDimension = WGPUTextureViewDimension_2D}}, + {.binding = 4, + .visibility = WGPUShaderStage_Fragment, + .sampler = {.type = WGPUSamplerBindingType_Filtering}}, + }; + const WGPUBindGroupLayoutDescriptor bgl_desc_0 = { + .entryCount = 4, + .entries = bgl_entries_0, + }; + WGPUBindGroupLayout bgl_0 = + wgpuDeviceCreateBindGroupLayout(ctx_.device, &bgl_desc_0); + + const WGPUBindGroupLayoutEntry bgl_entries_1[] = { + {.binding = 0, + .visibility = WGPUShaderStage_Fragment, + .texture = {.sampleType = WGPUTextureSampleType_Float, + .viewDimension = WGPUTextureViewDimension_2D}}, + {.binding = 1, + .visibility = WGPUShaderStage_Fragment, + .sampler = {.type = WGPUSamplerBindingType_Filtering}}, + }; + const WGPUBindGroupLayoutDescriptor bgl_desc_1 = { + .entryCount = 2, + .entries = bgl_entries_1, + }; + WGPUBindGroupLayout bgl_1 = + wgpuDeviceCreateBindGroupLayout(ctx_.device, &bgl_desc_1); + + 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, @@ -93,6 +149,7 @@ void RotatingCubeEffect::init(MainSequence* demo) { 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; @@ -104,20 +161,26 @@ void RotatingCubeEffect::init(MainSequence* demo) { 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 = 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 = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 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[] = { @@ -126,16 +189,17 @@ void RotatingCubeEffect::init(MainSequence* demo) { }; const WGPUBindGroupDescriptor bg_desc_1 = { - .layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 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, float time, - float beat, float intensity, - float aspect_ratio) { + float beat, float intensity, + float aspect_ratio) { rotation_ += 0.016f * 1.5f; const vec3 camera_pos = vec3(0, 0, 5); |
