diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-16 23:33:53 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-16 23:33:53 +0100 |
| commit | 3b2bee705cfe5a250bb6049a90b5d734d3125f73 (patch) | |
| tree | f6c7151b6b9c6eecdf7a3a750c4c57e5c528cc0e | |
| parent | 0585a596e2fc77239912bd6437ea76b1af5ad0d5 (diff) | |
fix: port Hybrid3D effect to sequence v2 architecture
Hybrid3D was calling Renderer3D::render() which creates its own command
encoder, bypassing the sequence system. Now uses renderer_.draw() with
the passed encoder.
Also adds texture blit support for RotatingCube compositing.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
| -rw-r--r-- | doc/BACKLOG.md | 24 | ||||
| -rw-r--r-- | src/effects/hybrid3_d_effect.cc | 28 | ||||
| -rw-r--r-- | src/effects/rotating_cube_effect.cc | 20 | ||||
| -rw-r--r-- | src/gpu/sequence.cc | 16 | ||||
| -rw-r--r-- | src/gpu/sequence.h | 3 | ||||
| -rw-r--r-- | workspaces/test/assets.txt | 1 | ||||
| -rw-r--r-- | workspaces/test/timeline.seq | 8 |
7 files changed, 89 insertions, 11 deletions
diff --git a/doc/BACKLOG.md b/doc/BACKLOG.md index 403ecc9..68995a7 100644 --- a/doc/BACKLOG.md +++ b/doc/BACKLOG.md @@ -150,6 +150,30 @@ Extend uniform parameter system to remaining effects. **Estimated Impact**: ~200-300 bytes per effect +### TODO MAYBE: Timeline Syntax for Buffer Operations +Explicit timeline control for buffer clear/blit operations. + +**Current**: Effects decide behavior (e.g., RotatingCube checks if input is "source") + +**Proposed**: Timeline syntax for buffer operations +- `[NONE]` - Shader samples input (post-process effects) +- `[CLEAR r g b a]` - Clear to color before render (3D standalone) +- `[BLIT]` - Copy input to output before render (3D composite) + +**Example**: +``` +EFFECT + RotatingCube source -> temp1 [CLEAR 0 0 0 0] 0.0 4.0 +EFFECT + RotatingCube temp1 -> temp2 [BLIT] 2.0 16.0 +``` + +**Trade-offs**: +- Pro: Explicit, instance-specific control, self-documenting +- Con: seq_compiler complexity, size cost, syntax bloat + +**Decision**: Effect-decides approach works for now. Only add if multiple 3D effects need per-instance control. + +**Priority**: Very Low (revisit if pattern becomes common) + ### Task #52: Procedural SDF Font Minimal bezier/spline set for [A-Z, 0-9] and SDF rendering. diff --git a/src/effects/hybrid3_d_effect.cc b/src/effects/hybrid3_d_effect.cc index 0e44853..c13c1e9 100644 --- a/src/effects/hybrid3_d_effect.cc +++ b/src/effects/hybrid3_d_effect.cc @@ -4,6 +4,7 @@ #include "util/fatal_error.h" #include "effects/hybrid3_d_effect.h" +#include "gpu/gpu.h" #include <cmath> Hybrid3D::Hybrid3D(const GpuContext& ctx, @@ -113,6 +114,29 @@ void Hybrid3D::render(WGPUCommandEncoder encoder, WGPUTextureView color_view = nodes.get_view(output_nodes_[0]); WGPUTextureView depth_view = nodes.get_view(depth_node_); - // Render 3D scene - renderer_.render(scene_, camera_, params.time, color_view, depth_view); + // Render 3D scene using sequence encoder + WGPURenderPassColorAttachment color_attachment = {}; +#if !defined(DEMO_CROSS_COMPILE_WIN32) + color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; +#endif + color_attachment.view = color_view; + color_attachment.loadOp = WGPULoadOp_Clear; + color_attachment.storeOp = WGPUStoreOp_Store; + color_attachment.clearValue = {0.05f, 0.05f, 0.05f, 1.0f}; + + WGPURenderPassDepthStencilAttachment depth_attachment = {}; + depth_attachment.view = depth_view; + depth_attachment.depthLoadOp = WGPULoadOp_Clear; + depth_attachment.depthStoreOp = WGPUStoreOp_Store; + depth_attachment.depthClearValue = 1.0f; + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; + pass_desc.depthStencilAttachment = &depth_attachment; + + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + renderer_.draw(pass, scene_, camera_, params.time); + wgpuRenderPassEncoderEnd(pass); + wgpuRenderPassEncoderRelease(pass); } diff --git a/src/effects/rotating_cube_effect.cc b/src/effects/rotating_cube_effect.cc index 1f56b8b..a91bc78 100644 --- a/src/effects/rotating_cube_effect.cc +++ b/src/effects/rotating_cube_effect.cc @@ -156,6 +156,21 @@ void RotatingCube::render(WGPUCommandEncoder encoder, wgpuQueueWriteBuffer(ctx_.queue, object_buffer_.buffer, 0, &obj_data, sizeof(ObjectData)); + // Blit input to output if compositing (not reading from source) + if (!input_nodes_.empty() && input_nodes_[0] != "source") { + WGPUTexture input_tex = nodes.get_texture(input_nodes_[0]); + WGPUTexture output_tex = nodes.get_texture(output_nodes_[0]); + if (input_tex && output_tex) { + WGPUTexelCopyTextureInfo src = { + .texture = input_tex, .mipLevel = 0, .aspect = WGPUTextureAspect_All}; + WGPUTexelCopyTextureInfo dst = { + .texture = output_tex, .mipLevel = 0, .aspect = WGPUTextureAspect_All}; + WGPUExtent3D copy_size = {(uint32_t)params.resolution.x, + (uint32_t)params.resolution.y, 1}; + wgpuCommandEncoderCopyTextureToTexture(encoder, &src, &dst, ©_size); + } + } + // Get output views WGPUTextureView color_view = nodes.get_view(output_nodes_[0]); WGPUTextureView depth_view = nodes.get_view(depth_node_); @@ -164,9 +179,10 @@ void RotatingCube::render(WGPUCommandEncoder encoder, WGPURenderPassColorAttachment color_attachment = { .view = color_view, .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, - .loadOp = WGPULoadOp_Clear, +// .loadOp = WGPULoadOp_Clear, + .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store, - .clearValue = {0.0, 0.0, 0.0, 1.0}}; + .clearValue = {0.0, 0.0, 0.0, 0.0}}; WGPURenderPassDepthStencilAttachment depth_attachment = { .view = depth_view, diff --git a/src/gpu/sequence.cc b/src/gpu/sequence.cc index 62c2933..0a0cb1e 100644 --- a/src/gpu/sequence.cc +++ b/src/gpu/sequence.cc @@ -92,6 +92,16 @@ NodeRegistry::get_output_views(const std::vector<std::string>& names) { return views; } +WGPUTexture NodeRegistry::get_texture(const std::string& name) { + auto alias_it = aliases_.find(name); + if (alias_it != aliases_.end()) { + return get_texture(alias_it->second); + } + auto it = nodes_.find(name); + FATAL_CHECK(it != nodes_.end(), "Node not found: %s\n", name.c_str()); + return it->second.texture; +} + void NodeRegistry::resize(int width, int height) { default_width_ = width; default_height_ = height; @@ -141,15 +151,15 @@ void NodeRegistry::create_texture(Node& node) { switch (node.type) { case NodeType::U8X4_NORM: format = WGPUTextureFormat_RGBA8Unorm; - usage = (WGPUTextureUsage)(WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding); + usage = (WGPUTextureUsage)(WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst); break; case NodeType::F32X4: format = WGPUTextureFormat_RGBA32Float; - usage = (WGPUTextureUsage)(WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding); + usage = (WGPUTextureUsage)(WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst); break; case NodeType::F16X8: format = WGPUTextureFormat_RGBA16Float; // WebGPU doesn't have 8-channel, use RGBA16 - usage = (WGPUTextureUsage)(WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding); + usage = (WGPUTextureUsage)(WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst); break; case NodeType::DEPTH24: format = WGPUTextureFormat_Depth24Plus; diff --git a/src/gpu/sequence.h b/src/gpu/sequence.h index a33dedb..e96e183 100644 --- a/src/gpu/sequence.h +++ b/src/gpu/sequence.h @@ -62,6 +62,9 @@ class NodeRegistry { std::vector<WGPUTextureView> get_output_views(const std::vector<std::string>& names); + // Retrieve texture (for blits) + WGPUTexture get_texture(const std::string& name); + // Resize all nodes void resize(int width, int height); diff --git a/workspaces/test/assets.txt b/workspaces/test/assets.txt index 68d0ab3..79f5e43 100644 --- a/workspaces/test/assets.txt +++ b/workspaces/test/assets.txt @@ -74,6 +74,7 @@ SHADER_POSTPROCESS_INLINE, NONE, ../../common/shaders/postprocess_inline.wgsl, " SHADER_PASSTHROUGH_V2, NONE, ../../common/shaders/passthrough.wgsl, "Passthrough Shader" SHADER_GAUSSIAN_BLUR_V2, NONE, ../../common/shaders/gaussian_blur.wgsl, "Gaussian Blur Shader" SHADER_HEPTAGON_V2, NONE, ../../common/shaders/heptagon.wgsl, "Heptagon Shader" +SHADER_ROTATING_CUBE_V2, NONE, ../main/shaders/rotating_cube.wgsl, "Rotating Cube Shader" SHADER_FLASH, NONE, shaders/flash.wgsl, "Flash Shader" # --- Test Assets (for test_assets.cc) --- diff --git a/workspaces/test/timeline.seq b/workspaces/test/timeline.seq index 98000f0..9f698fe 100644 --- a/workspaces/test/timeline.seq +++ b/workspaces/test/timeline.seq @@ -3,10 +3,10 @@ # BPM 120 (set in test_demo.track) SEQUENCE 0.0 0 "MainLoop" -EFFECT + RotatingCube source -> temp1 0.00 4.00 +EFFECT + Hybrid3D source -> temp1 0.00 4.00 EFFECT + GaussianBlur temp1 -> sink 0.00 4.00 SEQUENCE 4.0 0 "MainLoop" -EFFECT + Flash source -> temp1 0.0 16.0 -EFFECT + GaussianBlur temp1 -> temp2 2.0 16.0 -EFFECT + PeakMeter temp2 -> sink 0.0 16.0 +EFFECT + Heptagon source -> temp1 0.0 16.0 +#EFFECT + RotatingCube temp1 -> temp2 2.0 16.0 +EFFECT + PeakMeter temp1 -> sink 4.0 16.0 |
