cvkg_render_gpu/kvasir/
nodes.rs1use crate::kvasir::node::{ExecutionContext, KvasirNode};
2use crate::kvasir::resource::ResourceId;
3use crate::passes::accessibility::AccessibilityNode;
4use crate::passes::backdrop_region::BackdropRegionNode;
5use crate::passes::bloom::{BloomBlurNode, BloomExtractNode};
6use crate::passes::composite::CompositeNode;
7use crate::passes::geometry::GeometryNode;
8use crate::passes::glass::{BackdropBlurNode, BackdropCopyNode, GlassNode};
9use crate::passes::ui::UINode;
10use crate::passes::volumetric::VolumetricNode;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum PassId {
14 Geometry,
15 BackdropCopy,
16 BackdropBlur,
17 Volumetric,
18 Glass,
19 UI,
20 Flow,
21 ComputeParticle,
22 BloomExtract,
23 BloomBlur,
24 Composite,
25 Accessibility,
26 Present,
27 PostProcess {
28 pipeline_id: u64,
29 },
30 BackdropRegion,
32}
33
34pub struct PresentNode {
35 pub inputs: Vec<ResourceId>,
36 pub outputs: Vec<ResourceId>,
37}
38
39impl KvasirNode for PresentNode {
40 fn label(&self) -> &'static str {
41 "Present"
42 }
43 fn inputs(&self) -> &[ResourceId] {
44 &self.inputs
45 }
46 fn outputs(&self) -> &[ResourceId] {
47 &self.outputs
48 }
49 fn pass_id(&self) -> PassId {
50 PassId::Present
51 }
52 fn execute(&self, _ctx: &mut ExecutionContext) {
53 }
55}
56
57pub const RES_SCENE: ResourceId = ResourceId(1);
59pub const RES_SCENE_MSAA: ResourceId = ResourceId(5);
60pub const RES_BLUR_A: ResourceId = ResourceId(2);
61pub const RES_BLOOM_A: ResourceId = ResourceId(3);
62pub const RES_SWAPCHAIN: ResourceId = ResourceId(4);
63
64pub struct RenderGraphConfig<'a> {
66 pub has_glass: bool,
67 pub has_bloom: bool,
68 pub has_accessibility: bool,
69 pub has_volumetric: bool,
71 pub active_offscreens: &'a [crate::types::OffscreenEffectConfig],
72 pub portal_regions: &'a [cvkg_core::Rect],
73 pub width: u32,
74 pub height: u32,
75 pub scale: f32,
76}
77
78pub fn build_render_graph(config: &RenderGraphConfig<'_>) -> super::graph::KvasirGraph {
80 let mut builder = super::graph::GraphBuilder::new();
81
82 let geometry = builder.add_node(Box::new(GeometryNode::new()));
83 let mut last_scene_node = geometry;
84
85 for offscreen in config.active_offscreens {
86 let tex_id = ResourceId(1000 + (offscreen.target_id as u32));
87 debug_assert!(offscreen.target_id <= u32::MAX as u64, "target_id overflow");
88
89 let off_geom = builder.add_node(Box::new(
90 crate::passes::effects::OffscreenGeometryNode::new(offscreen.target_id, tex_id),
91 ));
92
93 let composite =
94 builder.add_node(Box::new(crate::passes::effects::EffectCompositeNode::new(
95 offscreen.target_id,
96 tex_id,
97 offscreen.effect.clone(),
98 offscreen.blend_mode,
99 offscreen.effect_args,
100 )));
101
102 builder.connect(off_geom, tex_id, composite);
103 builder.connect(last_scene_node, RES_SCENE, composite);
104 last_scene_node = composite;
105 }
106
107 if config.has_glass {
108 let copy = builder.add_node(Box::new(BackdropCopyNode::new()));
109 builder.connect(last_scene_node, RES_SCENE, copy);
110
111 let blur = builder.add_node(Box::new(BackdropBlurNode::new(
112 config.width / 2,
113 config.height / 2,
114 )));
115 builder.connect(copy, RES_BLUR_A, blur);
116
117 for (i, region) in config.portal_regions.iter().enumerate() {
119 let region_id = ResourceId(2000 + i as u32);
120 let region_node =
121 builder.add_node(Box::new(BackdropRegionNode::new(*region, region_id)));
122 builder.connect(last_scene_node, RES_SCENE, region_node);
123 }
124
125 let glass = builder.add_node(Box::new(GlassNode::new(config.scale)));
126 builder.connect(blur, RES_BLUR_A, glass);
127 builder.connect(last_scene_node, RES_SCENE, glass);
128 last_scene_node = glass;
129 }
130
131 let ui = builder.add_node(Box::new(UINode::new()));
132 builder.connect(last_scene_node, RES_SCENE, ui);
133 last_scene_node = ui;
134
135 let has_volumetric = config.has_volumetric;
137 if has_volumetric {
138 let volumetric = builder.add_node(Box::new(VolumetricNode::new()));
139 builder.connect(last_scene_node, RES_SCENE, volumetric);
140 last_scene_node = volumetric;
141 }
142
143 let mut last_bloom_node = None;
145 if config.has_bloom {
146 let extract = builder.add_node(Box::new(BloomExtractNode::new()));
147 builder.connect(last_scene_node, RES_SCENE, extract);
148
149 let blur = builder.add_node(Box::new(BloomBlurNode::new(
150 config.width / 2,
151 config.height / 2,
152 )));
153 builder.connect(extract, RES_BLOOM_A, blur);
154 last_bloom_node = Some(blur);
155 }
156
157 if config.has_accessibility {
159 let a11y = builder.add_node(Box::new(AccessibilityNode::new()));
160 builder.connect(last_scene_node, RES_SCENE, a11y);
161 last_scene_node = a11y;
163 }
164
165 let composite = builder.add_node(Box::new(CompositeNode::new(
169 config.has_bloom,
170 !config.has_accessibility,
171 )));
172 builder.connect(last_scene_node, RES_SCENE, composite);
173 if let Some(bloom_node) = last_bloom_node {
174 builder.connect(bloom_node, RES_BLOOM_A, composite);
175 }
176
177 let present = builder.add_node(Box::new(PresentNode {
179 inputs: vec![RES_SCENE],
180 outputs: vec![],
181 }));
182 builder.connect(last_scene_node, RES_SCENE, present);
183
184 builder.build()
185}