From 107c43ee7718b63d91faf513733cdc33ccc85f55 Mon Sep 17 00:00:00 2001 From: Janis Date: Mon, 6 Jan 2025 04:40:20 +0100 Subject: [PATCH] it works (magic) --- crates/renderer/src/render_graph.rs | 357 ++++++++++------------------ 1 file changed, 121 insertions(+), 236 deletions(-) diff --git a/crates/renderer/src/render_graph.rs b/crates/renderer/src/render_graph.rs index 06d4e6b..a5618dd 100644 --- a/crates/renderer/src/render_graph.rs +++ b/crates/renderer/src/render_graph.rs @@ -15,6 +15,7 @@ use crate::{ SwapchainFrame, }; use ash::vk; +use bytemuck::Contiguous; use itertools::Itertools; use petgraph::{ graph::NodeIndex, @@ -391,6 +392,7 @@ impl RenderGraph { } } } + let now = std::time::Instant::now(); #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum PassNode { @@ -443,7 +445,7 @@ impl RenderGraph { } // gather references to resources. - let (references, intervals) = { + let (references, intervals) = util::timed("build reference and interval trees", || { let mut references = BTreeSet::::new(); // interval for each resource from (first pass referencing, last pass referencing) @@ -539,13 +541,10 @@ impl RenderGraph { .or_insert((PassNode::Last, PassNode::Last)); } - eprintln!("references: {references:#?}"); - eprintln!("intervals: {intervals:#?}"); - (references, intervals) - }; + }); - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] enum Barrier { Logical, Execution { @@ -587,7 +586,7 @@ impl RenderGraph { let pass_count = self.pass_descs.len() as u32; // build graph from references. - let mut dag = { + let mut dag = util::timed("construct dag", || { let mut edges = Vec::new(); intervals.iter().for_each(|(&rid, &(from, to))| { @@ -740,12 +739,6 @@ impl RenderGraph { } // make all writes available if !to_make_available.is_empty() { - tracing::debug!( - "making available {:?} for {:?} on {:?}", - to_make_available, - rid, - a.pass - ); edges.push(( (write, a.pass), ( @@ -836,9 +829,9 @@ impl RenderGraph { } }); - let mut dag = petgraph::graph::DiGraph::new(); - let root = dag.add_node(PassNode::First); - let output = dag.add_node(PassNode::Last); + let mut dag = petgraph::stable_graph::StableDiGraph::new(); + dag.add_node(PassNode::First); + dag.add_node(PassNode::Last); for i in 0..self.pass_descs.len() { dag.add_node(PassNode::Pass(i)); } @@ -864,41 +857,29 @@ impl RenderGraph { } } - #[cfg(any(debug_assertions, test))] - std::fs::write( - "render_graph2.dot", - &format!( - "{:?}", - petgraph::dot::Dot::with_attr_getters( - &dag, - &[], - &|_graph, edgeref| { - format!( - "label = \"{},{:#?}\"", - edgeref.weight().0.as_u32(), - edgeref.weight().1, - ) - }, - &|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) } - ) - ), - ) - .expect("writing render_graph repr"); + // #[cfg(any(debug_assertions, test))] + // std::fs::write( + // "render_graph2.dot", + // &format!( + // "{:?}", + // petgraph::dot::Dot::with_attr_getters( + // &dag, + // &[], + // &|_graph, edgeref| { + // format!( + // "label = \"{},{:#?}\"", + // edgeref.weight().0.as_u32(), + // edgeref.weight().1, + // ) + // }, + // &|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) } + // ) + // ), + // ) + // .expect("writing render_graph repr"); dag - }; - - let now = std::time::Instant::now(); - let mut dag = petgraph::stable_graph::StableDiGraph::new(); - - let root = dag.add_node(PassNode::First); - let mut last_write: BTreeMap = self - .resources - .keys() - .filter_map(|id| self.accesses.get(id).map(|access| (*id, (root, *access)))) - .collect::>(); - - let mut last_read: BTreeMap = BTreeMap::new(); + }); // TODO: rewrite finding edges properly. // finding out if this graph is cyclical is actually non-trivial @@ -906,178 +887,14 @@ impl RenderGraph { // this could be resolved by copying resource 1 before the write pass. // tl;dr: write-after-read makes this all more complicated - // insert edges between write->read edges of 2 passes - for (i, pass) in self.pass_descs.iter().enumerate() { - let node = dag.add_node(PassNode::Pass(i)); - - let mut read_accesses = BTreeMap::new(); - for (rid, access) in &pass.reads { - read_accesses - .entry(*rid) - .and_modify(|a| { - // a single pass must not read one image twice with different layouts - *a = *a | *access; - }) - .or_insert(*access); - } - - for (rid, after) in read_accesses { - if let Some(&(other, before)) = last_write.get(&rid) { - tracing::trace!("adding edge between {other:?} and {node:?} for {rid:?} with ({before:?} -> {after:?})"); - dag.add_edge(other, node, (rid, (before, after))); - } - last_read.insert(rid, (node, after)); - } - - let mut write_accesses = BTreeMap::new(); - for (rid, access) in &pass.writes { - write_accesses - .entry(*rid) - .and_modify(|a| { - // a single pass must not write one image twice with different layouts - *a = *a | *access; - }) - .or_insert(*access); - } - - for (rid, after) in write_accesses { - last_write.insert(rid, (node, after)); - if let Some(&(other, read)) = last_read.get(&rid) - && other != node - { - tracing::trace!("adding edge between {other:?} and {node:?} for {rid:?} with ({read:?} -> {after:?}) (WaR)"); - dag.add_edge(other, node, (rid, (read, after))); - } - } - } - - // pseudo pass for tracking outputs - let output = dag.add_node(PassNode::Last); - for (id, (node, access)) in self - .outputs - .iter() - .filter_map(|&id| last_write.get(&id).cloned().map(|node| (id, node))) - { - dag.add_edge( - node, - output, - ( - id, - ( - access, - // make output writes available - Access { - stage: vk::PipelineStageFlags2::NONE, - mask: vk::AccessFlags2::empty(), - ..access - }, - ), - ), - ); - } - - // prune dead nodes - loop { - let sinks = dag - .externals(petgraph::Direction::Outgoing) - .filter(|idx| idx != &output) - .collect::>(); - if sinks.is_empty() { - break; - } - for sink in sinks { - dag.remove_node(sink); - } - } - - // handle layout additional transitions - let edges = dag - .node_references() - .map(|(source, _)| { - let mut per_resourcelayout_multimap: BTreeMap< - (GraphResourceId, Option), - Vec<(Access, NodeIndex)>, - > = BTreeMap::new(); - let mut resources = BTreeSet::new(); - - dag.edges_directed(source, petgraph::Direction::Outgoing) - .for_each(|edge| { - let (rid, (_, after)) = edge.weight(); - let target = edge.target(); - let key = (*rid, after.layout); - let item = (*after, target); - resources.insert(*rid); - - per_resourcelayout_multimap - .entry(key) - .and_modify(|list| list.push(item)) - .or_insert(vec![item]); - }); - - let mut edges = vec![]; - for resource in resources { - for (a, b) in per_resourcelayout_multimap - .range( - (resource, None) - ..=(resource, Some(vk::ImageLayout::from_raw(i32::MAX))), - ) - .tuple_windows() - { - let a = a.1; - let b = b.1; - - // create new edge between all members of (a) and (b). - // topological mapping will fold all transitions into one. - for i in 0..a.len().max(b.len()) { - let from = a.get(i).unwrap_or(a.last().unwrap()); - let to = b.get(i).unwrap_or(b.last().unwrap()); - - let edge = ((from.1, to.1), (resource, (from.0, to.0))); - edges.push(edge); - } - } - } - - edges - }) - .flatten() - .collect::>(); - - for ((from, to), edge) in edges { - tracing::trace!( - "adding additional edge between {from:?} and {to:?} for {:?} with ({:?} -> {:?})", - edge.0, - edge.1 .0, - edge.1 .1 - ); - dag.add_edge(from, to, edge); - } - - // #[cfg(any(debug_assertions, test))] - // std::fs::write( - // "render_graph.dot", - // &format!( - // "{:?}", - // petgraph::dot::Dot::with_attr_getters( - // &dag, - // &[], - // &|_graph, edgeref| { format!("label = \"{:?}\"", edgeref.weight()) }, - // &|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) } - // ) - // ), - // ) - // .expect("writing render_graph repr"); - let mut topological_map = Vec::new(); - let mut top_dag = dag.clone(); - let mut root_barriers = BTreeMap::new(); // create topological map of DAG from sink to source loop { - let (sinks, passes): (Vec<_>, Vec<_>) = top_dag + let (sinks, passes): (Vec<_>, Vec<_>) = dag .externals(petgraph::Direction::Outgoing) - .filter(|&id| id != root) - .filter_map(|id| top_dag.node_weight(id).cloned().map(|idx| (id, idx))) + //.filter(|&id| id != root) + .filter_map(|id| dag.node_weight(id).cloned().map(|idx| (id, idx))) .unzip(); if sinks.is_empty() { @@ -1087,26 +904,95 @@ impl RenderGraph { let mut barriers = BTreeMap::new(); for &sink in &sinks { - top_dag - .edges_directed(sink, petgraph::Direction::Incoming) + dag.edges_directed(sink, petgraph::Direction::Incoming) .for_each(|edge| { - let (rid, (before, after)) = edge.weight(); + let (rid, barrier) = edge.weight(); - // initial access is transitioned at the beginning - // this affects imported resources only. - if edge.source() == root { - &mut root_barriers - } else { - &mut barriers + let before_and_after = match *barrier { + Barrier::Logical => None, + Barrier::Execution { src, dst } => Some(( + Access { + stage: src, + ..Access::empty() + }, + Access { + stage: dst, + ..Access::empty() + }, + )), + Barrier::LayoutTransition { + src: (src, from), + dst: (dst, to), + } => Some(( + Access { + stage: src, + layout: Some(from), + ..Access::empty() + }, + Access { + stage: dst, + layout: Some(to), + ..Access::empty() + }, + )), + Barrier::MakeAvailable { + src: (stage, mask), + dst, + } => Some(( + Access { + stage, + mask, + ..Access::empty() + }, + Access { + stage: dst, + ..Access::empty() + }, + )), + Barrier::MakeVisible { + src, + dst: (stage, mask), + } => Some(( + Access { + stage: src, + ..Access::empty() + }, + Access { + stage, + mask, + ..Access::empty() + }, + )), + Barrier::MemoryBarrier { + src: (src_stage, src_mask), + dst: (dst_stage, dst_mask), + } => Some(( + Access { + stage: src_stage, + mask: src_mask, + ..Access::empty() + }, + Access { + stage: dst_stage, + mask: dst_mask, + ..Access::empty() + }, + )), + }; + + if let Some((before, after)) = before_and_after { + // initial access is transitioned at the beginning + // this affects imported resources only. + barriers + .entry(*rid) + .and_modify(|(from, to)| { + *from = *from | before; + *to = *to | after; + }) + .or_insert((before, after)); } - .entry(*rid) - .and_modify(|(from, to)| { - *from = *from | *before; - *to = *to | *after; - }) - .or_insert((*before, *after)); }); - top_dag.remove_node(sink); + dag.remove_node(sink); } let passes = passes @@ -1123,13 +1009,12 @@ impl RenderGraph { topological_map.push((passes, barriers)); } - topological_map.push((vec![], root_barriers)); //tracing::debug!("mapping: {topological_map:#?}"); // I don't think this can currently happen with the way passes are added. - top_dag.remove_node(root); - if top_dag.node_count() > 0 { - eprintln!("dag: {top_dag:?}"); + dag.remove_node(0.into()); + if dag.node_count() > 0 { + eprintln!("dag: {dag:?}"); panic!("dag is cyclic!"); }