diff options
| author | skal <pascal.massimino@gmail.com> | 2026-03-23 07:31:14 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-03-23 07:31:14 +0100 |
| commit | 1e3813355e37f903314ec2069ff788c6f69becfd (patch) | |
| tree | cb9ace6456a40eabb4ab0478f912c1adadffb6d1 /cnn_v3/src/gbuffer_effect.cc | |
| parent | 13cf1438caa56b34529d4031ddf73d38286b70e5 (diff) | |
feat(cnn_v3): GBufferEffect temporal feedback via post_render()
- Add Effect::post_render() virtual hook, called after all effects in
the sequence have rendered each frame. Default is no-op.
- Sequence::render_effects() runs a second pass invoking post_render()
on all DAG nodes after the render pass completes.
- GBufferEffect: declare internal node_prev_tex_ (U8X4_NORM) for
persistent prev-frame CNN output. post_render() copies cnn_output_node_
→ node_prev_tex_ via CopyTextureToTexture. render() binds node_prev_tex_
as prev_cnn (binding 6) — zero on frame 0 (matches training convention).
- Expose set_cnn_output_node(name) API; call once at setup.
- Drop brittle ping-pong / input_nodes_[0] fallback.
- Update doc/SEQUENCE.md: post_render() semantics, frame execution order,
temporal feedback canonical pattern, node types table with G-buffer types.
- Update cnn_v3/docs/HOWTO.md: temporal feedback wiring section.
36/36 tests passing.
handoff(Gemini): prev.rgb temporal feedback now correct and generic.
Set set_cnn_output_node("sink") (or CNN output node name) once at setup.
Diffstat (limited to 'cnn_v3/src/gbuffer_effect.cc')
| -rw-r--r-- | cnn_v3/src/gbuffer_effect.cc | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/cnn_v3/src/gbuffer_effect.cc b/cnn_v3/src/gbuffer_effect.cc index 25fef4c..512843c 100644 --- a/cnn_v3/src/gbuffer_effect.cc +++ b/cnn_v3/src/gbuffer_effect.cc @@ -61,6 +61,7 @@ GBufferEffect::GBufferEffect(const GpuContext& ctx, node_depth_ = prefix + "_depth"; node_shadow_ = prefix + "_shadow"; node_transp_ = prefix + "_transp"; + node_prev_tex_ = prefix + "_prev"; // Allocate GPU buffers for scene data. global_uniforms_buf_ = gpu_create_buffer(ctx_.device, sizeof(GBufGlobalUniforms), @@ -95,6 +96,7 @@ void GBufferEffect::declare_nodes(NodeRegistry& registry) { if (!registry.has_node(output_nodes_[1])) { registry.declare_node(output_nodes_[1], NodeType::GBUF_RGBA32UINT, -1, -1); } + registry.declare_node(node_prev_tex_, NodeType::U8X4_NORM, -1, -1); } void GBufferEffect::set_scene() { @@ -230,14 +232,9 @@ void GBufferEffect::render(WGPUCommandEncoder encoder, WGPUTextureView feat0_view = nodes.get_view(output_nodes_[0]); WGPUTextureView feat1_view = nodes.get_view(output_nodes_[1]); - // prev_cnn: first input node if available, else dummy. - WGPUTextureView prev_view = nullptr; - if (!input_nodes_.empty()) { - prev_view = nodes.get_view(input_nodes_[0]); - } - if (!prev_view) { - prev_view = dummy_texture_view_.get(); - } + // node_prev_tex_ is updated by post_render() at the end of each frame. + // On frame 0 it is zero (NodeRegistry zeroes new textures) — correct default. + WGPUTextureView prev_view = nodes.get_view(node_prev_tex_); // --- Pass 1: MRT rasterization --- update_raster_bind_group(nodes); @@ -776,3 +773,15 @@ void GBufferEffect::update_raster_bind_group(NodeRegistry& nodes) { wgpuBindGroupLayoutRelease(bgl); } +void GBufferEffect::post_render(WGPUCommandEncoder encoder, NodeRegistry& nodes) { + if (cnn_output_node_.empty() || !nodes.has_node(cnn_output_node_)) return; + WGPUTexelCopyTextureInfo src = {}; + src.texture = nodes.get_texture(cnn_output_node_); + src.mipLevel = 0; + WGPUTexelCopyTextureInfo dst = {}; + dst.texture = nodes.get_texture(node_prev_tex_); + dst.mipLevel = 0; + WGPUExtent3D extent = {(uint32_t)width_, (uint32_t)height_, 1}; + wgpuCommandEncoderCopyTextureToTexture(encoder, &src, &dst, &extent); +} + |
