cvkg_render_gpu/kvasir/
node.rs1use super::resource::ResourceId;
4use crate::renderer::GpuRenderer;
5use cvkg_core::PassNode;
6
7#[derive(Debug, thiserror::Error)]
9pub enum KvasirError {
10 #[error("Cycle detected in render graph: {0:?}")]
11 CycleDetected(Vec<String>),
12 #[error("Missing resource: {0}")]
13 MissingResource(String),
14 #[error("Invalid node configuration: {0}")]
15 InvalidNodeConfig(String),
16 #[error("Graph compilation failed: {0}")]
17 CompilationFailed(String),
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct GraphId(pub u64);
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum ExecutionHint {
27 Raster,
28 Compute,
29 Hybrid,
30}
31
32pub struct ExecutionContext<'a> {
53 pub device: &'a wgpu::Device,
54 pub queue: &'a wgpu::Queue,
55 pub encoder: &'a mut wgpu::CommandEncoder,
56 pub registry: &'a crate::kvasir::registry::ResourceRegistry,
57 pub renderer: &'a crate::renderer::GpuRenderer,
58 pub target_view: &'a wgpu::TextureView,
59 pub depth_view: &'a wgpu::TextureView,
60 pub blur_env_bind_group_a: &'a wgpu::BindGroup,
61 pub blur_env_bind_group_b: &'a wgpu::BindGroup,
62 pub bloom_env_bind_group_a: &'a wgpu::BindGroup,
63 pub bloom_env_bind_group_b: &'a wgpu::BindGroup,
64 pub scale_factor: f32,
65}
66
67impl<'a> ExecutionContext<'a> {
68 pub fn begin_render_pass(
69 &mut self,
70 desc: &wgpu::RenderPassDescriptor<'_>,
71 ) -> wgpu::RenderPass<'_> {
72 self.encoder.begin_render_pass(desc)
73 }
74
75 pub fn get_or_create_bind_group(
78 &self,
79 key: (crate::kvasir::resource::ResourceId, u32, bool),
80 layout: &wgpu::BindGroupLayout,
81 entries: &[wgpu::BindGroupEntry<'_>],
82 label: Option<&str>,
83 ) -> wgpu::BindGroup {
84 let mut cache = GpuRenderer::lock_or_clear_cache(&self.renderer.bind_group_cache);
85 let full_key = (self.renderer.current_window, key.0, key.1, key.2);
86 if let std::collections::hash_map::Entry::Vacant(e) = cache.entry(full_key) {
89 let bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
90 label,
91 layout,
92 entries,
93 });
94 e.insert(bg.clone());
95 bg
96 } else {
97 cache.get(&full_key).unwrap().clone()
98 }
99 }
100}
101
102#[cfg(not(target_arch = "wasm32"))]
103pub trait KvasirNode: Send + Sync {
104 fn label(&self) -> &'static str;
105 fn inputs(&self) -> &[ResourceId];
106 fn outputs(&self) -> &[ResourceId];
107 fn pass_id(&self) -> super::nodes::PassId;
108 fn execute(&self, ctx: &mut ExecutionContext);
109}
110
111#[cfg(target_arch = "wasm32")]
112pub trait KvasirNode {
113 fn label(&self) -> &'static str;
114 fn inputs(&self) -> &[ResourceId];
115 fn outputs(&self) -> &[ResourceId];
116 fn pass_id(&self) -> super::nodes::PassId;
117 fn execute(&self, ctx: &mut ExecutionContext);
118}
119
120use crate::passes::accessibility::AccessibilityNode;
125use crate::passes::backdrop_region::BackdropRegionNode;
126use crate::passes::bloom::{BloomBlurNode, BloomExtractNode};
127use crate::passes::composite::CompositeNode;
128use crate::passes::effects::{EffectCompositeNode, OffscreenGeometryNode};
129use crate::passes::geometry::GeometryNode;
130use crate::passes::glass::{BackdropBlurNode, BackdropCopyNode, GlassNode};
131use crate::passes::opaque3d::Opaque3dNode;
132use crate::passes::pre_world_panel::PreWorldPanelNode;
133use crate::passes::shadow::ShadowNode;
134use crate::passes::svg_filter::SvgFilterNode;
135use crate::passes::tonemap::ToneMapNode;
136use crate::passes::ui::UINode;
137use crate::passes::volumetric::VolumetricNode;
138
139impl PassNode for GeometryNode {}
140impl PassNode for UINode {}
141impl PassNode for ShadowNode {}
142impl PassNode for Opaque3dNode {}
143impl PassNode for CompositeNode {}
144impl PassNode for GlassNode {}
145impl PassNode for BackdropCopyNode {}
146impl PassNode for BackdropBlurNode {}
147impl PassNode for BloomExtractNode {}
148impl PassNode for BloomBlurNode {}
149impl PassNode for VolumetricNode {}
150impl PassNode for ToneMapNode {}
151impl PassNode for AccessibilityNode {}
152impl PassNode for BackdropRegionNode {}
153impl PassNode for PreWorldPanelNode {}
154impl PassNode for OffscreenGeometryNode {}
155impl PassNode for EffectCompositeNode {}
156impl PassNode for SvgFilterNode {}
157
158#[cfg(test)]
167mod p1_2_aliasing_contract_tests {
168 use super::*;
169
170 #[test]
175 fn renderer_field_is_immutable() {
176 fn _assert_immutable(_: &GpuRenderer) {}
178 let _f: fn(&GpuRenderer) = _assert_immutable;
179 }
180
181 #[test]
185 fn encoder_field_is_mutable() {
186 fn _assert_mut(_: &mut wgpu::CommandEncoder) {}
188 let _f: fn(&mut wgpu::CommandEncoder) = _assert_mut;
189 }
190}