Implement a Render Graph #16

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

View File

@ -1,9 +1,8 @@
mod node;
use std::{
cell::RefCell, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc
cell::{Ref, RefCell}, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc
};
use itertools::Itertools;
use lyra_ecs::World;
pub use node::*;
@ -34,6 +33,8 @@ pub trait RenderGraphLabel: Debug + 'static {
}
}
pub struct RenderGraphHash(u64);
#[derive(Clone)]
pub struct RenderGraphLabelValue(Rc<dyn RenderGraphLabel>);
@ -77,9 +78,10 @@ impl Eq for RenderGraphLabelValue {}
struct PassEntry {
inner: Arc<RefCell<dyn Node>>,
desc: Arc<NodeDesc>,
desc: Rc<RefCell<NodeDesc>>,
/// The index of the pass in the execution graph
graph_index: petgraph::matrix_graph::NodeIndex<usize>,
pipeline: Rc<RefCell<Option<PipelineResource>>>,
}
pub struct BindGroupEntry {
@ -116,20 +118,19 @@ pub struct RenderTarget {
pub struct RenderGraph {
device: Rc<wgpu::Device>,
queue: Rc<wgpu::Queue>,
slots: FxHashMap<u64, ResourcedSlot>,
slots: FxHashMap<RenderGraphLabelValue, ResourcedSlot>,
/// HashMap used to lookup the slot id using the label's hash
slot_label_lookup: FxHashMap<RenderGraphLabelValue, u64>,
passes: FxHashMap<u64, PassEntry>,
//slot_label_lookup: FxHashMap<RenderGraphLabelValue, u64>,
passes: FxHashMap<RenderGraphLabelValue, PassEntry>,
// TODO: Use a SlotMap
bind_groups: FxHashMap<u64, BindGroupEntry>,
bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>,
/// HashMap used to lookup the bind group id using the label's hash
bind_group_names: FxHashMap<RenderGraphLabelValue, u64>,
//bind_group_names: FxHashMap<RenderGraphLabelValue, u64>,
// TODO: make pipelines a `type` parameter in RenderPasses,
// then the pipelines can be retrieved via TypeId to the pass.
pipelines: FxHashMap<u64, PipelineResource>,
current_id: u64,
//pipelines: FxHashMap<u64, PipelineResource>,
/// A directed graph describing the execution path of the RenderGraph
execution_graph: petgraph::matrix_graph::DiMatrix<u64, (), Option<()>, usize>,
execution_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
}
impl RenderGraph {
@ -138,12 +139,8 @@ impl RenderGraph {
device,
queue,
slots: Default::default(),
slot_label_lookup: Default::default(),
passes: Default::default(),
bind_groups: Default::default(),
bind_group_names: Default::default(),
pipelines: Default::default(),
current_id: 1,
execution_graph: Default::default(),
}
}
@ -152,19 +149,6 @@ impl RenderGraph {
&*self.device
}
pub fn next_id(&mut self) -> u64 {
self.current_id += 1;
self.current_id
}
pub(crate) fn slot_id_rc(&self, label: &RenderGraphLabelValue) -> Option<u64> {
self.slot_label_lookup.get(&label.clone().into()).cloned()
}
pub fn slot_id(&self, label: &dyn RenderGraphLabel) -> Option<u64> {
self.slot_label_lookup.get(&label.rc_clone().into()).cloned()
}
/// Add a [`Node`] to the RenderGraph.
///
/// When the node is added, its [`Node::desc`] method will be executed.
@ -176,34 +160,35 @@ impl RenderGraph {
/// not change. The IDs of output slots do stay the same.
/// 3. Ensuring that no two slots share the same ID when the names do not match.
#[instrument(skip(self, pass), level = "debug")]
pub fn add_pass<P: Node>(&mut self, mut pass: P) {
pub fn add_pass<P: Node>(&mut self, label: impl RenderGraphLabel, mut pass: P) {
let mut desc = pass.desc(self);
// collect all the slots of the pass
for slot in &mut desc.slots {
if let Some((id, other)) = self
.slot_label_lookup
.get(&slot.label)
.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
if let Some(other) = self
.slots
.get_mut(&slot.label)
//.map(|s| (id, s))
//.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
{
debug_assert_eq!(
slot.ty, other.ty,
"slot {:?} in pass {:?} does not match existing slot of same name",
slot.label, desc.label
slot.label, label
);
trace!(
/* trace!(
"Found existing slot for {:?}, changing id to {}",
slot.label,
id
);
); */
// if there is a slot of the same name
slot.id = *id;
//slot.id = *id;
} else {
debug_assert!(!self.slots.contains_key(&slot.id),
debug_assert!(!self.slots.contains_key(&slot.label),
"Reuse of id detected in render graph! Pass: {:?}, slot: {:?}",
desc.label, slot.label,
label, slot.label,
);
let res_slot = ResourcedSlot {
@ -212,33 +197,29 @@ impl RenderGraph {
value: slot.value.clone().unwrap_or(SlotValue::None),
};
self.slots.insert(slot.id, res_slot);
self.slot_label_lookup.insert(slot.label.clone(), slot.id);
self.slots.insert(slot.label.clone(), res_slot);
}
}
// get clones of the bind groups and layouts
for (label, bg, bgl) in &desc.bind_groups {
let bg_id = self.next_id();
self.bind_groups.insert(
bg_id,
BindGroupEntry {
label: label.clone(),
bg: bg.clone(),
layout: bgl.clone(),
},
);
self.bind_group_names.insert(label.clone().into(), bg_id);
self.bind_groups.insert(label.clone(), BindGroupEntry {
label: label.clone(),
bg: bg.clone(),
layout: bgl.clone(),
});
}
let index = self.execution_graph.add_node(desc.id);
let label: RenderGraphLabelValue = label.into();
let index = self.execution_graph.add_node(label.clone());
self.passes.insert(
desc.id,
label,
PassEntry {
inner: Arc::new(RefCell::new(pass)),
desc: Arc::new(desc),
desc: Rc::new(RefCell::new(desc)),
graph_index: index,
pipeline: Rc::new(RefCell::new(None)),
},
);
}
@ -250,9 +231,10 @@ impl RenderGraph {
#[instrument(skip(self, device))]
pub fn setup(&mut self, device: &wgpu::Device) {
// For all passes, create their pipelines
for pass in self.passes.values() {
if let Some(pipeline_desc) = &pass.desc.pipeline_desc {
let pipeline = match pass.desc.ty {
for pass in self.passes.values_mut() {
let desc = (*pass.desc).borrow();
if let Some(pipeline_desc) = &desc.pipeline_desc {
let pipeline = match desc.ty {
NodeType::Render => Pipeline::Render(RenderPipeline::create(
device,
pipeline_desc
@ -270,11 +252,14 @@ impl RenderGraph {
}
};
drop(desc);
let res = PipelineResource {
pipeline,
bg_layout_name_lookup: Default::default(),
};
self.pipelines.insert(pass.desc.id, res);
let mut pipeline = pass.pipeline.borrow_mut();
*pipeline = Some(res);
}
}
}
@ -282,10 +267,16 @@ impl RenderGraph {
#[instrument(skip(self, world))]
pub fn prepare(&mut self, world: &mut World) {
// prepare all passes
let mut context = RenderGraphContext::new(&self.device, &self.queue, None);
for (_, pass) in &mut self.passes {
let mut buffer_writes = VecDeque::<GraphBufferWrite>::new();
// reserve some buffer writes. not all nodes write so half the amount of them is probably
// fine.
buffer_writes.reserve(self.passes.len() / 2);
for (label, pass) in &mut self.passes {
let mut context = RenderGraphContext::new(&self.device, &self.queue, None, label.clone());
let mut inner = pass.inner.borrow_mut();
inner.prepare(world, &mut context);
buffer_writes.append(&mut context.buffer_writes);
}
{
@ -293,10 +284,10 @@ impl RenderGraph {
let s = debug_span!("queue_buffer_writes");
let _e = s.enter();
while let Some(bufwr) = context.buffer_writes.pop_front() {
while let Some(bufwr) = buffer_writes.pop_front() {
let slot = self
.slots
.get(&self.slot_id_rc(&bufwr.target_slot).unwrap())
.get(&bufwr.target_slot)
.expect(&format!(
"Failed to find slot '{:?}' for buffer write",
bufwr.target_slot
@ -313,23 +304,22 @@ impl RenderGraph {
#[instrument(skip(self))]
pub fn render(&mut self) {
let mut sorted: VecDeque<u64> = petgraph::algo::toposort(&self.execution_graph, None)
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.execution_graph, None)
.expect("RenderGraph had cycled!")
.iter()
.map(|i| self.execution_graph[i.clone()])
.map(|i| self.execution_graph[i.clone()].clone())
.collect();
let path_names = sorted
.iter()
.map(|i| self.pass(*i).unwrap().label.clone())
.collect_vec();
trace!("Render graph execution order: {:?}", path_names);
//debug!("Render graph execution order: {:?}", sorted);
let mut encoders = Vec::with_capacity(self.passes.len() / 2);
while let Some(pass_id) = sorted.pop_front() {
let pass = self.passes.get(&pass_id).unwrap();
while let Some(pass_label) = sorted.pop_front() {
let pass = self.passes.get(&pass_label).unwrap();
let pass_inn = pass.inner.clone();
let pass_desc = pass.desc.clone();
let label = format!("{:?} Encoder", pass_desc.label);
let pass_desc = (*pass_desc).borrow();
let label = format!("{:?} Encoder", pass_label.0);
// encoders are not needed for presenter nodes.
let encoder = if pass_desc.ty.should_have_pipeline() {
@ -346,7 +336,7 @@ impl RenderGraph {
// clone of the Rc's is required to appease the borrow checker
let device = self.device.clone();
let queue = self.queue.clone();
let mut context = RenderGraphContext::new(&device, &queue, encoder);
let mut context = RenderGraphContext::new(&device, &queue, encoder, pass_label.clone());
// all encoders need to be submitted before a presenter node is executed.
if pass_desc.ty == NodeType::Presenter {
@ -354,9 +344,9 @@ impl RenderGraph {
self.queue.submit(encoders.drain(..));
}
trace!("Executing {:?}", pass_desc.label);
trace!("Executing {:?}", pass_label.0);
let mut inner = pass_inn.borrow_mut();
inner.execute(self, &*pass_desc, &mut context);
inner.execute(self, &pass_desc, &mut context);
if let Some(encoder) = context.encoder {
encoders.push(encoder.finish());
@ -374,49 +364,52 @@ impl RenderGraph {
}
}
pub fn slot_value(&self, id: u64) -> Option<&SlotValue> {
self.slots.get(&id).map(|s| &s.value)
pub fn slot_value<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&SlotValue> {
self.slots.get(&label.into()).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 slot_value_mut<L: Into<RenderGraphLabelValue>>(&mut self, label: L) -> Option<&mut SlotValue> {
self.slots.get_mut(&label.into()).map(|s| &mut s.value)
}
pub fn pass(&self, id: u64) -> Option<&NodeDesc> {
self.passes.get(&id).map(|s| &*s.desc)
pub fn node_desc<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<Ref<NodeDesc>> {
self.passes.get(&label.into()).map(|s| (*s.desc).borrow())
}
#[inline(always)]
pub fn pipeline(&self, id: u64) -> &Pipeline {
&self.pipelines.get(&id).unwrap().pipeline
pub fn pipeline<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<Ref<Pipeline>> {
self.passes.get(&label.into())
.and_then(|p| {
let v = p.pipeline.borrow();
match &*v {
Some(_) => Some(Ref::map(v, |p| &p.as_ref().unwrap().pipeline)),
None => None,
}
})
}
#[inline(always)]
pub fn try_bind_group(&self, id: u64) -> Option<&Rc<wgpu::BindGroup>> {
self.bind_groups.get(&id).map(|e| &e.bg)
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroup>> {
self.bind_groups.get(&label.into()).map(|e| &e.bg)
}
#[inline(always)]
pub fn bind_group(&self, id: u64) -> &Rc<wgpu::BindGroup> {
self.try_bind_group(id).expect("Unknown id for bind group")
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroup> {
self.try_bind_group(label).expect("Unknown id for bind group")
}
#[inline(always)]
pub fn try_bind_group_layout(&self, id: u64) -> Option<&Rc<wgpu::BindGroupLayout>> {
self.bind_groups.get(&id).and_then(|e| e.layout.as_ref())
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroupLayout>> {
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref())
}
#[inline(always)]
pub fn bind_group_layout(&self, id: u64) -> &Rc<wgpu::BindGroupLayout> {
self.try_bind_group_layout(id)
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> {
self.try_bind_group_layout(label)
.expect("Unknown id for bind group layout")
}
#[inline(always)]
pub fn bind_group_id(&self, label: &dyn RenderGraphLabel) -> Option<u64> {
self.bind_group_names.get(&label.rc_clone().into()).copied()
}
pub fn add_edge(&mut self, from: impl RenderGraphLabel, to: impl RenderGraphLabel)
{
let from = RenderGraphLabelValue::from(from);
@ -425,13 +418,13 @@ impl RenderGraph {
let from_idx = self
.passes
.iter()
.find(|p| p.1.desc.label == from)
.find(|p| *p.0 == from)
.map(|p| p.1.graph_index)
.expect("Failed to find from pass");
let to_idx = self
.passes
.iter()
.find(|p| p.1.desc.label == to)
.find(|p| *p.0 == to)
.map(|p| p.1.graph_index)
.expect("Failed to find to pass");
@ -471,9 +464,8 @@ impl RenderGraph {
) {
for (label, index) in bind_groups {
let bg = self
.bind_group_id(*label)
.map(|bgi| self.bind_group(bgi))
.expect(&format!("Could not find bind group '{:?}'", label));
.bind_group(label.rc_clone());
//.expect(&format!("Could not find bind group '{:?}'", label));
pass.set_bind_group(*index, bg, &[]);
}
@ -490,22 +482,28 @@ pub(crate) struct GraphBufferWrite {
#[allow(dead_code)]
pub struct RenderGraphContext<'a> {
/// Becomes None when the encoder is submitted
pub(crate) encoder: Option<wgpu::CommandEncoder>,
pub(crate) device: &'a wgpu::Device,
pub(crate) queue: &'a wgpu::Queue,
/// The [`wgpu::CommandEncoder`] used to encode GPU operations.
///
/// This is `None` during the `prepare` stage.
pub encoder: Option<wgpu::CommandEncoder>,
/// The gpu device that is being used.
pub device: &'a wgpu::Device,
pub queue: &'a wgpu::Queue,
pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
/// The label of this Node.
pub label: RenderGraphLabelValue,
}
impl<'a> RenderGraphContext<'a> {
pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>) -> Self {
pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>, label: RenderGraphLabelValue) -> Self {
Self {
encoder,
device,
queue,
buffer_writes: Default::default(),
renderpass_desc: vec![],
label,
}
}

View File

@ -1,4 +1,4 @@
use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, num::NonZeroU32, rc::Rc};
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc};
use bind_match::bind_match;
use lyra_ecs::World;
@ -97,8 +97,6 @@ pub struct NodeSlot {
pub ty: SlotType,
/// The way this slot uses the value. Defines if this slot is an output or input.
pub attribute: SlotAttribute,
// What if these are just completely removed and labels are used everywhere instead?
pub id: u64,
/// The identifying label of this slot.
pub label: RenderGraphLabelValue,
/// The value of the slot.
@ -163,15 +161,12 @@ impl RenderGraphPipelineInfo {
/// Descriptor of a Node in a [`RenderGraph`].
pub struct NodeDesc {
pub id: u64,
/// The label of the Node, used to identify the Node.
pub label: RenderGraphLabelValue,
/// The [`NodeType`] of the node.
pub ty: NodeType,
/// The slots that the Node uses.
/// This defines the resources that the node uses and creates in the graph.
pub slots: Vec<NodeSlot>,
slot_label_lookup: HashMap<RenderGraphLabelValue, u64>,
//slot_label_lookup: HashMap<RenderGraphLabelValue, u64>,
/// An optional pipeline descriptor for the Node.
/// This is `None` if the Node type is not a node that requires a pipeline
/// (see [`NodeType::should_have_pipeline`]).
@ -187,19 +182,14 @@ pub struct NodeDesc {
impl NodeDesc {
/// Create a new node descriptor.
pub fn new<L: RenderGraphLabel>(
id: u64,
label: L,
pub fn new(
pass_type: NodeType,
pipeline_desc: Option<PipelineDescriptor>,
bind_groups: Vec<(&dyn RenderGraphLabel, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
) -> Self {
Self {
id,
label: label.into(),
ty: pass_type,
slots: vec![],
slot_label_lookup: HashMap::default(),
pipeline_desc,
bind_groups: bind_groups
.into_iter()
@ -218,7 +208,6 @@ impl NodeDesc {
"input slots should not have values"
);
self.slot_label_lookup.insert(slot.label.clone().into(), slot.id);
self.slots.push(slot);
}
@ -230,7 +219,6 @@ impl NodeDesc {
#[inline(always)]
pub fn add_buffer_slot<L: RenderGraphLabel>(
&mut self,
id: u64,
label: L,
attribute: SlotAttribute,
value: Option<SlotValue>,
@ -241,7 +229,6 @@ impl NodeDesc {
);
let slot = NodeSlot {
id,
label: label.into(),
ty: SlotType::Buffer,
attribute,
@ -258,7 +245,6 @@ impl NodeDesc {
#[inline(always)]
pub fn add_texture_slot<L: RenderGraphLabel>(
&mut self,
id: u64,
label: L,
attribute: SlotAttribute,
value: Option<SlotValue>,
@ -269,7 +255,6 @@ impl NodeDesc {
);
let slot = NodeSlot {
id,
label: label.into(),
ty: SlotType::Texture,
attribute,
@ -286,7 +271,6 @@ impl NodeDesc {
#[inline(always)]
pub fn add_texture_view_slot<L: RenderGraphLabel>(
&mut self,
id: u64,
label: L,
attribute: SlotAttribute,
value: Option<SlotValue>,
@ -297,7 +281,6 @@ impl NodeDesc {
);
let slot = NodeSlot {
id,
label: label.into(),
ty: SlotType::TextureView,
attribute,
@ -314,7 +297,6 @@ impl NodeDesc {
#[inline(always)]
pub fn add_sampler_slot<L: RenderGraphLabel>(
&mut self,
id: u64,
label: L,
attribute: SlotAttribute,
value: Option<SlotValue>,
@ -325,7 +307,6 @@ impl NodeDesc {
);
let slot = NodeSlot {
id,
label: label.into(),
ty: SlotType::Sampler,
attribute,
@ -341,7 +322,7 @@ impl NodeDesc {
.filter(|s| s.attribute == SlotAttribute::Input)
.collect()
}
/// Returns all output slots that the descriptor defines.
pub fn output_slots(&self) -> Vec<&NodeSlot> {
self.slots

View File

@ -40,8 +40,6 @@ pub struct BasePass {
/// This should be Some when the pass is first created then after its added to
/// the render graph it will be None and stay None.
temp_render_target: Option<RenderTarget>,
main_rt_id: u64,
window_tv_id: u64,
screen_size: glam::UVec2,
}
@ -102,8 +100,6 @@ impl Node for BasePass {
let depth_texture_view = Rc::new(depth_texture.view);
let mut desc = NodeDesc::new(
graph.next_id(),
BasePassLabel,
NodeType::Node,
None,
vec![
@ -119,37 +115,30 @@ impl Node for BasePass {
],
);
self.main_rt_id = graph.next_id();
desc.add_slot(NodeSlot {
ty: SlotType::RenderTarget,
attribute: SlotAttribute::Output,
id: self.main_rt_id,
label: BasePassSlots::MainRenderTarget.into(),
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(
render_target,
)))),
});
self.window_tv_id = graph.next_id();
desc.add_texture_view_slot(
self.window_tv_id,
BasePassSlots::WindowTextureView,
SlotAttribute::Output,
Some(SlotValue::Lazy),
);
desc.add_texture_view_slot(
graph.next_id(),
BasePassSlots::DepthTextureView,
SlotAttribute::Output,
Some(SlotValue::TextureView(depth_texture_view)),
);
desc.add_buffer_slot(
graph.next_id(),
BasePassSlots::ScreenSize,
SlotAttribute::Output,
Some(SlotValue::Buffer(Rc::new(screen_size_buf))),
);
desc.add_buffer_slot(
graph.next_id(),
BasePassSlots::Camera,
SlotAttribute::Output,
Some(SlotValue::Buffer(Rc::new(camera_buf))),
@ -177,7 +166,7 @@ impl Node for BasePass {
context: &mut crate::render::graph::RenderGraphContext,
) {
let tv_slot = graph
.slot_value_mut(self.main_rt_id)
.slot_value_mut(BasePassSlots::MainRenderTarget)
.expect("somehow the main render target slot is missing");
let mut rt = tv_slot.as_render_target_mut().unwrap();
debug_assert!(
@ -203,7 +192,7 @@ impl Node for BasePass {
// store the surface texture to the slot
let tv_slot = graph
.slot_value_mut(self.window_tv_id)
.slot_value_mut(BasePassSlots::WindowTextureView)
.expect("somehow the window texture view slot is missing");
*tv_slot = SlotValue::TextureView(Rc::new(view));
}

View File

@ -40,8 +40,6 @@ impl Node for LightBasePass {
let light_buffers = self.light_buffers.as_ref().unwrap();
let mut desc = NodeDesc::new(
graph.next_id(),
LightBasePassLabel,
NodeType::Node,
None,
vec![(
@ -52,7 +50,6 @@ impl Node for LightBasePass {
);
desc.add_buffer_slot(
graph.next_id(),
LightBasePassSlots::Lights,
SlotAttribute::Output,
Some(SlotValue::Buffer(light_buffers.buffer.clone())),

View File

@ -49,8 +49,7 @@ impl Node for LightCullComputePass {
// get the size of the work group for the grid
let main_rt = graph
.slot_id(&BasePassSlots::MainRenderTarget)
.and_then(|s| graph.slot_value(s))
.slot_value(BasePassSlots::MainRenderTarget)
.and_then(|s| s.as_render_target())
.expect("missing main render target");
self.workgroup_size =
@ -169,16 +168,13 @@ impl Node for LightCullComputePass {
}));
drop(main_rt);
let pass_id = graph.next_id();
let depth_tex_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::DepthTexture).unwrap());
let camera_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::Camera).unwrap());
let lights_bgl = graph.bind_group_layout(graph.bind_group_id(&LightBasePassSlots::Lights).unwrap());
let screen_size_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::ScreenSize).unwrap());
let depth_tex_bgl = graph.bind_group_layout(BasePassSlots::DepthTexture);
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize);
let mut desc = NodeDesc::new(
pass_id,
LightCullComputePassLabel,
NodeType::Compute,
Some(PipelineDescriptor::Compute(ComputePipelineDescriptor {
label: Some("light_cull_pipeline".into()),
@ -201,20 +197,17 @@ impl Node for LightCullComputePass {
);
desc.add_texture_view_slot(
graph.next_id(),
BasePassSlots::WindowTextureView,
SlotAttribute::Input,
None,
);
desc.add_buffer_slot(
graph.next_id(),
BasePassSlots::ScreenSize,
SlotAttribute::Input,
None,
);
desc.add_buffer_slot(graph.next_id(), BasePassSlots::Camera, SlotAttribute::Input, None);
desc.add_buffer_slot(BasePassSlots::Camera, SlotAttribute::Input, None);
desc.add_buffer_slot(
graph.next_id(),
LightCullComputePassSlots::IndexCounterBuffer,
SlotAttribute::Output,
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))),
@ -228,27 +221,20 @@ impl Node for LightCullComputePass {
fn execute(
&mut self,
graph: &mut crate::render::graph::RenderGraph,
desc: &crate::render::graph::NodeDesc,
_: &crate::render::graph::NodeDesc,
context: &mut RenderGraphContext,
) {
let label = context.label.clone();
let pipeline = graph.pipeline(label)
.expect("Failed to find Pipeline for LightCullComputePass");
let pipeline = pipeline.as_compute();
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("light_cull_pass"),
});
let pipeline = graph.pipeline(desc.id);
pass.set_pipeline(pipeline.as_compute());
/* let depth_tex_bg = graph.bind_group(graph.bind_group_id("depth_texture").unwrap());
let camera_bg = graph.bind_group(graph.bind_group_id("camera").unwrap());
let lights_bg = graph.bind_group(graph.bind_group_id("light_buffers").unwrap());
let grid_bg = graph.bind_group(graph.bind_group_id("light_indices_grid").unwrap());
let screen_size_bg = graph.bind_group(graph.bind_group_id("screen_size").unwrap());
pass.set_bind_group(0, depth_tex_bg, &[]);
pass.set_bind_group(1, camera_bg, &[]);
pass.set_bind_group(2, lights_bg, &[]);
pass.set_bind_group(3, grid_bg, &[]);
pass.set_bind_group(4, screen_size_bg, &[]); */
pass.set_pipeline(pipeline);
graph.set_bind_groups(
&mut pass,

View File

@ -232,21 +232,16 @@ impl Node for MeshPass {
self.default_texture = Some(RenderTexture::from_bytes(&device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap());
// get surface config format
let main_rt = graph.slot_id(&BasePassSlots::MainRenderTarget)
.and_then(|s| graph.slot_value(s))
let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget)
.and_then(|s| s.as_render_target())
.expect("missing main render target");
let surface_config_format = main_rt.surface_config.format;
drop(main_rt);
// get the id here to make borrow checker happy
let pass_id = graph.next_id();
let camera_bgl = graph.bind_group_layout(graph.bind_group_id(&BasePassSlots::Camera).unwrap());
let lights_bgl = graph.bind_group_layout(graph.bind_group_id(&LightBasePassSlots::Lights).unwrap());
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
let light_grid_bgl = graph
.bind_group_layout(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup)
.expect("Missing light grid bind group"));
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
let shader = Rc::new(Shader {
label: Some("base_shader".into()),
@ -254,8 +249,6 @@ impl Node for MeshPass {
});
let desc = NodeDesc::new(
pass_id,
MeshesPassLabel,
NodeType::Render,
Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
label: Some("meshes".into()),
@ -445,38 +438,37 @@ impl Node for MeshPass {
fn execute(
&mut self,
graph: &mut crate::render::graph::RenderGraph,
desc: &crate::render::graph::NodeDesc,
_: &crate::render::graph::NodeDesc,
context: &mut crate::render::graph::RenderGraphContext,
) {
let encoder = context.encoder.as_mut().unwrap();
let view = graph
.slot_value(graph.slot_id(&BasePassSlots::WindowTextureView).unwrap())
.slot_value(BasePassSlots::WindowTextureView)
.unwrap()
.as_texture_view()
.expect("BasePassSlots::WindowTextureView was not a TextureView slot");
let depth_view = graph
.slot_value(graph.slot_id(&BasePassSlots::DepthTextureView).unwrap())
.slot_value(BasePassSlots::DepthTextureView)
.unwrap()
.as_texture_view()
.expect("BasePassSlots::DepthTextureView was not a TextureView slot");
let camera_bg = graph
.bind_group(graph.bind_group_id(&BasePassSlots::Camera)
.expect("Missing camera bind group"));
.bind_group(BasePassSlots::Camera);
let lights_bg = graph
.bind_group(graph.bind_group_id(&LightBasePassSlots::Lights)
.expect("Missing lights bind group"));
.bind_group(LightBasePassSlots::Lights);
let light_grid_bg = graph
.bind_group(graph.bind_group_id(&LightCullComputePassSlots::LightIndicesGridGroup)
.expect("Missing light grid bind group"));
.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
let material_bg = graph
.bind_group(graph.bind_group_id(&MeshesPassSlots::Material)
.expect("Missing material bind group"));
.bind_group(MeshesPassSlots::Material);
let pipeline = graph.pipeline(context.label.clone())
.expect("Failed to find pipeline for MeshPass");
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
@ -504,7 +496,6 @@ impl Node for MeshPass {
}),
});
let pipeline = graph.pipeline(desc.id);
pass.set_pipeline(&pipeline.as_render());
//let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup();

View File

@ -18,8 +18,7 @@ impl PresentPassLabel {
/// screen size buffer, camera buffer,
pub struct PresentPass {
/// Label of this pass
label: PresentPassLabel,
//render_target_slot: Rc<dyn RenderGraphLabel>,
pub label: PresentPassLabel,
}
impl PresentPass {
@ -32,10 +31,8 @@ impl PresentPass {
}
impl Node for PresentPass {
fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc {
fn desc(&mut self, _graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc {
let mut desc = NodeDesc::new(
graph.next_id(),
self.label.clone(),
NodeType::Presenter,
None,
vec![],
@ -45,7 +42,6 @@ impl Node for PresentPass {
NodeSlot {
ty: SlotType::RenderTarget,
attribute: SlotAttribute::Input,
id: graph.next_id(),
label: self.label.0.clone(),
value: None,
}
@ -59,9 +55,9 @@ impl Node for PresentPass {
}
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::NodeDesc, _context: &mut crate::render::graph::RenderGraphContext) {
let id = graph.slot_id_rc(&self.label.0)
.expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0));
let mut slot = graph.slot_value_mut(id).unwrap().as_render_target_mut().unwrap();
let mut slot = graph.slot_value_mut(self.label.0.clone())
.expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0))
.as_render_target_mut().unwrap();
let surf_tex = slot.current_texture.take().unwrap();
surf_tex.present();
}

View File

@ -125,19 +125,20 @@ impl BasicRenderer {
let mut g = RenderGraph::new(device.clone(), queue.clone());
debug!("Adding base pass");
g.add_pass(BasePass::new(surface, config));
g.add_pass(BasePassLabel, BasePass::new(surface, config));
debug!("Adding light base pass");
g.add_pass(LightBasePass::new());
g.add_pass(LightBasePassLabel, LightBasePass::new());
debug!("Adding light cull compute pass");
g.add_pass(LightCullComputePass::new(size));
g.add_pass(LightCullComputePassLabel, LightCullComputePass::new(size));
//debug!("Adding triangle pass");
//g.add_pass(TrianglePass::new());
debug!("Adding mesh pass");
g.add_pass(MeshPass::new());
g.add_pass(MeshesPassLabel, MeshPass::new());
debug!("Adding present pass");
g.add_pass(PresentPass::new(BasePassSlots::MainRenderTarget));
let p = PresentPass::new(BasePassSlots::MainRenderTarget);
g.add_pass(p.label.clone(), p);
g.add_edge(BasePassLabel, LightBasePassLabel);
g.add_edge(LightBasePassLabel, LightCullComputePassLabel);
@ -188,7 +189,7 @@ impl Renderer for BasicRenderer {
self.size = new_size;
// update surface config and the surface
let mut rt = self.graph.slot_value_mut(self.graph.slot_id(&BasePassSlots::MainRenderTarget).unwrap())
let mut rt = self.graph.slot_value_mut(BasePassSlots::MainRenderTarget)
.unwrap().as_render_target_mut().unwrap();
rt.surface_config.width = new_size.width;
rt.surface_config.height = new_size.height;