1use crate::prelude::*;
2use wde_logger::prelude::*;
3use wde_wgpu::passes::{
4 CommandBuffer, RenderPassBuilder, RenderPassColorAttachment, RenderPassDepth
5};
6
7use bevy::ecs::system::{ReadOnlySystemParam, SystemParamItem, SystemState};
8use bevy::prelude::*;
9use std::collections::HashMap;
10use std::ops::Range;
11use wde_wgpu::pipelines::BindGroup;
12
13use crate::{
14 assets::{Mesh, Texture},
15 core::SwapchainFrame
16};
17
18pub use wde_wgpu::command_buffer::{LoadOp, Operations, StoreOp};
19pub use wde_wgpu::render_pass::RenderPassInstance;
20
21pub struct RenderPassDescColorAttachment {
24 pub texture: Option<AssetId<Texture>>,
26 pub load: LoadOp<crate::prelude::Color>,
28 pub store: StoreOp,
30 pub resolve_target: Option<AssetId<Texture>>
32}
33impl Default for RenderPassDescColorAttachment {
34 fn default() -> Self {
35 Self {
36 texture: None,
37 load: LoadOp::Load,
38 store: StoreOp::Store,
39 resolve_target: None
40 }
41 }
42}
43pub struct RenderPassDescDepthAttachment {
45 pub texture: Option<AssetId<Texture>>,
47 pub load: LoadOp<f32>,
49 pub store: StoreOp
51}
52impl Default for RenderPassDescDepthAttachment {
53 fn default() -> Self {
54 Self {
55 texture: None,
56 load: LoadOp::Load,
57 store: StoreOp::Store
58 }
59 }
60}
61#[derive(Default)]
63pub struct RenderPassDesc {
64 pub attachments_depth: Option<RenderPassDescDepthAttachment>,
66 pub attachments_colors: Option<Vec<RenderPassDescColorAttachment>>
68}
69
70pub struct DrawCommandsBatch {
72 pub bind_group: Option<(u32, BindGroup)>, pub index_range: Range<u32>,
74 pub instance_range: Range<u32>
75}
76impl Default for DrawCommandsBatch {
77 fn default() -> Self {
78 Self {
79 bind_group: None,
80 index_range: 0..0,
81 instance_range: 0..1
82 }
83 }
84}
85pub enum SubPassCommand {
87 Pipeline(Option<CachedPipelineIndex>),
89 BindGroup(u32, Option<BindGroup>),
91 Mesh(Option<AssetId<Mesh>>),
93 DrawBatches(Vec<DrawCommandsBatch>),
95 Custom(for<'pass> fn(&'pass World, &mut RenderPassInstance<'pass>))
97}
98#[derive(Default)]
101pub struct RenderSubPassDesc(pub Vec<SubPassCommand>);
102
103pub type RenderPassId = i32;
105pub trait RenderPass {
109 type Params: ReadOnlySystemParam;
110
111 fn describe(params: &SystemParamItem<Self::Params>) -> RenderPassDesc;
113 fn id() -> RenderPassId;
115 fn label() -> &'static str;
117
118 fn custom_render(_world: &mut World, _command_buffer: &mut CommandBuffer) -> Option<bool> {
126 None
127 }
128}
129trait RenderPassNode: Send + Sync + 'static {
131 fn describe(&mut self, world: &mut World) -> RenderPassDesc;
132 fn label(&mut self) -> &'static str;
133 fn custom_render(
134 &mut self,
135 world: &mut World,
136 command_buffer: &mut CommandBuffer
137 ) -> Option<bool>;
138}
139struct RenderPassHolder<P: RenderPass> {
140 _phantom: std::marker::PhantomData<P>
141}
142impl<P> RenderPassNode for RenderPassHolder<P>
143where
144 P: RenderPass + Send + Sync + 'static
145{
146 fn describe(&mut self, world: &mut World) -> RenderPassDesc {
147 let mut state: SystemState<P::Params> = SystemState::new(world);
148 let params = state.get(world);
149 P::describe(¶ms)
150 }
151 fn label(&mut self) -> &'static str {
152 P::label()
153 }
154 fn custom_render(
155 &mut self,
156 world: &mut World,
157 command_buffer: &mut CommandBuffer
158 ) -> Option<bool> {
159 P::custom_render(world, command_buffer)
160 }
161}
162
163pub trait RenderSubPass {
168 type Params: ReadOnlySystemParam;
169
170 fn describe(params: &SystemParamItem<Self::Params>) -> RenderSubPassDesc;
172 fn label() -> &'static str;
174}
175trait RenderSubPassNode: Send + Sync + 'static {
177 fn describe(&mut self, world: &mut World) -> RenderSubPassDesc;
178 fn label(&mut self) -> &'static str;
179}
180struct RenderSubPassHolder<SP: RenderSubPass> {
181 _phantom: std::marker::PhantomData<SP>
182}
183impl<SP> RenderSubPassNode for RenderSubPassHolder<SP>
184where
185 SP: RenderSubPass + Send + Sync + 'static
186{
187 fn describe(&mut self, world: &mut World) -> RenderSubPassDesc {
188 let mut state: SystemState<SP::Params> = SystemState::new(world);
189 let params = state.get(world);
190 SP::describe(¶ms)
191 }
192 fn label(&mut self) -> &'static str {
193 SP::label()
194 }
195}
196
197#[derive(Resource, Default)]
202pub struct RenderGraph {
203 passes: Vec<Box<dyn RenderPassNode>>,
205 sub_passes: Vec<Box<dyn RenderSubPassNode>>,
206
207 sorted_ids: Vec<RenderPassId>, render_passes_by_id: HashMap<RenderPassId, usize>, sub_passes_by_renderpass: HashMap<usize, Vec<usize>> }
214impl RenderGraph {
215 pub fn add_pass<P: RenderPass + Send + Sync + 'static>(&mut self) -> &mut Self {
218 let id = P::id();
219
220 debug_assert!(
222 !self.render_passes_by_id.contains_key(&id),
223 "A render pass with ID {} already exists in the render graph.",
224 id
225 );
226
227 self.passes.push(Box::new(RenderPassHolder {
229 _phantom: std::marker::PhantomData::<P>
230 }));
231 self.render_passes_by_id.insert(id, self.passes.len() - 1);
232 self.sorted_ids.push(id);
233
234 self.sorted_ids.sort();
236 self
237 }
238
239 pub fn add_sub_pass<SP: RenderSubPass + Send + Sync + 'static, P: RenderPass>(
242 &mut self
243 ) -> &mut Self {
244 self.sub_passes.push(Box::new(RenderSubPassHolder {
245 _phantom: std::marker::PhantomData::<SP>
246 }));
247 let sub_pass_index = self.render_passes_by_id.get(&P::id()).copied();
248 debug_assert!(
249 sub_pass_index.is_some(),
250 "Cannot add sub-pass '{}' to render pass '{}': no render pass with ID {} found in the render graph.",
251 SP::label(),
252 P::label(),
253 P::id()
254 );
255 self.sub_passes_by_renderpass
256 .entry(sub_pass_index.unwrap())
257 .or_default()
258 .push(self.sub_passes.len() - 1);
259 self
260 }
261
262 pub(crate) fn render(world: &mut World) {
263 world.resource_scope(|world, mut graph: Mut<RenderGraph>| {
264 render(&mut graph, world);
265 });
266 }
267}
268
269fn render(graph: &mut RenderGraph, world: &mut World) {
270 trace!(
271 "Starting render graph execution with {} passes.",
272 graph.passes.len()
273 );
274
275 let mut command_buffer = {
277 let render_instance = world.get_resource::<RenderInstance>().unwrap();
278 CommandBuffer::new(&render_instance.0.read().unwrap(), "render-graph")
279 };
280
281 for pass_id in &graph.sorted_ids {
283 let pass_index = *graph.render_passes_by_id.get(pass_id).unwrap();
285 let pass = &mut graph.passes[pass_index];
286 let pass_label = pass.label();
287 let pass_desc = pass.describe(world);
288
289 if let Some(custom_render_result) = pass.custom_render(world, &mut command_buffer) {
291 if !custom_render_result {
293 debug!(
294 "Custom rendering for pass '{}' could not be completed. Skipping this pass for this frame.",
295 pass_label
296 );
297 }
298 continue;
299 }
300
301 let mut sub_passes = Vec::new();
303 if let Some(sub_pass_indices) = graph.sub_passes_by_renderpass.get(&pass_index) {
304 for sub_pass_index in sub_pass_indices {
305 let sub_pass = &mut graph.sub_passes[*sub_pass_index];
306 let sub_pass_label = sub_pass.label();
307 let sub_pass_desc = sub_pass.describe(world);
308 sub_passes.push((sub_pass_label, sub_pass_desc));
309 }
310 }
311
312 trace!("Rendering render pass '{}'.", pass_label);
314 let _pass_span = debug_span!("render_pass", pass_id = *pass_id, pass_label).entered();
315 let mut render_pass =
316 match create_render_pass(world, &mut command_buffer, pass_label, &pass_desc) {
317 Ok(pass) => pass,
318 Err(e) => {
319 debug!(
320 "Failed to create render pass: '{}'. Skipping this frame.",
321 e
322 );
323 continue;
324 }
325 };
326
327 for (sub_pass_label, sub_pass_desc) in &sub_passes {
329 trace!("Rendering sub-pass '{}'.", sub_pass_label);
330 let _sub_pass_span =
331 debug_span!("render_sub_pass", sub_pass_label = *sub_pass_label).entered();
332 render_sub_pass(world, &mut render_pass, sub_pass_desc);
333 }
334 }
335
336 let render_instance = world.get_resource::<RenderInstance>().unwrap();
338 command_buffer.submit(&render_instance.0.read().unwrap());
339}
340
341fn create_render_pass<'p>(
342 world: &'p World,
343 command_buffer: &'p mut CommandBuffer,
344 label: &str,
345 pass_desc: &RenderPassDesc
346) -> Result<RenderPassInstance<'p>, String> {
347 let render_instance = world.get_resource::<RenderInstance>().unwrap();
349 let textures = world.get_resource::<RenderAssets<GpuTexture>>().unwrap();
350 let swapchain_frame = world.get_resource::<SwapchainFrame>().unwrap();
351 let (surface_width, surface_height) = {
352 let render_instance = render_instance.0.read().unwrap();
353 let config = render_instance.surface_config.as_ref().unwrap();
354 (config.width, config.height)
355 };
356
357 command_buffer.create_render_pass(label, |builder: &mut RenderPassBuilder| -> Result<(), String> {
359 if let Some(attachments_color) = &pass_desc.attachments_colors {
361 for color_attachment_desc in attachments_color.iter() {
363 if let Some(texture_id) = color_attachment_desc.texture
364 && let Some(texture) = textures.get(texture_id) {
365 if !(surface_width == texture.texture.size.0 && surface_height == texture.texture.size.1) {
366 return Err(format!("Color attachment texture has invalid size: expected ({}, {}), got ({}, {})", surface_width, surface_height, texture.texture.size.0, texture.texture.size.1));
367 }
368 let resolve_target = if let Some(resolve_target) = color_attachment_desc.resolve_target {
369 if let Some(resolve_target) = textures.get(resolve_target)
370 && surface_width == resolve_target.texture.size.0 && surface_height == resolve_target.texture.size.1 {
371 Some(resolve_target)
372 } else {
373 return Err("Invalid resolve target for color attachment".to_string());
374 }
375 } else { None };
376 builder.add_color_attachment(RenderPassColorAttachment {
377 texture: Some(&texture.texture.view),
378 load: match color_attachment_desc.load {
379 LoadOp::Load => LoadOp::Load,
380 LoadOp::Clear(color) => LoadOp::Clear(color.into())
381 },
382 store: color_attachment_desc.store,
383 resolve_target: resolve_target.map(|tex| &tex.texture.view)
384 });
385 } else {
386 return Err("Invalid texture for color attachment".to_string());
387 }
388 }
389 } else {
390 let swapchain_frame = match swapchain_frame.data.as_ref() {
392 Some(frame) => frame,
393 None => return Err("No swapchain frame available".to_string())
394 };
395 builder.add_color_attachment(RenderPassColorAttachment {
396 texture: Some(&swapchain_frame.view),
397 ..default()
398 });
399 }
400
401 if let Some(depth_attachment) = pass_desc.attachments_depth.as_ref() {
403 if let Some(depth_texture) = depth_attachment.texture
404 && let Some(depth_texture) = textures.get(depth_texture) {
405 if !(surface_width == depth_texture.texture.size.0 && surface_height == depth_texture.texture.size.1) {
406 return Err(format!("Depth attachment texture has invalid size: expected ({}, {}), got ({}, {}).", surface_width, surface_height, depth_texture.texture.size.0, depth_texture.texture.size.1));
407 }
408 builder.set_depth_texture(RenderPassDepth {
409 texture: Some(&depth_texture.texture.view),
410 load: depth_attachment.load,
411 store: depth_attachment.store
412 });
413 } else {
414 return Err("Invalid texture for depth attachment".to_string());
415 }
416 }
417 Ok(())
418 })
419}
420
421fn render_sub_pass<'p>(
422 world: &'p World,
423 render_pass: &mut RenderPassInstance<'p>,
424 sub_pass_desc: &'p RenderSubPassDesc
425) {
426 let pipeline_manager = world.get_resource::<PipelineManager>().unwrap();
428 let meshes = world.get_resource::<RenderAssets<GpuMesh>>().unwrap();
429
430 for stage_command in &sub_pass_desc.0 {
432 match stage_command {
433 SubPassCommand::Pipeline(pipeline) => {
435 if let Some(pipeline) = pipeline
436 && let CachedPipelineStatus::OkRender(pipeline) =
437 pipeline_manager.get_pipeline(*pipeline)
438 {
439 if let Err(e) = render_pass.set_pipeline(pipeline) {
440 error!("Failed to set pipeline: {:?}.", e);
441 return;
442 }
443 } else {
444 return;
445 }
446 }
447
448 SubPassCommand::BindGroup(index, bind_group) => {
450 if let Some(bind_group) = bind_group {
451 render_pass.set_bind_group(*index, bind_group);
452 } else {
453 return;
454 }
455 }
456
457 SubPassCommand::Mesh(mesh) => {
459 if let Some(mesh) = mesh
460 && let Some(mesh) = meshes.get(*mesh)
461 {
462 render_pass.set_vertex_buffer(0, mesh.vertex_buffer.as_ref().unwrap());
463 render_pass.set_index_buffer(mesh.index_buffer.as_ref().unwrap());
464 } else {
465 return;
466 }
467 }
468
469 SubPassCommand::DrawBatches(batches) => {
471 for batch in batches {
472 if let Some((index, bind_group)) = &batch.bind_group {
474 render_pass.set_bind_group(*index, bind_group);
475 }
476
477 match render_pass
479 .draw_indexed(batch.index_range.clone(), batch.instance_range.clone())
480 {
481 Ok(_) => {}
482 Err(e) => {
483 error!("Failed to draw: {:?}.", e);
484 }
485 }
486 }
487 }
488
489 SubPassCommand::Custom(custom_fn) => {
491 custom_fn(world, render_pass);
492 }
493 }
494 }
495}