diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-10 15:58:46 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-10 15:58:46 +0100 |
| commit | 139059e1fdbcace3e233c6109a61446b14b774e4 (patch) | |
| tree | a0f73e916fb93819020c254234be9b05eb1ee1c8 /src/gpu | |
| parent | ebb1a07857fe25fdaa66b2f86303bc8fbd621cfe (diff) | |
fix: Resolve CNN effect black screen bug (framebuffer capture + uniforms)
Two bugs causing black screen when CNN post-processing activated:
1. Framebuffer capture timing: Capture ran inside post-effect loop after
ping-pong swaps, causing layers 1+ to capture wrong buffer. Moved
capture before loop to copy framebuffer_a once before post-chain starts.
2. Missing uniforms update: CNNEffect never updated uniforms_ buffer,
leaving uniforms.resolution uninitialized (0,0). UV calculation
p.xy/uniforms.resolution produced NaN, causing all texture samples
to return black. Added uniforms update in update_bind_group().
Files modified:
- src/gpu/effect.cc: Capture before post-chain (lines 308-346)
- src/gpu/effects/cnn_effect.cc: Add uniforms update (lines 132-142)
- workspaces/main/shaders/cnn/cnn_layer.wgsl: Remove obsolete comment
- doc/CNN_DEBUG.md: Historical debugging doc
- CLAUDE.md: Reference CNN_DEBUG.md in historical section
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/gpu')
| -rw-r--r-- | src/gpu/effect.cc | 72 | ||||
| -rw-r--r-- | src/gpu/effects/cnn_effect.cc | 15 |
2 files changed, 53 insertions, 34 deletions
diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc index b50acce..fba3353 100644 --- a/src/gpu/effect.cc +++ b/src/gpu/effect.cc @@ -293,6 +293,45 @@ void MainSequence::render_frame(float global_time, float beat, float peak, wgpuRenderPassEncoderEnd(scene_pass); // 3. Post Chain + + // Capture framebuffer ONCE before post-processing chain + bool needs_capture = false; + for (const SequenceItem* item : post_effects) { + PostProcessEffect* pp = (PostProcessEffect*)(item->effect.get()); + if (pp->needs_framebuffer_capture()) { + needs_capture = true; + break; + } + } + + if (needs_capture) { + WGPUTextureView captured_view = get_auxiliary_view("captured_frame"); + if (captured_view) { + WGPURenderPassColorAttachment capture_attachment = {}; + capture_attachment.view = captured_view; + capture_attachment.resolveTarget = nullptr; + capture_attachment.loadOp = WGPULoadOp_Clear; + capture_attachment.storeOp = WGPUStoreOp_Store; + capture_attachment.clearValue = {0.0f, 0.0f, 0.0f, 1.0f}; +#if !defined(DEMO_CROSS_COMPILE_WIN32) + capture_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; +#endif + WGPURenderPassDescriptor capture_desc = { + .colorAttachmentCount = 1, .colorAttachments = &capture_attachment}; + WGPURenderPassEncoder capture_pass = + wgpuCommandEncoderBeginRenderPass(encoder, &capture_desc); + wgpuRenderPassEncoderSetViewport(capture_pass, 0.0f, 0.0f, + (float)width_, (float)height_, 0.0f, 1.0f); + + PostProcessEffect* passthrough = + (PostProcessEffect*)passthrough_effect_.get(); + passthrough->update_bind_group(framebuffer_view_a_); + passthrough->render(capture_pass, 0, 0, 0, aspect_ratio); + + wgpuRenderPassEncoderEnd(capture_pass); + } + } + WGPUSurfaceTexture st = {}; WGPUTextureView final_view = nullptr; @@ -340,39 +379,6 @@ void MainSequence::render_frame(float global_time, float beat, float peak, PostProcessEffect* pp = (PostProcessEffect*)(post_effects[i]->effect.get()); - // Capture framebuffer if effect needs it - if (pp->needs_framebuffer_capture()) { - WGPUTextureView captured_view = get_auxiliary_view("captured_frame"); - if (captured_view) { - // Get source texture from current_input view - // Note: This is a simplified blit using a render pass - WGPURenderPassColorAttachment capture_attachment = {}; - capture_attachment.view = captured_view; - capture_attachment.resolveTarget = nullptr; - capture_attachment.loadOp = WGPULoadOp_Clear; - capture_attachment.storeOp = WGPUStoreOp_Store; - capture_attachment.clearValue = {0.0f, 0.0f, 0.0f, 1.0f}; -#if !defined(DEMO_CROSS_COMPILE_WIN32) - capture_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; -#endif - WGPURenderPassDescriptor capture_desc = { - .colorAttachmentCount = 1, .colorAttachments = &capture_attachment}; - WGPURenderPassEncoder capture_pass = - wgpuCommandEncoderBeginRenderPass(encoder, &capture_desc); - wgpuRenderPassEncoderSetViewport(capture_pass, 0.0f, 0.0f, - (float)width_, (float)height_, 0.0f, - 1.0f); - - // Use passthrough effect to copy framebuffer_a (scene) to captured_frame - PostProcessEffect* passthrough = - (PostProcessEffect*)passthrough_effect_.get(); - passthrough->update_bind_group(framebuffer_view_a_); - passthrough->render(capture_pass, 0, 0, 0, aspect_ratio); - - wgpuRenderPassEncoderEnd(capture_pass); - } - } - pp->update_bind_group(current_input); WGPURenderPassColorAttachment pp_attachment = {}; diff --git a/src/gpu/effects/cnn_effect.cc b/src/gpu/effects/cnn_effect.cc index cb00455..7107bea 100644 --- a/src/gpu/effects/cnn_effect.cc +++ b/src/gpu/effects/cnn_effect.cc @@ -104,7 +104,10 @@ void CNNEffect::init(MainSequence* demo) { void CNNEffect::render(WGPURenderPassEncoder pass, float time, float beat, float intensity, float aspect_ratio) { - if (!bind_group_) return; + if (!bind_group_) { + fprintf(stderr, "CNN render: no bind_group\n"); + return; + } wgpuRenderPassEncoderSetPipeline(pass, pipeline_); wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); @@ -114,6 +117,16 @@ void CNNEffect::render(WGPURenderPassEncoder pass, float time, float beat, void CNNEffect::update_bind_group(WGPUTextureView input_view) { input_view_ = input_view; + // Update common uniforms (CRITICAL for UV calculation!) + const CommonPostProcessUniforms u = { + .resolution = {(float)width_, (float)height_}, + .aspect_ratio = (float)width_ / (float)height_, + .time = 0.0f, + .beat = 0.0f, + .audio_intensity = 0.0f, + }; + uniforms_.update(ctx_.queue, u); + // All layers: get captured frame (original input from layer 0) if (demo_) { original_view_ = demo_->get_auxiliary_view("captured_frame"); |
