From 6944733a6a2f05c18e7e0b73f847a4c9144801fd Mon Sep 17 00:00:00 2001 From: skal Date: Tue, 10 Feb 2026 12:48:43 +0100 Subject: feat: Add multi-layer CNN support with framebuffer capture and blend control Implements automatic layer chaining and generic framebuffer capture API for multi-layer neural network effects with proper original input preservation. Key changes: - Effect::needs_framebuffer_capture() - generic API for pre-render capture - MainSequence: auto-capture to "captured_frame" auxiliary texture - CNNEffect: multi-layer support via layer_index/total_layers params - seq_compiler: expands "layers=N" to N chained effect instances - Shader: @binding(4) original_input available to all layers - Training: generates layer switches and original input binding - Blend: mix(original, result, blend_amount) uses layer 0 input Timeline syntax: CNNEffect layers=3 blend=0.7 Co-Authored-By: Claude Sonnet 4.5 --- src/gpu/effect.cc | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'src/gpu/effect.cc') diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc index 6a4762c..0662f26 100644 --- a/src/gpu/effect.cc +++ b/src/gpu/effect.cc @@ -65,7 +65,7 @@ void Sequence::update_active_list(float seq_time) { #if !defined(STRIP_ALL) Effect* effect_ptr = item.effect.get(); const char* effect_name = typeid(*effect_ptr).name(); - printf(" [EFFECT START] %s (priority=%d, time=%.2f-%.2f)\n", effect_name, + printf(" [EFFECT START] <%s> (priority=%d, time=%.2f-%.2f)\n", effect_name, item.priority, item.start_time, item.end_time); #endif item.effect->start(); @@ -74,7 +74,7 @@ void Sequence::update_active_list(float seq_time) { #if !defined(STRIP_ALL) Effect* effect_ptr = item.effect.get(); const char* effect_name = typeid(*effect_ptr).name(); - printf(" [EFFECT END] %s (priority=%d)\n", effect_name, item.priority); + printf(" [EFFECT END] <%s> (priority=%d)\n", effect_name, item.priority); #endif item.effect->end(); item.active = false; @@ -339,6 +339,39 @@ 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_Load; + capture_attachment.storeOp = WGPUStoreOp_Store; +#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 current_input to captured_frame + PostProcessEffect* passthrough = + (PostProcessEffect*)passthrough_effect_.get(); + passthrough->update_bind_group(current_input); + passthrough->render(capture_pass, 0, 0, 0, aspect_ratio); + + wgpuRenderPassEncoderEnd(capture_pass); + } + } + pp->update_bind_group(current_input); WGPURenderPassColorAttachment pp_attachment = {}; -- cgit v1.2.3