Skip to main content

cvkg_render_gpu/passes/
composite.rs

1use crate::kvasir::node::{ExecutionContext, KvasirNode};
2use crate::kvasir::nodes::{PassId, RES_BLOOM_A, RES_SCENE, RES_SWAPCHAIN};
3use crate::kvasir::resource::ResourceId;
4
5pub struct CompositeNode {
6    pub inputs: Vec<ResourceId>,
7    pub outputs: Vec<ResourceId>,
8    pub has_bloom: bool,
9    /// If true, clear the target before rendering (first pass to touch the swapchain).
10    /// If false, load existing content (e.g., accessibility pass already wrote to it).
11    pub clear_target: bool,
12}
13
14impl CompositeNode {
15    pub fn new(has_bloom: bool, clear_target: bool) -> Self {
16        Self {
17            inputs: if has_bloom {
18                vec![RES_SCENE, RES_BLOOM_A]
19            } else {
20                vec![RES_SCENE]
21            },
22            outputs: vec![RES_SWAPCHAIN],
23            has_bloom,
24            clear_target,
25        }
26    }
27}
28
29impl KvasirNode for CompositeNode {
30    fn label(&self) -> &'static str {
31        "Composite"
32    }
33
34    fn inputs(&self) -> &[ResourceId] {
35        &self.inputs
36    }
37
38    fn outputs(&self) -> &[ResourceId] {
39        &self.outputs
40    }
41
42    fn pass_id(&self) -> PassId {
43        PassId::Composite
44    }
45
46    fn execute(&self, ctx: &mut ExecutionContext) {
47        let target_view = ctx.target_view;
48
49        // Get scene view and create cached bind group BEFORE render pass (avoids borrow conflict)
50        let scene_view = match ctx.registry.get_texture_view(RES_SCENE) {
51            Some(v) => v,
52            None => {
53                log::error!("Missing texture view for {}", stringify!(RES_SCENE));
54                return;
55            }
56        };
57        let scene_texture_bind_group = ctx.get_or_create_bind_group(
58            (RES_SCENE, 1, false),
59            &ctx.renderer.texture_bind_group_layout,
60            &[
61                wgpu::BindGroupEntry {
62                    binding: 0,
63                    resource: wgpu::BindingResource::TextureViewArray(&vec![&scene_view; 32]),
64                },
65                wgpu::BindGroupEntry {
66                    binding: 1,
67                    resource: wgpu::BindingResource::Sampler(&ctx.renderer.dummy_sampler),
68                },
69            ],
70            Some("composite_scene_bg"),
71        );
72
73        let mut p = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
74            label: Some("Surtr P7 Composite"),
75            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
76                view: target_view,
77                resolve_target: None,
78                ops: wgpu::Operations {
79                    load: if self.clear_target {
80                        wgpu::LoadOp::Clear(wgpu::Color {
81                            r: 0.0,
82                            g: 0.0,
83                            b: 0.0,
84                            a: 1.0,
85                        })
86                    } else {
87                        // Load existing content (e.g., accessibility pass output)
88                        wgpu::LoadOp::Load
89                    },
90                    store: wgpu::StoreOp::Store,
91                },
92                depth_slice: None,
93            })],
94            depth_stencil_attachment: None,
95            timestamp_writes: ctx.renderer.skuld_queries.as_ref().map(|q| {
96                wgpu::RenderPassTimestampWrites {
97                    query_set: q,
98                    beginning_of_pass_write_index: None,
99                    end_of_pass_write_index: Some(1),
100                }
101            }),
102            occlusion_query_set: None,
103            multiview_mask: None,
104        });
105
106        p.set_pipeline(&ctx.renderer.composite_pipeline);
107
108        let dummy_bg = &ctx.renderer.dummy_env_bind_group;
109        if self.has_bloom {
110            p.set_bind_group(1, ctx.bloom_env_bind_group_a, &[]);
111        } else {
112            p.set_bind_group(1, dummy_bg, &[]);
113        }
114
115        p.set_bind_group(0, &scene_texture_bind_group, &[]);
116        p.set_bind_group(2, &ctx.renderer.berserker_bind_group, &[]);
117        p.draw(0..3, 0..1);
118    }
119}