it works (magic)
This commit is contained in:
parent
efd73fce43
commit
107c43ee77
|
@ -15,6 +15,7 @@ use crate::{
|
||||||
SwapchainFrame,
|
SwapchainFrame,
|
||||||
};
|
};
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use bytemuck::Contiguous;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use petgraph::{
|
use petgraph::{
|
||||||
graph::NodeIndex,
|
graph::NodeIndex,
|
||||||
|
@ -391,6 +392,7 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum PassNode {
|
enum PassNode {
|
||||||
|
@ -443,7 +445,7 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather references to resources.
|
// gather references to resources.
|
||||||
let (references, intervals) = {
|
let (references, intervals) = util::timed("build reference and interval trees", || {
|
||||||
let mut references = BTreeSet::<GraphRef>::new();
|
let mut references = BTreeSet::<GraphRef>::new();
|
||||||
|
|
||||||
// interval for each resource from (first pass referencing, last pass referencing)
|
// interval for each resource from (first pass referencing, last pass referencing)
|
||||||
|
@ -539,13 +541,10 @@ impl RenderGraph {
|
||||||
.or_insert((PassNode::Last, PassNode::Last));
|
.or_insert((PassNode::Last, PassNode::Last));
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("references: {references:#?}");
|
|
||||||
eprintln!("intervals: {intervals:#?}");
|
|
||||||
|
|
||||||
(references, intervals)
|
(references, intervals)
|
||||||
};
|
});
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum Barrier {
|
enum Barrier {
|
||||||
Logical,
|
Logical,
|
||||||
Execution {
|
Execution {
|
||||||
|
@ -587,7 +586,7 @@ impl RenderGraph {
|
||||||
let pass_count = self.pass_descs.len() as u32;
|
let pass_count = self.pass_descs.len() as u32;
|
||||||
|
|
||||||
// build graph from references.
|
// build graph from references.
|
||||||
let mut dag = {
|
let mut dag = util::timed("construct dag", || {
|
||||||
let mut edges = Vec::new();
|
let mut edges = Vec::new();
|
||||||
|
|
||||||
intervals.iter().for_each(|(&rid, &(from, to))| {
|
intervals.iter().for_each(|(&rid, &(from, to))| {
|
||||||
|
@ -740,12 +739,6 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
// make all writes available
|
// make all writes available
|
||||||
if !to_make_available.is_empty() {
|
if !to_make_available.is_empty() {
|
||||||
tracing::debug!(
|
|
||||||
"making available {:?} for {:?} on {:?}",
|
|
||||||
to_make_available,
|
|
||||||
rid,
|
|
||||||
a.pass
|
|
||||||
);
|
|
||||||
edges.push((
|
edges.push((
|
||||||
(write, a.pass),
|
(write, a.pass),
|
||||||
(
|
(
|
||||||
|
@ -836,9 +829,9 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut dag = petgraph::graph::DiGraph::new();
|
let mut dag = petgraph::stable_graph::StableDiGraph::new();
|
||||||
let root = dag.add_node(PassNode::First);
|
dag.add_node(PassNode::First);
|
||||||
let output = dag.add_node(PassNode::Last);
|
dag.add_node(PassNode::Last);
|
||||||
for i in 0..self.pass_descs.len() {
|
for i in 0..self.pass_descs.len() {
|
||||||
dag.add_node(PassNode::Pass(i));
|
dag.add_node(PassNode::Pass(i));
|
||||||
}
|
}
|
||||||
|
@ -864,41 +857,29 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(debug_assertions, test))]
|
// #[cfg(any(debug_assertions, test))]
|
||||||
std::fs::write(
|
// std::fs::write(
|
||||||
"render_graph2.dot",
|
// "render_graph2.dot",
|
||||||
&format!(
|
// &format!(
|
||||||
"{:?}",
|
// "{:?}",
|
||||||
petgraph::dot::Dot::with_attr_getters(
|
// petgraph::dot::Dot::with_attr_getters(
|
||||||
&dag,
|
// &dag,
|
||||||
&[],
|
// &[],
|
||||||
&|_graph, edgeref| {
|
// &|_graph, edgeref| {
|
||||||
format!(
|
// format!(
|
||||||
"label = \"{},{:#?}\"",
|
// "label = \"{},{:#?}\"",
|
||||||
edgeref.weight().0.as_u32(),
|
// edgeref.weight().0.as_u32(),
|
||||||
edgeref.weight().1,
|
// edgeref.weight().1,
|
||||||
)
|
// )
|
||||||
},
|
// },
|
||||||
&|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) }
|
// &|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) }
|
||||||
)
|
// )
|
||||||
),
|
// ),
|
||||||
)
|
// )
|
||||||
.expect("writing render_graph repr");
|
// .expect("writing render_graph repr");
|
||||||
|
|
||||||
dag
|
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.
|
// TODO: rewrite finding edges properly.
|
||||||
// finding out if this graph is cyclical is actually non-trivial
|
// 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.
|
// this could be resolved by copying resource 1 before the write pass.
|
||||||
// tl;dr: write-after-read makes this all more complicated
|
// 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 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
|
// create topological map of DAG from sink to source
|
||||||
loop {
|
loop {
|
||||||
let (sinks, passes): (Vec<_>, Vec<_>) = top_dag
|
let (sinks, passes): (Vec<_>, Vec<_>) = dag
|
||||||
.externals(petgraph::Direction::Outgoing)
|
.externals(petgraph::Direction::Outgoing)
|
||||||
.filter(|&id| id != root)
|
//.filter(|&id| id != root)
|
||||||
.filter_map(|id| top_dag.node_weight(id).cloned().map(|idx| (id, idx)))
|
.filter_map(|id| dag.node_weight(id).cloned().map(|idx| (id, idx)))
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
if sinks.is_empty() {
|
if sinks.is_empty() {
|
||||||
|
@ -1087,26 +904,95 @@ impl RenderGraph {
|
||||||
let mut barriers = BTreeMap::new();
|
let mut barriers = BTreeMap::new();
|
||||||
|
|
||||||
for &sink in &sinks {
|
for &sink in &sinks {
|
||||||
top_dag
|
dag.edges_directed(sink, petgraph::Direction::Incoming)
|
||||||
.edges_directed(sink, petgraph::Direction::Incoming)
|
|
||||||
.for_each(|edge| {
|
.for_each(|edge| {
|
||||||
let (rid, (before, after)) = edge.weight();
|
let (rid, barrier) = edge.weight();
|
||||||
|
|
||||||
|
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
|
// initial access is transitioned at the beginning
|
||||||
// this affects imported resources only.
|
// this affects imported resources only.
|
||||||
if edge.source() == root {
|
barriers
|
||||||
&mut root_barriers
|
|
||||||
} else {
|
|
||||||
&mut barriers
|
|
||||||
}
|
|
||||||
.entry(*rid)
|
.entry(*rid)
|
||||||
.and_modify(|(from, to)| {
|
.and_modify(|(from, to)| {
|
||||||
*from = *from | *before;
|
*from = *from | before;
|
||||||
*to = *to | *after;
|
*to = *to | after;
|
||||||
})
|
})
|
||||||
.or_insert((*before, *after));
|
.or_insert((before, after));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
top_dag.remove_node(sink);
|
dag.remove_node(sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
let passes = passes
|
let passes = passes
|
||||||
|
@ -1123,13 +1009,12 @@ impl RenderGraph {
|
||||||
|
|
||||||
topological_map.push((passes, barriers));
|
topological_map.push((passes, barriers));
|
||||||
}
|
}
|
||||||
topological_map.push((vec![], root_barriers));
|
|
||||||
|
|
||||||
//tracing::debug!("mapping: {topological_map:#?}");
|
//tracing::debug!("mapping: {topological_map:#?}");
|
||||||
// I don't think this can currently happen with the way passes are added.
|
// I don't think this can currently happen with the way passes are added.
|
||||||
top_dag.remove_node(root);
|
dag.remove_node(0.into());
|
||||||
if top_dag.node_count() > 0 {
|
if dag.node_count() > 0 {
|
||||||
eprintln!("dag: {top_dag:?}");
|
eprintln!("dag: {dag:?}");
|
||||||
panic!("dag is cyclic!");
|
panic!("dag is cyclic!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue