Implement a Render Graph #16
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
label: Some(&label),
|
label: Some(&label),
|
||||||
});
|
});
|
||||||
let mut context = RenderGraphContext::new(queue, Some(encoder));
|
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,
|
||||||
|
|
|
@ -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: &[
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, ops::Deref};
|
use std::ops::Deref;
|
||||||
|
|
||||||
use wgpu::PipelineLayout;
|
use wgpu::PipelineLayout;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue