diff options
| author | skal <pascal.massimino@gmail.com> | 2026-03-22 19:58:04 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-03-22 19:58:04 +0100 |
| commit | 9bf9b0aa0573f77bd667e6976a8bb413153daa1d (patch) | |
| tree | c204a3ccf604ebb3cc28cda1b35f2a6aecc9b27e | |
| parent | 053f455132003267fbe6c4822124f9e447a646a6 (diff) | |
feat(cnn_v3): GBufDeferredEffect — simple deferred render (albedo * shadow)
New effect unpacks feat_tex0/feat_tex1 and outputs albedo * shadow.
Replaces CNNv3Effect in cnn_v3_test sequence until training is complete.
37/37 tests passing.
handoff(Gemini): GBufDeferredEffect wired in timeline; CNN v3 pipeline: GBufferEffect → GBufDeferredEffect → sink.
| -rw-r--r-- | cmake/DemoSourceLists.cmake | 1 | ||||
| -rw-r--r-- | cnn_v3/shaders/gbuf_deferred.wgsl | 39 | ||||
| -rw-r--r-- | cnn_v3/src/gbuf_deferred_effect.cc | 138 | ||||
| -rw-r--r-- | cnn_v3/src/gbuf_deferred_effect.h | 23 | ||||
| -rw-r--r-- | src/effects/shaders.cc | 1 | ||||
| -rw-r--r-- | src/effects/shaders.h | 1 | ||||
| -rw-r--r-- | src/gpu/demo_effects.h | 1 | ||||
| -rw-r--r-- | src/tests/gpu/test_demo_effects.cc | 5 | ||||
| -rwxr-xr-x | tools/seq_compiler.py | 3 | ||||
| -rw-r--r-- | workspaces/main/assets.txt | 1 | ||||
| -rw-r--r-- | workspaces/main/timeline.seq | 4 |
11 files changed, 213 insertions, 4 deletions
diff --git a/cmake/DemoSourceLists.cmake b/cmake/DemoSourceLists.cmake index e5c7339..52278ee 100644 --- a/cmake/DemoSourceLists.cmake +++ b/cmake/DemoSourceLists.cmake @@ -43,6 +43,7 @@ set(COMMON_GPU_EFFECTS cnn_v3/src/gbuffer_effect.cc cnn_v3/src/cnn_v3_effect.cc cnn_v3/src/gbuf_view_effect.cc + cnn_v3/src/gbuf_deferred_effect.cc # TODO: Port CNN effects to v2 (complex v1 dependencies) # cnn_v1/src/cnn_v1_effect.cc # cnn_v2/src/cnn_v2_effect.cc diff --git a/cnn_v3/shaders/gbuf_deferred.wgsl b/cnn_v3/shaders/gbuf_deferred.wgsl new file mode 100644 index 0000000..959ab60 --- /dev/null +++ b/cnn_v3/shaders/gbuf_deferred.wgsl @@ -0,0 +1,39 @@ +// G-buffer deferred render — albedo * shadow +// Reads feat_tex0 (rgba32uint, f16 geometric) and feat_tex1 (rgba32uint, u8 context). +// Unpacks albedo and shadow and outputs albedo * shadow. + +@group(0) @binding(0) var feat_tex0: texture_2d<u32>; +@group(0) @binding(1) var feat_tex1: texture_2d<u32>; +@group(0) @binding(2) var<uniform> uniforms: GBufDeferredUniforms; + +struct GBufDeferredUniforms { + resolution: vec2f, +} + +@vertex +fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4f { + let x = f32((vid & 1u) << 2u) - 1.0; + let y = f32((vid & 2u) << 1u) - 1.0; + return vec4f(x, y, 0.0, 1.0); +} + +@fragment +fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f { + let coord = vec2i(pos.xy); + + // feat_tex0 layout: + // [0] pack2x16float(albedo.r, albedo.g) + // [1] pack2x16float(albedo.b, normal.x) + let t0 = textureLoad(feat_tex0, coord, 0); + let rg = unpack2x16float(t0.x); + let bx = unpack2x16float(t0.y); + let albedo = vec3f(rg.x, rg.y, bx.x); + + // feat_tex1 layout: + // [2] pack4x8unorm(mip2.g, mip2.b, shadow, transp) + let t1 = textureLoad(feat_tex1, coord, 0); + let ctx2 = unpack4x8unorm(t1.z); + let shadow = ctx2.z; // shadow is the third byte + + return vec4f(albedo * shadow, 1.0); +} diff --git a/cnn_v3/src/gbuf_deferred_effect.cc b/cnn_v3/src/gbuf_deferred_effect.cc new file mode 100644 index 0000000..8967fe5 --- /dev/null +++ b/cnn_v3/src/gbuf_deferred_effect.cc @@ -0,0 +1,138 @@ +// GBufDeferredEffect — simple deferred render: albedo * shadow from packed G-buffer. + +#include "gbuf_deferred_effect.h" +#include "gpu/gpu.h" +#include "util/fatal_error.h" + +extern const char* gbuf_deferred_wgsl; + +struct GBufDeferredUniforms { + float resolution[2]; +}; +static_assert(sizeof(GBufDeferredUniforms) == 8, "GBufDeferredUniforms must be 8 bytes"); + +static WGPUBindGroupLayoutEntry bgl_uint_tex(uint32_t binding) { + WGPUBindGroupLayoutEntry e = {}; + e.binding = binding; + e.visibility = WGPUShaderStage_Fragment; + e.texture.sampleType = WGPUTextureSampleType_Uint; + e.texture.viewDimension = WGPUTextureViewDimension_2D; + return e; +} + +static WGPUBindGroupLayoutEntry bgl_uniform(uint32_t binding, uint64_t min_size) { + WGPUBindGroupLayoutEntry e = {}; + e.binding = binding; + e.visibility = WGPUShaderStage_Fragment; + e.buffer.type = WGPUBufferBindingType_Uniform; + e.buffer.minBindingSize = min_size; + return e; +} + +GBufDeferredEffect::GBufDeferredEffect(const GpuContext& ctx, + const std::vector<std::string>& inputs, + const std::vector<std::string>& outputs, + float start_time, float end_time) + : Effect(ctx, inputs, outputs, start_time, end_time) { + HEADLESS_RETURN_IF_NULL(ctx_.device); + + WGPUBindGroupLayoutEntry entries[3] = { + bgl_uint_tex(0), + bgl_uint_tex(1), + bgl_uniform(2, sizeof(GBufDeferredUniforms)), + }; + WGPUBindGroupLayoutDescriptor bgl_desc = {}; + bgl_desc.entryCount = 3; + bgl_desc.entries = entries; + WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(ctx_.device, &bgl_desc); + + WGPUPipelineLayoutDescriptor pl_desc = {}; + pl_desc.bindGroupLayoutCount = 1; + pl_desc.bindGroupLayouts = &bgl; + WGPUPipelineLayout pl = wgpuDeviceCreatePipelineLayout(ctx_.device, &pl_desc); + + WGPUShaderSourceWGSL wgsl_src = {}; + wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; + wgsl_src.code = str_view(gbuf_deferred_wgsl); + WGPUShaderModuleDescriptor shader_desc = {}; + shader_desc.nextInChain = &wgsl_src.chain; + WGPUShaderModule shader = wgpuDeviceCreateShaderModule(ctx_.device, &shader_desc); + + WGPUColorTargetState target = {}; + target.format = WGPUTextureFormat_RGBA8Unorm; + target.writeMask = WGPUColorWriteMask_All; + + WGPUFragmentState frag = {}; + frag.module = shader; + frag.entryPoint = str_view("fs_main"); + frag.targetCount = 1; + frag.targets = ⌖ + + WGPURenderPipelineDescriptor pipe_desc = {}; + pipe_desc.layout = pl; + pipe_desc.vertex.module = shader; + pipe_desc.vertex.entryPoint = str_view("vs_main"); + pipe_desc.fragment = &frag; + pipe_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + pipe_desc.multisample.count = 1; + pipe_desc.multisample.mask = UINT32_MAX; + + pipeline_.set(wgpuDeviceCreateRenderPipeline(ctx_.device, &pipe_desc)); + + wgpuShaderModuleRelease(shader); + wgpuPipelineLayoutRelease(pl); + wgpuBindGroupLayoutRelease(bgl); +} + +void GBufDeferredEffect::render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) { + WGPUTextureView feat0_view = nodes.get_view(input_nodes_[0]); + WGPUTextureView feat1_view = nodes.get_view(input_nodes_[1]); + WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); + + // Upload resolution uniform into the base class uniforms buffer (first 8 bytes). + GBufDeferredUniforms u; + u.resolution[0] = params.resolution.x; + u.resolution[1] = params.resolution.y; + wgpuQueueWriteBuffer(ctx_.queue, uniforms_buffer_.get().buffer, 0, + &u, sizeof(u)); + + WGPUBindGroupLayout bgl = + wgpuRenderPipelineGetBindGroupLayout(pipeline_.get(), 0); + + WGPUBindGroupEntry bg_entries[3] = {}; + bg_entries[0].binding = 0; + bg_entries[0].textureView = feat0_view; + bg_entries[1].binding = 1; + bg_entries[1].textureView = feat1_view; + bg_entries[2].binding = 2; + bg_entries[2].buffer = uniforms_buffer_.get().buffer; + bg_entries[2].size = sizeof(GBufDeferredUniforms); + + WGPUBindGroupDescriptor bg_desc = {}; + bg_desc.layout = bgl; + bg_desc.entryCount = 3; + bg_desc.entries = bg_entries; + bind_group_.replace(wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc)); + wgpuBindGroupLayoutRelease(bgl); + + WGPURenderPassColorAttachment color_att = {}; + color_att.view = output_view; + color_att.loadOp = WGPULoadOp_Clear; + color_att.storeOp = WGPUStoreOp_Store; + color_att.clearValue = {0.0f, 0.0f, 0.0f, 1.0f}; + color_att.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_att; + + WGPURenderPassEncoder pass = + wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(pass); + wgpuRenderPassEncoderRelease(pass); +} diff --git a/cnn_v3/src/gbuf_deferred_effect.h b/cnn_v3/src/gbuf_deferred_effect.h new file mode 100644 index 0000000..4daf13d --- /dev/null +++ b/cnn_v3/src/gbuf_deferred_effect.h @@ -0,0 +1,23 @@ +// GBufDeferredEffect — simple deferred render from packed G-buffer. +// Inputs: feat_tex0, feat_tex1 (rgba32uint). Output: albedo * shadow (rgba8unorm). + +#pragma once +#include "gpu/effect.h" +#include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" + +class GBufDeferredEffect : public Effect { + public: + GBufDeferredEffect(const GpuContext& ctx, + const std::vector<std::string>& inputs, + const std::vector<std::string>& outputs, + float start_time, float end_time); + + void render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) override; + + private: + RenderPipeline pipeline_; + BindGroup bind_group_; +}; diff --git a/src/effects/shaders.cc b/src/effects/shaders.cc index a74d920..7a68e5f 100644 --- a/src/effects/shaders.cc +++ b/src/effects/shaders.cc @@ -119,6 +119,7 @@ const char* gbuf_raster_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_GBUF_RASTE const char* gbuf_pack_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_GBUF_PACK); const char* gbuf_shadow_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_GBUF_SHADOW); const char* gbuf_view_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_GBUF_VIEW); +const char* gbuf_deferred_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_GBUF_DEFERRED); const char* cnn_v3_enc0_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_CNN_V3_ENC0); const char* cnn_v3_enc1_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_CNN_V3_ENC1); const char* cnn_v3_bottleneck_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_CNN_V3_BOTTLENECK); diff --git a/src/effects/shaders.h b/src/effects/shaders.h index 1664706..7e1cf02 100644 --- a/src/effects/shaders.h +++ b/src/effects/shaders.h @@ -25,6 +25,7 @@ extern const char* gbuf_raster_wgsl; extern const char* gbuf_pack_wgsl; extern const char* gbuf_shadow_wgsl; extern const char* gbuf_view_wgsl; +extern const char* gbuf_deferred_wgsl; // CNN v3 inference shaders extern const char* cnn_v3_enc0_wgsl; diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index edb4a23..77e5c76 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -36,6 +36,7 @@ #include "../../cnn_v3/src/gbuffer_effect.h" #include "../../cnn_v3/src/cnn_v3_effect.h" #include "../../cnn_v3/src/gbuf_view_effect.h" +#include "../../cnn_v3/src/gbuf_deferred_effect.h" // TODO: Port CNN effects // #include "../../cnn_v1/src/cnn_v1_effect.h" diff --git a/src/tests/gpu/test_demo_effects.cc b/src/tests/gpu/test_demo_effects.cc index d6e9c8a..1bb89f9 100644 --- a/src/tests/gpu/test_demo_effects.cc +++ b/src/tests/gpu/test_demo_effects.cc @@ -94,6 +94,11 @@ static void test_effects() { fixture.ctx(), std::vector<std::string>{"gbuf_feat0", "gbuf_feat1"}, std::vector<std::string>{"gbuf_view_out"}, 0.0f, 1000.0f)}, + {"GBufDeferredEffect", + std::make_shared<GBufDeferredEffect>( + fixture.ctx(), + std::vector<std::string>{"gbuf_feat0", "gbuf_feat1"}, + std::vector<std::string>{"gbuf_deferred_out"}, 0.0f, 1000.0f)}, }; int passed = 0; diff --git a/tools/seq_compiler.py b/tools/seq_compiler.py index 2d802b2..2de0f46 100755 --- a/tools/seq_compiler.py +++ b/tools/seq_compiler.py @@ -399,7 +399,8 @@ def generate_cpp(seq: SequenceDecl, sorted_effects: List[EffectDecl], 'NtscYiq': 'ntsc', 'GBufferEffect': '#include "../../cnn_v3/src/gbuffer_effect.h"', 'CNNv3Effect': '#include "../../cnn_v3/src/cnn_v3_effect.h"', - 'GBufViewEffect': '#include "../../cnn_v3/src/gbuf_view_effect.h"', + 'GBufViewEffect': '#include "../../cnn_v3/src/gbuf_view_effect.h"', + 'GBufDeferredEffect': '#include "../../cnn_v3/src/gbuf_deferred_effect.h"', } includes = set() for effect in seq.effects: diff --git a/workspaces/main/assets.txt b/workspaces/main/assets.txt index 3045ab6..4a71aac 100644 --- a/workspaces/main/assets.txt +++ b/workspaces/main/assets.txt @@ -103,6 +103,7 @@ SHADER_GBUF_RASTER, WGSL, ../../cnn_v3/shaders/gbuf_raster.wgsl, "CNN v3 G-buffe SHADER_GBUF_PACK, WGSL, ../../cnn_v3/shaders/gbuf_pack.wgsl, "CNN v3 G-buffer feature pack compute shader" SHADER_GBUF_VIEW, WGSL, ../../cnn_v3/shaders/gbuf_view.wgsl, "CNN v3 G-buffer channel visualization (4x5 grid)" SHADER_GBUF_SHADOW, WGSL, ../../cnn_v3/shaders/gbuf_shadow.wgsl, "CNN v3 G-buffer SDF shadow raymarching pass" +SHADER_GBUF_DEFERRED, WGSL, ../../cnn_v3/shaders/gbuf_deferred.wgsl, "CNN v3 simple deferred render (albedo * shadow)" # --- CNN v3 Inference --- SHADER_CNN_V3_COMMON, WGSL, ../../cnn_v3/shaders/cnn_v3_common.wgsl, "CNN v3 shared helpers snippet (get_w, unpack_8ch)" diff --git a/workspaces/main/timeline.seq b/workspaces/main/timeline.seq index 3e4c140..bb667b3 100644 --- a/workspaces/main/timeline.seq +++ b/workspaces/main/timeline.seq @@ -13,10 +13,8 @@ SEQUENCE 8.00 0 "scene 2" SEQUENCE 12.00 0 "cnn_v3_test" NODE gbuf_feat0 gbuf_rgba32uint NODE gbuf_feat1 gbuf_rgba32uint - NODE cnn_v3_out gbuf_albedo EFFECT + GBufferEffect source -> gbuf_feat0 gbuf_feat1 0.00 8.00 - EFFECT + CNNv3Effect gbuf_feat0 gbuf_feat1 -> cnn_v3_out 0.00 8.00 - EFFECT + Passthrough cnn_v3_out -> sink 0.00 8.00 + EFFECT + GBufDeferredEffect gbuf_feat0 gbuf_feat1 -> sink 0.00 8.00 SEQUENCE 20.00 2 "hybrid_heptagon" # Heptagon -> Hybrid3D -> sink |
