it works (magic)
This commit is contained in:
parent
efd73fce43
commit
107c43ee77
|
@ -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::<GraphRef>::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<GraphResourceId, (NodeIndex, Access)> = self
|
||||
.resources
|
||||
.keys()
|
||||
.filter_map(|id| self.accesses.get(id).map(|access| (*id, (root, *access))))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
let mut last_read: BTreeMap<GraphResourceId, (NodeIndex, Access)> = 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::<Vec<_>>();
|
||||
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<vk::ImageLayout>),
|
||||
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::<Vec<_>>();
|
||||
|
||||
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!");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue