rendergraph resolves faster
Access: layout is nolonger stored in option rendergraph resolved with linear vectors instead of maps
This commit is contained in:
parent
3332e59453
commit
6274b6e5a8
|
@ -184,7 +184,7 @@ pub fn egui_pre_pass(
|
||||||
rg.import_image(
|
rg.import_image(
|
||||||
img,
|
img,
|
||||||
Access {
|
Access {
|
||||||
layout: Some(vk::ImageLayout::GENERAL),
|
layout: vk::ImageLayout::GENERAL,
|
||||||
..Access::undefined()
|
..Access::undefined()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -229,12 +229,12 @@ pub fn egui_pre_pass(
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::NONE,
|
stage: vk::PipelineStageFlags2::NONE,
|
||||||
mask: vk::AccessFlags2::empty(),
|
mask: vk::AccessFlags2::empty(),
|
||||||
layout: Some(vk::ImageLayout::UNDEFINED),
|
layout: vk::ImageLayout::UNDEFINED,
|
||||||
},
|
},
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::TRANSFER,
|
stage: vk::PipelineStageFlags2::TRANSFER,
|
||||||
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
layout: Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -270,12 +270,12 @@ pub fn egui_pre_pass(
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::TRANSFER,
|
stage: vk::PipelineStageFlags2::TRANSFER,
|
||||||
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
layout: Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||||
},
|
},
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::TRANSFER,
|
stage: vk::PipelineStageFlags2::TRANSFER,
|
||||||
mask: vk::AccessFlags2::TRANSFER_READ,
|
mask: vk::AccessFlags2::TRANSFER_READ,
|
||||||
layout: Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
|
layout: vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
@ -285,12 +285,12 @@ pub fn egui_pre_pass(
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::NONE,
|
stage: vk::PipelineStageFlags2::NONE,
|
||||||
mask: vk::AccessFlags2::empty(),
|
mask: vk::AccessFlags2::empty(),
|
||||||
layout: Some(vk::ImageLayout::GENERAL),
|
layout: vk::ImageLayout::GENERAL,
|
||||||
},
|
},
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::TRANSFER,
|
stage: vk::PipelineStageFlags2::TRANSFER,
|
||||||
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
layout: Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
@ -334,12 +334,12 @@ pub fn egui_pre_pass(
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::TRANSFER,
|
stage: vk::PipelineStageFlags2::TRANSFER,
|
||||||
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
mask: vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
layout: Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||||
},
|
},
|
||||||
Access {
|
Access {
|
||||||
stage: vk::PipelineStageFlags2::ALL_COMMANDS,
|
stage: vk::PipelineStageFlags2::ALL_COMMANDS,
|
||||||
mask: vk::AccessFlags2::empty(),
|
mask: vk::AccessFlags2::empty(),
|
||||||
layout: Some(vk::ImageLayout::GENERAL),
|
layout: vk::ImageLayout::GENERAL,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -374,7 +374,7 @@ pub fn egui_pre_pass(
|
||||||
(
|
(
|
||||||
*id,
|
*id,
|
||||||
Access {
|
Access {
|
||||||
layout: Some(vk::ImageLayout::GENERAL),
|
layout: vk::ImageLayout::GENERAL,
|
||||||
..Access::undefined()
|
..Access::undefined()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -2535,7 +2535,6 @@ impl<W> Renderer<W> {
|
||||||
let mut rg = render_graph::RenderGraph::new();
|
let mut rg = render_graph::RenderGraph::new();
|
||||||
let (textures_to_remove, cmds) = util::timed("record command buffer", || {
|
let (textures_to_remove, cmds) = util::timed("record command buffer", || {
|
||||||
let framebuffer = rg.import_image(frame.image.clone(), Access::undefined());
|
let framebuffer = rg.import_image(frame.image.clone(), Access::undefined());
|
||||||
rg.mark_as_output(framebuffer);
|
|
||||||
|
|
||||||
render_graph::clear_pass(&mut rg, clear_color, framebuffer);
|
render_graph::clear_pass(&mut rg, clear_color, framebuffer);
|
||||||
egui_pass::egui_pre_pass(
|
egui_pass::egui_pre_pass(
|
||||||
|
@ -2558,6 +2557,8 @@ impl<W> Renderer<W> {
|
||||||
)?;
|
)?;
|
||||||
render_graph::present_pass(&mut rg, framebuffer);
|
render_graph::present_pass(&mut rg, framebuffer);
|
||||||
|
|
||||||
|
rg.mark_as_output(framebuffer, Access::present());
|
||||||
|
|
||||||
Result::Ok((textures_to_remove, rg.resolve(dev.clone())?))
|
Result::Ok((textures_to_remove, rg.resolve(dev.clone())?))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -424,707 +424,8 @@ bitflags::bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod asdf {
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use ash::vk;
|
|
||||||
|
|
||||||
use crate::render_graph::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum PassNode {
|
|
||||||
First,
|
|
||||||
Pass(u16),
|
|
||||||
Last,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct PackedPassNode(u16);
|
|
||||||
impl PackedPassNode {
|
|
||||||
pub fn unpack(self) -> PassNode {
|
|
||||||
match self.0 {
|
|
||||||
0 => PassNode::First,
|
|
||||||
1 => PassNode::Last,
|
|
||||||
n => PassNode::Pass(n - 2),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn pack(i: PassNode) -> Self {
|
|
||||||
Self(match i {
|
|
||||||
PassNode::First => 0,
|
|
||||||
PassNode::Last => 1,
|
|
||||||
PassNode::Pass(n) => n + 2,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PassNode> for PackedPassNode {
|
|
||||||
fn from(value: PassNode) -> Self {
|
|
||||||
Self::pack(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<PackedPassNode> for PassNode {
|
|
||||||
fn from(value: PackedPassNode) -> Self {
|
|
||||||
PackedPassNode::unpack(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PassNode {
|
|
||||||
pub fn dag_index(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
PassNode::First => 0,
|
|
||||||
PassNode::Last => 1,
|
|
||||||
PassNode::Pass(i) => 2 + *i as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn into_u32(&self, max_i: u32) -> u32 {
|
|
||||||
match self {
|
|
||||||
PassNode::First => 0,
|
|
||||||
PassNode::Last => max_i + 1,
|
|
||||||
PassNode::Pass(i) => 1 + *i as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn range_full(from: Self, to: Self, max_i: u32) -> std::ops::RangeInclusive<u32> {
|
|
||||||
from.into_u32(max_i)..=to.into_u32(max_i)
|
|
||||||
}
|
|
||||||
pub fn pass(i: usize) -> Self {
|
|
||||||
Self::Pass(i as u16)
|
|
||||||
}
|
|
||||||
pub fn get_pass_idx(&self) -> Option<usize> {
|
|
||||||
match self {
|
|
||||||
PassNode::First | PassNode::Last => None,
|
|
||||||
PassNode::Pass(i) => Some(*i as usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_u32(v: u32, max_i: u32) -> Self {
|
|
||||||
match v {
|
|
||||||
0 => Self::First,
|
|
||||||
n if n == 1 + max_i => Self::Last,
|
|
||||||
n => Self::Pass(n as u16 - 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum Barrier {
|
|
||||||
Logical,
|
|
||||||
Execution {
|
|
||||||
src: vk::PipelineStageFlags2,
|
|
||||||
dst: vk::PipelineStageFlags2,
|
|
||||||
},
|
|
||||||
LayoutTransition {
|
|
||||||
src: (vk::PipelineStageFlags2, vk::ImageLayout),
|
|
||||||
dst: (vk::PipelineStageFlags2, vk::ImageLayout),
|
|
||||||
},
|
|
||||||
|
|
||||||
MakeAvailable {
|
|
||||||
src: (vk::PipelineStageFlags2, vk::AccessFlags2),
|
|
||||||
dst: vk::PipelineStageFlags2,
|
|
||||||
},
|
|
||||||
MakeVisible {
|
|
||||||
src: vk::PipelineStageFlags2,
|
|
||||||
dst: (vk::PipelineStageFlags2, vk::AccessFlags2),
|
|
||||||
},
|
|
||||||
MemoryBarrier {
|
|
||||||
src: (vk::PipelineStageFlags2, vk::AccessFlags2),
|
|
||||||
dst: (vk::PipelineStageFlags2, vk::AccessFlags2),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Barrier {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Barrier::Logical => write!(f, "Logical"),
|
|
||||||
Barrier::Execution { .. } => write!(f, "Execution"),
|
|
||||||
Barrier::LayoutTransition { .. } => write!(f, "Layout"),
|
|
||||||
Barrier::MakeAvailable { .. } => write!(f, "MakeAvailable"),
|
|
||||||
Barrier::MakeVisible { .. } => write!(f, "MakeVisible"),
|
|
||||||
Barrier::MemoryBarrier { .. } => write!(f, "MemoryBarrier"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NodeRefsMap {
|
|
||||||
num_resources: usize,
|
|
||||||
num_passes: usize,
|
|
||||||
// bitmap of passes referencing rid
|
|
||||||
references: Vec<u64>,
|
|
||||||
|
|
||||||
// range into ref_accesses*: start, end, index
|
|
||||||
ref_ranges: Vec<(u32, u32, u32)>,
|
|
||||||
ref_accesses: Vec<(Access, Access)>,
|
|
||||||
ref_access_passid: Vec<PackedPassNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeRefsMap {
|
|
||||||
pub fn new(num_resources: usize, num_passes: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
num_resources,
|
|
||||||
num_passes,
|
|
||||||
references: vec![0; ((num_passes + 2) * num_resources).div_ceil(64) as usize],
|
|
||||||
ref_ranges: Vec::new(),
|
|
||||||
ref_accesses: Vec::new(),
|
|
||||||
ref_access_passid: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allocate_ref_ranges(&mut self, passes: &[PassDesc]) {
|
|
||||||
let mut rid_passcount = vec![0; self.num_resources];
|
|
||||||
|
|
||||||
for pass in passes.iter() {
|
|
||||||
for rid in pass
|
|
||||||
.reads
|
|
||||||
.iter()
|
|
||||||
.chain(pass.writes.iter())
|
|
||||||
.map(|id| id.0)
|
|
||||||
.collect::<BTreeSet<_>>()
|
|
||||||
{
|
|
||||||
rid_passcount[rid.0 as usize] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::debug!("per-resource pass-count: {rid_passcount:?}");
|
|
||||||
|
|
||||||
let mut total = 0;
|
|
||||||
for num_passes in rid_passcount {
|
|
||||||
self.ref_ranges.push((total, total + num_passes, 0));
|
|
||||||
self.ref_accesses
|
|
||||||
.extend((0..num_passes).map(|_| (Access::empty(), Access::empty())));
|
|
||||||
self.ref_access_passid
|
|
||||||
.extend((0..num_passes).map(|_| PackedPassNode(0)));
|
|
||||||
total += num_passes;
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::debug!(
|
|
||||||
"ref_ranges and ref_accesses:\n{:?}\n{:?}\n{:?}",
|
|
||||||
self.ref_ranges,
|
|
||||||
self.ref_accesses,
|
|
||||||
self.ref_access_passid
|
|
||||||
);
|
|
||||||
// for resourcedesc in resources: ref first pass
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_accesses_for_rid_pass_mut(
|
|
||||||
&mut self,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
pass: PackedPassNode,
|
|
||||||
) -> &mut (Access, Access) {
|
|
||||||
let (start, _, i) = self.ref_ranges[rid.0 as usize];
|
|
||||||
|
|
||||||
let idx = self.ref_access_passid[start as usize..(start + i) as usize]
|
|
||||||
.binary_search(&pass)
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
// increase counter
|
|
||||||
self.ref_ranges[rid.0 as usize].2 += 1;
|
|
||||||
i as usize
|
|
||||||
})
|
|
||||||
+ start as usize;
|
|
||||||
|
|
||||||
self.ref_access_passid[idx] = pass;
|
|
||||||
|
|
||||||
&mut self.ref_accesses[idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_reads_for_rid_pass_mut(
|
|
||||||
&mut self,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
pass: PackedPassNode,
|
|
||||||
) -> &mut Access {
|
|
||||||
&mut self.get_accesses_for_rid_pass_mut(rid, pass).0
|
|
||||||
}
|
|
||||||
fn get_writes_for_rid_pass_mut(
|
|
||||||
&mut self,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
pass: PackedPassNode,
|
|
||||||
) -> &mut Access {
|
|
||||||
&mut self.get_accesses_for_rid_pass_mut(rid, pass).1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_accesses_for_rid_pass(
|
|
||||||
&self,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
pass: PackedPassNode,
|
|
||||||
) -> Option<(Access, Access)> {
|
|
||||||
let (start, _, i) = self.ref_ranges[rid.0 as usize];
|
|
||||||
|
|
||||||
let idx = self.ref_access_passid[start as usize..(start + i) as usize]
|
|
||||||
.binary_search(&pass)
|
|
||||||
.ok()?
|
|
||||||
+ start as usize;
|
|
||||||
|
|
||||||
Some(self.ref_accesses[idx])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_reads_for_rid_pass(
|
|
||||||
&self,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
pass: PackedPassNode,
|
|
||||||
) -> Option<Access> {
|
|
||||||
Some(self.get_accesses_for_rid_pass(rid, pass)?.0)
|
|
||||||
}
|
|
||||||
fn get_writes_for_rid_pass(
|
|
||||||
&self,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
pass: PackedPassNode,
|
|
||||||
) -> Option<Access> {
|
|
||||||
Some(self.get_accesses_for_rid_pass(rid, pass)?.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reference_rid_pass(&mut self, rid: GraphResourceId, pass: PackedPassNode) {
|
|
||||||
let bit_idx = rid.0 as usize * (self.num_passes + 2) + pass.0 as usize;
|
|
||||||
let word_idx = bit_idx / 64;
|
|
||||||
let word_offset = bit_idx % 64;
|
|
||||||
tracing::debug!(
|
|
||||||
bit_idx,
|
|
||||||
word_idx,
|
|
||||||
word_offset,
|
|
||||||
"pass: {pass:?} references rid: {rid:?} "
|
|
||||||
);
|
|
||||||
self.references[word_idx] |= 1 << word_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ref_passes(&mut self, passes: &[PassDesc]) {
|
|
||||||
for (i, pass) in passes.iter().enumerate() {
|
|
||||||
let packed_pass = PassNode::pass(i).into();
|
|
||||||
|
|
||||||
for &(rid, access) in &pass.reads {
|
|
||||||
let read = self.get_reads_for_rid_pass_mut(rid, packed_pass);
|
|
||||||
*read = *read | access;
|
|
||||||
|
|
||||||
// TODO: check for first pass as well
|
|
||||||
self.reference_rid_pass(rid, PassNode::pass(i).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
for &(rid, access) in &pass.writes {
|
|
||||||
let write = self.get_writes_for_rid_pass_mut(rid, packed_pass);
|
|
||||||
*write = *write | access;
|
|
||||||
|
|
||||||
// TODO: check for first pass as well
|
|
||||||
self.reference_rid_pass(rid, PassNode::pass(i).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ref_inputs(&mut self, resources: &[GraphResource]) {
|
|
||||||
for (i, resource) in resources.iter().enumerate() {
|
|
||||||
match resource {
|
|
||||||
GraphResource::ImageDesc(_) | GraphResource::BufferDesc(_) => {
|
|
||||||
self.reference_rid_pass(GraphResourceId(i as u32), PassNode::First.into());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ref_outputs(&mut self, outputs: &[GraphResourceId]) {
|
|
||||||
for &rid in outputs {
|
|
||||||
self.reference_rid_pass(rid, PassNode::Last.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_dag(
|
|
||||||
&self,
|
|
||||||
) -> petgraph::stable_graph::StableDiGraph<PackedPassNode, (GraphResourceId, Barrier)>
|
|
||||||
{
|
|
||||||
struct Edge {
|
|
||||||
from: PackedPassNode,
|
|
||||||
to: PackedPassNode,
|
|
||||||
rid: GraphResourceId,
|
|
||||||
barrier: Barrier,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
enum Ref {
|
|
||||||
Write(PackedPassNode, Access),
|
|
||||||
Read(PackedPassNode, Access),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ref {
|
|
||||||
fn node(&self) -> PackedPassNode {
|
|
||||||
match self {
|
|
||||||
Ref::Write(node, _) | Ref::Read(node, _) => *node,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn access(&self) -> Access {
|
|
||||||
match self {
|
|
||||||
Ref::Write(_, access) | Ref::Read(_, access) => *access,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut edges = Vec::<Edge>::new();
|
|
||||||
|
|
||||||
let bits = crate::util::BitIter::new(
|
|
||||||
&self.references,
|
|
||||||
self.num_resources * (self.num_passes + 2),
|
|
||||||
)
|
|
||||||
.chunks(self.num_passes + 2);
|
|
||||||
|
|
||||||
tracing::debug!("building edges:");
|
|
||||||
tracing::debug!("chunks: {bits:#?}");
|
|
||||||
for (i, bits) in bits.enumerate() {
|
|
||||||
let rid = GraphResourceId(i as u32);
|
|
||||||
tracing::debug!("rid: {rid:?}");
|
|
||||||
tracing::debug!("passes: {bits}");
|
|
||||||
|
|
||||||
let mut to_make_available = AccessMask::empty();
|
|
||||||
let mut made_available = AccessMask::empty();
|
|
||||||
|
|
||||||
let mut last_ref = Option::<Ref>::None;
|
|
||||||
let mut last_read = Option::<Ref>::None;
|
|
||||||
let mut last_write = Option::<Ref>::None;
|
|
||||||
|
|
||||||
for pass in bits {
|
|
||||||
let packed_pass = PackedPassNode(pass as u16);
|
|
||||||
tracing::debug!("pass: {:?}", packed_pass.unpack());
|
|
||||||
|
|
||||||
let read = self.get_reads_for_rid_pass(rid, packed_pass);
|
|
||||||
if let Some(read) = read {
|
|
||||||
tracing::debug!("read: {:?}", read);
|
|
||||||
let make_visible = read.into_access_mask() & !made_available;
|
|
||||||
if let Some(last_write) = last_write.as_ref() {
|
|
||||||
let from = last_write.node();
|
|
||||||
let to = packed_pass;
|
|
||||||
let from_write = last_write.access();
|
|
||||||
|
|
||||||
// if last_write is some, make visible the writes
|
|
||||||
if !make_visible.is_empty() {
|
|
||||||
made_available = made_available | make_visible;
|
|
||||||
|
|
||||||
edges.push(Edge {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
rid,
|
|
||||||
barrier: Barrier::MakeVisible {
|
|
||||||
src: from_write.stage,
|
|
||||||
dst: (make_visible.stage, make_visible.mask),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// make available any changes
|
|
||||||
if !to_make_available.is_empty() {
|
|
||||||
edges.push(Edge {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
rid,
|
|
||||||
barrier: Barrier::MakeAvailable {
|
|
||||||
src: (to_make_available.stage, to_make_available.mask),
|
|
||||||
dst: read.stage,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
to_make_available = AccessMask::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
if make_visible.is_empty() && !to_make_available.is_empty() {
|
|
||||||
// still require a-after-b
|
|
||||||
edges.push(Edge {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
rid,
|
|
||||||
barrier: Barrier::Execution {
|
|
||||||
src: from_write.stage,
|
|
||||||
dst: read.stage,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// layout transition from previous pass, either read or write
|
|
||||||
if let Some(last_ref) = last_ref.as_ref() {
|
|
||||||
if last_ref.access().layout != read.layout {
|
|
||||||
let from = last_ref.node();
|
|
||||||
let to = packed_pass;
|
|
||||||
edges.push(Edge {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
rid,
|
|
||||||
barrier: Barrier::LayoutTransition {
|
|
||||||
src: (
|
|
||||||
last_ref.access().stage,
|
|
||||||
last_ref.access().layout.unwrap(),
|
|
||||||
),
|
|
||||||
dst: (read.stage, read.layout.unwrap()),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let write = self.get_writes_for_rid_pass(rid, packed_pass);
|
|
||||||
if let Some(write) = write {
|
|
||||||
tracing::debug!("write: {:?}", write);
|
|
||||||
match last_ref.as_ref() {
|
|
||||||
Some(Ref::Read(node, before)) => {
|
|
||||||
// execution barrier to ward against write-after-read
|
|
||||||
|
|
||||||
edges.push(Edge {
|
|
||||||
from: *node,
|
|
||||||
to: packed_pass,
|
|
||||||
rid,
|
|
||||||
barrier: Barrier::Execution {
|
|
||||||
src: before.stage,
|
|
||||||
dst: write.stage,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Some(Ref::Write(node, before)) => {
|
|
||||||
// check for layout transition here
|
|
||||||
if before.layout != write.layout {
|
|
||||||
edges.push(Edge {
|
|
||||||
from: *node,
|
|
||||||
to: packed_pass,
|
|
||||||
rid,
|
|
||||||
barrier: Barrier::LayoutTransition {
|
|
||||||
src: (before.stage, before.layout.unwrap()),
|
|
||||||
dst: (write.stage, write.layout.unwrap()),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(read) = read {
|
|
||||||
last_read = Some(Ref::Read(packed_pass, read));
|
|
||||||
last_ref = last_read;
|
|
||||||
}
|
|
||||||
if let Some(write) = write {
|
|
||||||
last_write = Some(Ref::Write(packed_pass, write));
|
|
||||||
last_ref = last_write;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut dag = petgraph::stable_graph::StableDiGraph::<
|
|
||||||
PackedPassNode,
|
|
||||||
(GraphResourceId, Barrier),
|
|
||||||
>::new();
|
|
||||||
|
|
||||||
let root = dag.add_node(PassNode::First.into());
|
|
||||||
let output = dag.add_node(PassNode::Last.into());
|
|
||||||
|
|
||||||
_ = (root, output);
|
|
||||||
|
|
||||||
for i in 0..self.num_passes {
|
|
||||||
dag.add_node(PassNode::pass(i).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert edges
|
|
||||||
for edge in edges {
|
|
||||||
let Edge {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
rid,
|
|
||||||
barrier,
|
|
||||||
} = edge;
|
|
||||||
dag.add_edge(
|
|
||||||
from.unpack().dag_index().into(),
|
|
||||||
to.unpack().dag_index().into(),
|
|
||||||
(rid, barrier),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,
|
|
||||||
edgeref.weight().1,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&|_graph, noderef| {
|
|
||||||
format!(
|
|
||||||
"label = \"Pass({:?})\"",
|
|
||||||
petgraph::visit::NodeRef::weight(&noderef)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.expect("writing render_graph repr");
|
|
||||||
|
|
||||||
// prune dead ends
|
|
||||||
let mut sinks = dag
|
|
||||||
.externals(petgraph::Direction::Outgoing)
|
|
||||||
.filter(|idx| dag.node_weight(*idx) != Some(&PassNode::Last.into()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
while let Some(sink) = sinks.pop() {
|
|
||||||
let mut neighbors = dag
|
|
||||||
.neighbors_directed(sink, petgraph::Direction::Incoming)
|
|
||||||
.detach();
|
|
||||||
while let Some((edge, node)) = neighbors.next(&dag) {
|
|
||||||
dag.remove_edge(edge);
|
|
||||||
|
|
||||||
if dag
|
|
||||||
.neighbors_directed(node, petgraph::Direction::Outgoing)
|
|
||||||
.count()
|
|
||||||
== 0
|
|
||||||
{
|
|
||||||
sinks.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dag.remove_node(sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
dag
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toposort_dag(
|
|
||||||
&self,
|
|
||||||
mut dag: petgraph::stable_graph::StableDiGraph<
|
|
||||||
PackedPassNode,
|
|
||||||
(GraphResourceId, Barrier),
|
|
||||||
>,
|
|
||||||
) -> Vec<(
|
|
||||||
Vec<PackedPassNode>,
|
|
||||||
BTreeMap<GraphResourceId, (Access, Access)>,
|
|
||||||
)> {
|
|
||||||
let mut topomap = Vec::new();
|
|
||||||
|
|
||||||
let mut sinks = dag
|
|
||||||
.externals(petgraph::Direction::Incoming)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let mut next_sinks = vec![];
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if sinks.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut passes = Vec::with_capacity(self.num_passes);
|
|
||||||
let mut barriers = BTreeMap::new();
|
|
||||||
for &sink in &sinks {
|
|
||||||
for &(rid, barrier) in dag
|
|
||||||
.edges_directed(sink, petgraph::Direction::Incoming)
|
|
||||||
.map(|edge| 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
|
|
||||||
// this affects imported resources only.
|
|
||||||
barriers
|
|
||||||
.entry(rid)
|
|
||||||
.and_modify(|(from, to)| {
|
|
||||||
*from = *from | before;
|
|
||||||
*to = *to | after;
|
|
||||||
})
|
|
||||||
.or_insert((before, after));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut neighbors = dag
|
|
||||||
.neighbors_directed(sink, petgraph::Direction::Incoming)
|
|
||||||
.detach();
|
|
||||||
while let Some((edge, node)) = neighbors.next(&dag) {
|
|
||||||
dag.remove_edge(edge);
|
|
||||||
|
|
||||||
if dag
|
|
||||||
.neighbors_directed(node, petgraph::Direction::Outgoing)
|
|
||||||
.count()
|
|
||||||
== 0
|
|
||||||
{
|
|
||||||
next_sinks.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
passes.push(*dag.node_weight(sink).unwrap());
|
|
||||||
dag.remove_node(sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
topomap.push((passes, barriers));
|
|
||||||
core::mem::swap(&mut sinks, &mut next_sinks);
|
|
||||||
}
|
|
||||||
|
|
||||||
topomap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct BitIter<'a> {
|
pub struct BitIter<'a> {
|
||||||
bits: &'a [u64],
|
bits: &'a [u64],
|
||||||
num_bits: usize,
|
num_bits: usize,
|
||||||
bit_offset: usize,
|
bit_offset: usize,
|
||||||
|
@ -1146,7 +447,7 @@ impl<'a> std::fmt::Display for BitIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BitIter<'a> {
|
impl<'a> BitIter<'a> {
|
||||||
fn new(bits: &'a [u64], num_bits: usize) -> Self {
|
pub fn new(bits: &'a [u64], num_bits: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bits,
|
bits,
|
||||||
num_bits,
|
num_bits,
|
||||||
|
@ -1155,7 +456,7 @@ impl<'a> BitIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunks(self, chunk_size: usize) -> ChunkedBitIter<'a> {
|
pub fn chunks(self, chunk_size: usize) -> ChunkedBitIter<'a> {
|
||||||
ChunkedBitIter {
|
ChunkedBitIter {
|
||||||
inner: self,
|
inner: self,
|
||||||
chunk_size,
|
chunk_size,
|
||||||
|
@ -1185,7 +486,7 @@ impl Iterator for BitIter<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ChunkedBitIter<'a> {
|
pub struct ChunkedBitIter<'a> {
|
||||||
inner: BitIter<'a>,
|
inner: BitIter<'a>,
|
||||||
chunk_size: usize,
|
chunk_size: usize,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
|
|
Loading…
Reference in a new issue