1use crate::kvasir::node::{ExecutionContext, KvasirNode};
2use crate::kvasir::nodes::{PassId, RES_SCENE};
3use crate::kvasir::resource::ResourceId;
4
5pub struct OffscreenGeometryNode {
6 pub target_id: u64,
7 pub output_texture: ResourceId,
8 pub inputs: Vec<ResourceId>,
9 pub outputs: Vec<ResourceId>,
10}
11
12impl OffscreenGeometryNode {
13 pub fn new(target_id: u64, output_texture: ResourceId) -> Self {
14 Self {
15 target_id,
16 output_texture,
17 inputs: vec![],
18 outputs: vec![output_texture],
19 }
20 }
21}
22
23impl KvasirNode for OffscreenGeometryNode {
24 fn label(&self) -> &'static str {
25 "OffscreenGeometry"
26 }
27 fn inputs(&self) -> &[ResourceId] {
28 &self.inputs
29 }
30 fn outputs(&self) -> &[ResourceId] {
31 &self.outputs
32 }
33 fn pass_id(&self) -> PassId {
34 PassId::Geometry
35 }
36
37 fn execute(&self, ctx: &mut ExecutionContext) {
38 let view = match ctx.registry.get_texture_view(self.output_texture) {
39 Some(v) => v,
40 None => {
41 log::error!(
42 "Missing texture view for {}",
43 stringify!(self.output_texture)
44 );
45 return;
46 }
47 };
48 let mut p = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
52 label: Some("Surtr Offscreen Geometry"),
53 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
54 view: &view,
55 resolve_target: None,
56 ops: wgpu::Operations {
57 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
58 store: wgpu::StoreOp::Store,
59 },
60 depth_slice: None,
61 })],
62 depth_stencil_attachment: None, timestamp_writes: None,
64 occlusion_query_set: None,
65 multiview_mask: None,
66 });
67
68 if !ctx.renderer.draw_calls.is_empty() {
69 p.set_vertex_buffer(0, ctx.renderer.geometry_buffers.vertex_buffer.slice(..));
70 p.set_vertex_buffer(1, ctx.renderer.geometry_buffers.instance_buffer.slice(..));
71 p.set_index_buffer(
72 ctx.renderer.geometry_buffers.index_buffer.slice(..),
73 wgpu::IndexFormat::Uint32,
74 );
75 p.set_bind_group(1, &ctx.renderer.dummy_env_bind_group, &[]);
76 p.set_bind_group(2, &ctx.renderer.berserker_bind_group, &[]);
77 p.set_bind_group(3, &ctx.renderer.gradient_bind_group, &[]);
78
79 for call in ctx
80 .renderer
81 .draw_calls
82 .iter()
83 .filter(|c| c.target_id == Some(self.target_id))
84 {
85 p.set_pipeline(&ctx.renderer.opaque_pipeline);
86 let bg = if let Some(id) = call.texture_id {
87 if id == 0 {
88 &ctx.renderer.mega_heim_bind_group
89 } else {
90 ctx.renderer
91 .texture_bind_groups
92 .get(id as usize)
93 .unwrap_or(&ctx.renderer.dummy_texture_bind_group)
94 }
95 } else {
96 &ctx.renderer.dummy_texture_bind_group
97 };
98 p.set_bind_group(0, bg, &[]);
99 p.draw_indexed(
100 call.index_start..call.index_start + call.index_count,
101 0,
102 call.instance_start..call.instance_start + call.instance_count,
103 );
104 }
105 }
106 }
107}
108
109pub struct EffectCompositeNode {
110 pub target_id: u64,
111 pub input_texture: ResourceId,
112 pub output_scene: ResourceId,
113 pub effect: String,
114 pub blend_mode: u32,
115 pub effect_args: [f32; 16],
116 pub inputs: Vec<ResourceId>,
117 pub outputs: Vec<ResourceId>,
118}
119
120impl EffectCompositeNode {
121 pub fn new(
122 target_id: u64,
123 input_texture: ResourceId,
124 effect: String,
125 blend_mode: u32,
126 effect_args: [f32; 16],
127 ) -> Self {
128 Self {
129 target_id,
130 input_texture,
131 output_scene: RES_SCENE,
132 effect,
133 blend_mode,
134 effect_args,
135 inputs: vec![input_texture],
136 outputs: vec![RES_SCENE],
137 }
138 }
139}
140
141impl KvasirNode for EffectCompositeNode {
142 fn label(&self) -> &'static str {
143 "EffectComposite"
144 }
145 fn inputs(&self) -> &[ResourceId] {
146 &self.inputs
147 }
148 fn outputs(&self) -> &[ResourceId] {
149 &self.outputs
150 }
151 fn pass_id(&self) -> PassId {
152 PassId::PostProcess {
153 pipeline_id: self.target_id,
154 }
155 }
156
157 fn execute(&self, ctx: &mut ExecutionContext) {
158 let input_view = match ctx.registry.get_texture_view(self.input_texture) {
159 Some(v) => v,
160 None => {
161 log::error!(
162 "Missing texture view for {}",
163 stringify!(self.input_texture)
164 );
165 return;
166 }
167 };
168 let scene_view = match ctx.registry.get_texture_view(self.output_scene) {
169 Some(v) => v,
170 None => {
171 log::error!("Missing texture view for {}", stringify!(self.output_scene));
172 return;
173 }
174 };
175
176 let bind_group = ctx.get_or_create_bind_group(
178 (self.input_texture, 0, false),
179 &ctx.renderer.texture_bind_group_layout,
180 &[
181 wgpu::BindGroupEntry {
182 binding: 0,
183 resource: wgpu::BindingResource::TextureViewArray(&vec![&input_view; 32]),
184 },
185 wgpu::BindGroupEntry {
186 binding: 1,
187 resource: wgpu::BindingResource::Sampler(&ctx.renderer.linear_sampler),
188 },
189 ],
190 Some("Effect Input Bind Group"),
191 );
192
193 let pipeline = if let Some(p) = ctx.renderer.effect_pipelines.get(&self.effect) {
198 p
199 } else {
200 return;
201 };
202
203 ctx.renderer.queue.write_buffer(
205 &ctx.renderer.effect_params_buffer,
206 0,
207 bytemuck::cast_slice(&[crate::types::EffectUniforms {
208 time: ctx.renderer.start_time.elapsed().as_secs_f32(),
209 pad0: 0.0,
210 size: [
211 ctx.renderer.current_width() as f32,
212 ctx.renderer.current_height() as f32,
213 ],
214 args: self.effect_args,
215 }]),
216 );
217
218 let mut p = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
219 label: Some("Surtr Effect Composite"),
220 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
221 view: &scene_view,
222 resolve_target: None,
223 ops: wgpu::Operations {
224 load: wgpu::LoadOp::Load,
225 store: wgpu::StoreOp::Store,
226 },
227 depth_slice: None,
228 })],
229 depth_stencil_attachment: None,
230 timestamp_writes: None,
231 occlusion_query_set: None,
232 multiview_mask: None,
233 });
234
235 p.set_pipeline(pipeline);
236 p.set_bind_group(0, &bind_group, &[]);
237 p.set_bind_group(1, &ctx.renderer.effect_params_bind_group, &[]);
238 p.draw(0..3, 0..1); }
240}