Implement a Render Graph #16

Merged
SeanOMik merged 20 commits from feature/render-graph into main 2024-06-15 22:54:47 +00:00
5 changed files with 42 additions and 37 deletions
Showing only changes of commit 8c3446389c - Show all commits

View File

@ -38,7 +38,7 @@ impl GraphExecutionPath {
let node = Node { let node = Node {
id: desc.id, id: desc.id,
desc: (*desc).clone(), desc: (*desc),
slot_inputs: inputs slot_inputs: inputs
}; };
nodes.insert(node.id, node); nodes.insert(node.id, node);
@ -75,12 +75,14 @@ impl GraphExecutionPath {
} }
} }
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct SlotOwnerPair { struct SlotOwnerPair {
pass: u64, pass: u64,
slot: u64, slot: u64,
} }
#[allow(dead_code)]
struct Node<'a> { struct Node<'a> {
id: u64, id: u64,
desc: &'a RenderGraphPassDesc, desc: &'a RenderGraphPassDesc,

View File

@ -121,17 +121,11 @@ impl RenderGraph {
self.slot_names.get(name).cloned() self.slot_names.get(name).cloned()
} }
pub fn slot_value(&self, id: u64) -> Option<&SlotValue> { #[instrument(skip(self, pass), level = "debug")]
self.slots.get(&id).map(|s| &s.value)
}
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
self.passes.get(&id).map(|s| &*s.desc)
}
pub fn add_pass<P: RenderGraphPass>(&mut self, mut pass: P) { pub fn add_pass<P: RenderGraphPass>(&mut self, mut pass: P) {
let mut desc = pass.desc(self); let mut desc = pass.desc(self);
// collect all the slots of the pass
for slot in &mut desc.slots { for slot in &mut desc.slots {
if let Some((id, other)) = self if let Some((id, other)) = self
.slot_names .slot_names
@ -158,6 +152,7 @@ impl RenderGraph {
} }
} }
// get clones of the bind groups and layouts
for (name, bg, bgl) in &desc.bind_groups { for (name, bg, bgl) in &desc.bind_groups {
let bg_id = self.next_id(); let bg_id = self.next_id();
self.bind_groups.insert( self.bind_groups.insert(
@ -211,8 +206,8 @@ impl RenderGraph {
inner.prepare(world, &mut context); inner.prepare(world, &mut context);
} }
// Queue all buffer writes to the gpu
{ {
// Queue all buffer writes to the gpu
let s = debug_span!("queue_buffer_writes"); let s = debug_span!("queue_buffer_writes");
let _e = s.enter(); let _e = s.enter();
@ -236,7 +231,7 @@ impl RenderGraph {
// create the execution path for the graph. This will be executed in `RenderGraph::render` // create the execution path for the graph. This will be executed in `RenderGraph::render`
let builtin = { let builtin = {
let mut h = FxHashSet::default(); let mut h = FxHashSet::default();
h.insert(0u64); h.insert(0u64); // include the base pass
h h
}; };
let descs = self.passes.values().map(|p| &*p.desc).collect(); let descs = self.passes.values().map(|p| &*p.desc).collect();
@ -248,7 +243,8 @@ impl RenderGraph {
self.exec_path = Some(path); self.exec_path = Some(path);
} }
pub fn render(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, surface: &wgpu::Surface) { #[instrument(skip(self, surface))]
pub fn render(&mut self, surface: &wgpu::Surface) {
let mut path = self.exec_path.take().unwrap(); let mut path = self.exec_path.take().unwrap();
let output = surface.get_current_texture().unwrap(); let output = surface.get_current_texture().unwrap();
@ -272,10 +268,13 @@ impl RenderGraph {
let pass_desc = pass.desc.clone(); let pass_desc = pass.desc.clone();
let label = format!("{} Encoder", pass_desc.name); let label = format!("{} Encoder", pass_desc.name);
let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let encoder = self
label: Some(&label), .device
}); .create_command_encoder(&wgpu::CommandEncoderDescriptor {
let mut context = RenderGraphContext::new(queue, Some(encoder)); label: Some(&label),
});
let queue = self.queue.clone(); // clone is required to appease the borrow checker
let mut context = RenderGraphContext::new(&queue, Some(encoder));
let mut inner = pass_inn.borrow_mut(); let mut inner = pass_inn.borrow_mut();
inner.execute(self, &*pass_desc, &mut context); inner.execute(self, &*pass_desc, &mut context);
@ -283,10 +282,22 @@ impl RenderGraph {
encoders.push(context.encoder.unwrap().finish()); encoders.push(context.encoder.unwrap().finish());
} }
queue.submit(encoders.into_iter()); self.queue.submit(encoders.into_iter());
output.present(); output.present();
} }
pub fn slot_value(&self, id: u64) -> Option<&SlotValue> {
self.slots.get(&id).map(|s| &s.value)
}
pub fn slot_value_mut(&mut self, id: u64) -> Option<&mut SlotValue> {
self.slots.get_mut(&id).map(|s| &mut s.value)
}
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
self.passes.get(&id).map(|s| &*s.desc)
}
#[inline(always)] #[inline(always)]
pub fn pipeline(&self, id: u64) -> &Pipeline { pub fn pipeline(&self, id: u64) -> &Pipeline {
&self.pipelines.get(&id).unwrap().pipeline &self.pipelines.get(&id).unwrap().pipeline
@ -319,7 +330,8 @@ impl RenderGraph {
} }
} }
pub(crate) struct BufferWrite { /// A queued write to a GPU buffer targeting a graph slot.
pub(crate) struct GraphBufferWrite {
/// The name of the slot that has the resource that will be written /// The name of the slot that has the resource that will be written
target_slot: String, target_slot: String,
offset: u64, offset: u64,
@ -331,7 +343,7 @@ pub struct RenderGraphContext<'a> {
/// Becomes None when the encoder is submitted /// Becomes None when the encoder is submitted
pub(crate) encoder: Option<wgpu::CommandEncoder>, pub(crate) encoder: Option<wgpu::CommandEncoder>,
pub(crate) queue: &'a wgpu::Queue, pub(crate) queue: &'a wgpu::Queue,
pub(crate) buffer_writes: VecDeque<BufferWrite>, pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>, renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
} }
@ -373,15 +385,17 @@ impl<'a> RenderGraphContext<'a> {
/// This does not submit the data to the GPU immediately, or add it to the `wgpu::Queue`. The /// This does not submit the data to the GPU immediately, or add it to the `wgpu::Queue`. The
/// data will be submitted to the GPU queue right after the prepare stage for all passes /// data will be submitted to the GPU queue right after the prepare stage for all passes
/// is ran. /// is ran.
#[instrument(skip(self, bytes), level="trace", fields(size = bytes.len()))]
pub fn queue_buffer_write(&mut self, target_slot: &str, offset: u64, bytes: &[u8]) { pub fn queue_buffer_write(&mut self, target_slot: &str, offset: u64, bytes: &[u8]) {
self.buffer_writes.push_back(BufferWrite { self.buffer_writes.push_back(GraphBufferWrite {
target_slot: target_slot.to_string(), target_slot: target_slot.to_string(),
offset, offset,
bytes: bytes.to_vec(), bytes: bytes.to_vec(),
}) })
} }
/// Write /// Queue a data write of a type that to a buffer at that is contained in `target_slot`.
#[instrument(skip(self, bytes), level="trace", fields(size = std::mem::size_of::<T>()))]
pub fn queue_buffer_write_with<T: bytemuck::NoUninit>( pub fn queue_buffer_write_with<T: bytemuck::NoUninit>(
&mut self, &mut self,
target_slot: &str, target_slot: &str,

View File

@ -12,13 +12,9 @@ use crate::{
DeltaTime, DeltaTime,
}; };
/// Supplies some basic things other passes needs. /// A demo pass that renders a triangle that changes colors every frame.
///
/// screen size buffer, camera buffer,
#[derive(Default)] #[derive(Default)]
pub struct TrianglePass { pub struct TrianglePass {
//color_bg: Option<Rc<wgpu::BindGroup>>,
//color_buf: Option<Rc<wgpu::Buffer>>,
acc: f32, acc: f32,
} }
@ -48,8 +44,6 @@ impl RenderGraphPass for TrianglePass {
let color_bgl = Rc::new(color_bgl); let color_bgl = Rc::new(color_bgl);
let color_bg = Rc::new(color_bg); let color_bg = Rc::new(color_bg);
//let color_buf = Rc::new(color_buf);
let mut desc = RenderGraphPassDesc::new( let mut desc = RenderGraphPassDesc::new(
graph.next_id(), graph.next_id(),
"TrianglePass", "TrianglePass",
@ -120,14 +114,9 @@ impl RenderGraphPass for TrianglePass {
.slot_value(graph.slot_id("window_texture_view").unwrap()) .slot_value(graph.slot_id("window_texture_view").unwrap())
.unwrap() .unwrap()
.as_texture_view(); .as_texture_view();
let encoder = context.encoder.as_mut().unwrap();
//context.queue.write_buffer(buffer, offset, data)
//let color_bg = graph.bind_group(graph.slot_id("color_buffer").unwrap());
//let color_bg = self.color_bg.as_ref().unwrap();
let color_bg = graph.bind_group(graph.bind_group_id("color_bg").unwrap()); let color_bg = graph.bind_group(graph.bind_group_id("color_bg").unwrap());
let encoder = context.encoder.as_mut().unwrap();
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("TrianglePass"), label: Some("TrianglePass"),
color_attachments: &[ color_attachments: &[

View File

@ -270,7 +270,7 @@ impl Renderer for BasicRenderer {
#[instrument(skip(self))] #[instrument(skip(self))]
fn render(&mut self) -> Result<(), wgpu::SurfaceError> { fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
self.graph.render(&self.device, &self.queue, &self.surface); self.graph.render(&self.surface);
Ok(()) Ok(())
} }

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, ops::Deref}; use std::ops::Deref;
use wgpu::PipelineLayout; use wgpu::PipelineLayout;