diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-08 22:17:31 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-08 22:17:31 +0100 |
| commit | 1b89a26a750cc32725564a0c5186a437f372fd93 (patch) | |
| tree | 9873fbce565a36cbf0dd081d7e100e0b0867139a /src/gpu/effects | |
| parent | 86e56474d284944795f4c02ae850561374620f8a (diff) | |
feat: Fix CircleMaskEffect and RotatingCubeEffect auxiliary texture masking
Resolves critical shader composition and format mismatch issues, enabling the
circle mask + rotating cube demonstration at 2.0s-4.0s in the timeline.
**Key Fixes:**
1. **Shader Function Name** (masked_cube.wgsl):
- Fixed call to `ray_box_intersection()` (was incorrectly `ray_box()`)
- Updated return value handling to use `RayBounds` struct (`.hit`, `.t_entry`, `.t_exit`)
- Removed unused `sky_tex` binding to match pipeline layout expectations (5 bindings → 4)
2. **Shader Lifetime Issue** (rotating_cube_effect.h/cc):
- Added `std::string composed_shader_` member to persist shader source
- Prevents use-after-free when WebGPU asynchronously parses shader code
3. **Format Mismatch** (circle_mask_effect.cc):
- Changed compute pipeline format from hardcoded `RGBA8Unorm` to `ctx_.format` (Bgra8UnormSrgb)
- Matches auxiliary texture format created by `MainSequence::register_auxiliary_texture()`
- Added depth stencil state to render pipeline to match scene pass requirements:
* Format: Depth24Plus
* depthWriteEnabled: False (no depth writes needed)
* depthCompare: Always (always pass depth test)
4. **WebGPU Descriptor Initialization** (circle_mask_effect.cc):
- Added `depthSlice = WGPU_DEPTH_SLICE_UNDEFINED` for non-Windows builds
- Ensures proper initialization of render pass color attachments
5. **Test Coverage** (test_demo_effects.cc):
- Updated EXPECTED_SCENE_COUNT: 6 → 8 (added CircleMaskEffect, RotatingCubeEffect)
- Marked both effects as requiring 3D pipeline setup (skipped in basic tests)
**Technical Details:**
- **Auxiliary Texture Flow**: CircleMaskEffect generates mask (1.0 inside, 0.0 outside) →
RotatingCubeEffect samples mask to render only inside circle → GaussianBlurEffect post-processes
- **Pipeline Format Matching**: All pipelines targeting same render pass must use matching formats:
* Color: Bgra8UnormSrgb (system framebuffer format)
* Depth: Depth24Plus (scene pass depth buffer)
- **ShaderComposer Integration**: Relies on `InitShaderComposer()` (called in `gpu.cc:372`)
registering snippets: `common_uniforms`, `math/sdf_utils`, `ray_box`, etc.
**Effect Behavior:**
- Runs from 2.0s to 4.0s in demo timeline
- CircleMaskEffect (priority 0): Draws green outside circle, transparent inside
- RotatingCubeEffect (priority 1): Renders bump-mapped cube inside circle
- GaussianBlurEffect (priority 2): Post-process blur on entire composition
**Test Results:**
- All 33 tests pass (100%)
- No WebGPU validation errors
- Demo runs cleanly with `--seek 2.0`
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/gpu/effects')
| -rw-r--r-- | src/gpu/effects/circle_mask_effect.cc | 33 | ||||
| -rw-r--r-- | src/gpu/effects/rotating_cube_effect.cc | 8 | ||||
| -rw-r--r-- | src/gpu/effects/rotating_cube_effect.h | 4 |
3 files changed, 29 insertions, 16 deletions
diff --git a/src/gpu/effects/circle_mask_effect.cc b/src/gpu/effects/circle_mask_effect.cc index 55bcb90..226b603 100644 --- a/src/gpu/effects/circle_mask_effect.cc +++ b/src/gpu/effects/circle_mask_effect.cc @@ -51,7 +51,7 @@ void CircleMaskEffect::init(MainSequence* demo) { WGPUShaderModule compute_module = wgpuDeviceCreateShaderModule(ctx_.device, &compute_desc); const WGPUColorTargetState compute_target = { - .format = WGPUTextureFormat_RGBA8Unorm, + .format = ctx_.format, // Match auxiliary texture format .writeMask = WGPUColorWriteMask_All, }; WGPUFragmentState compute_frag = {}; @@ -60,6 +60,7 @@ void CircleMaskEffect::init(MainSequence* demo) { compute_frag.targetCount = 1; compute_frag.targets = &compute_target; WGPURenderPipelineDescriptor compute_pipeline_desc = {}; + compute_pipeline_desc.label = label_view("CircleMaskEffect_compute"); compute_pipeline_desc.vertex.module = compute_module; compute_pipeline_desc.vertex.entryPoint = str_view("vs_main"); compute_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; @@ -98,11 +99,19 @@ void CircleMaskEffect::init(MainSequence* demo) { render_frag.entryPoint = str_view("fs_main"); render_frag.targetCount = 1; render_frag.targets = &render_target; + const WGPUDepthStencilState depth_stencil = { + .format = WGPUTextureFormat_Depth24Plus, + .depthWriteEnabled = WGPUOptionalBool_False, // Don't write depth + .depthCompare = WGPUCompareFunction_Always, // Always pass + }; + WGPURenderPipelineDescriptor render_pipeline_desc = {}; + render_pipeline_desc.label = label_view("CircleMaskEffect_render"); render_pipeline_desc.vertex.module = render_module; render_pipeline_desc.vertex.entryPoint = str_view("vs_main"); render_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; render_pipeline_desc.primitive.cullMode = WGPUCullMode_None; + render_pipeline_desc.depthStencil = &depth_stencil; render_pipeline_desc.multisample.count = 1; render_pipeline_desc.multisample.mask = 0xFFFFFFFF; render_pipeline_desc.fragment = &render_frag; @@ -139,16 +148,18 @@ void CircleMaskEffect::compute(WGPUCommandEncoder encoder, float time, compute_uniforms_.update(ctx_.queue, uniforms); WGPUTextureView mask_view = demo_->get_auxiliary_view("circle_mask"); - const WGPURenderPassColorAttachment color_attachment = { - .view = mask_view, - .loadOp = WGPULoadOp_Clear, - .storeOp = WGPUStoreOp_Store, - .clearValue = {0.0, 0.0, 0.0, 1.0}, - }; - const WGPURenderPassDescriptor pass_desc = { - .colorAttachmentCount = 1, - .colorAttachments = &color_attachment, - }; + WGPURenderPassColorAttachment color_attachment = {}; + color_attachment.view = mask_view; + color_attachment.loadOp = WGPULoadOp_Clear; + color_attachment.storeOp = WGPUStoreOp_Store; + color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; +#if !defined(DEMO_CROSS_COMPILE_WIN32) + color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; +#endif + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); wgpuRenderPassEncoderSetPipeline(pass, compute_pipeline_); diff --git a/src/gpu/effects/rotating_cube_effect.cc b/src/gpu/effects/rotating_cube_effect.cc index b4f3d3e..7f590c5 100644 --- a/src/gpu/effects/rotating_cube_effect.cc +++ b/src/gpu/effects/rotating_cube_effect.cc @@ -63,12 +63,12 @@ void RotatingCubeEffect::init(MainSequence* demo) { ShaderComposer::CompositionMap composition_map; composition_map["render/scene_query_mode"] = "render/scene_query_linear"; - std::string composed_shader = ShaderComposer::Get().Compose( + composed_shader_ = ShaderComposer::Get().Compose( {}, std::string(shader_code, shader_size), composition_map); WGPUShaderSourceWGSL wgsl_src = {}; wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(composed_shader.c_str()); + wgsl_src.code = str_view(composed_shader_.c_str()); WGPUShaderModuleDescriptor shader_desc = {}; shader_desc.nextInChain = &wgsl_src.chain; @@ -105,18 +105,16 @@ void RotatingCubeEffect::init(MainSequence* demo) { pipeline_ = wgpuDeviceCreateRenderPipeline(ctx_.device, &pipeline_desc); wgpuShaderModuleRelease(shader_module); - WGPUTextureView dummy_sky = noise_view_; const WGPUBindGroupEntry entries_0[] = { {.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_}, - {.binding = 5, .textureView = dummy_sky}, }; const WGPUBindGroupDescriptor bg_desc_0 = { .layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0), - .entryCount = 5, + .entryCount = 4, .entries = entries_0, }; bind_group_0_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc_0); diff --git a/src/gpu/effects/rotating_cube_effect.h b/src/gpu/effects/rotating_cube_effect.h index 1ce81b7..89b3fa6 100644 --- a/src/gpu/effects/rotating_cube_effect.h +++ b/src/gpu/effects/rotating_cube_effect.h @@ -8,6 +8,7 @@ #include "gpu/effect.h" #include "gpu/gpu.h" #include "util/mini_math.h" +#include <string> class RotatingCubeEffect : public Effect { public: @@ -46,6 +47,9 @@ class RotatingCubeEffect : public Effect { WGPUSampler noise_sampler_ = nullptr; WGPUSampler mask_sampler_ = nullptr; float rotation_ = 0.0f; + + // Store composed shader to keep it alive for WebGPU + std::string composed_shader_; }; #endif /* ROTATING_CUBE_EFFECT_H_ */ |
