pub(crate) mod analysis;
pub(crate) mod cb_replay;
pub(crate) mod cross_submit;
mod graph;
mod ir;
pub mod record;
pub use graph::ShaderResourceSlot;
pub(crate) use graph::{DeferredPresentAcquire, IrSubmitState, PartitionSubmitResult, ResolvedPresentSlot};
pub use ir::NodeAccess;
pub(crate) use ir::{BarrierSet, BarrierUsage, GraphIR};
pub(crate) use ir::{DispatchDim, NodeKind, ResourceBinding, TaskNode};
#[allow(unused_imports)]
pub(crate) use ir::{NodeAccessUnion, SlotUsageSet, UsageKindFlags};
pub use record::{ComputeNodeRecord, RenderPassRecord};
use crate::backend::{BufferHandle, TextureHandle};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct TransientId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TransientTextureId(pub u32);
pub const SWAPCHAIN_SLOT_PLACEHOLDER: u32 = u32::MAX - 1;
pub const PRESENT_LEASE_SLOT_PLACEHOLDER: u32 = u32::MAX - 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum ResourceId {
Buffer(BufferHandle),
BufferRange {
parent: BufferHandle,
offset: u64,
len: u64,
},
Texture(TextureHandle),
RenderTarget(crate::backend::RenderTargetHandle),
#[allow(dead_code)] TransientBuffer(TransientId),
#[allow(dead_code)]
TransientTexture(TransientTextureId),
#[allow(dead_code)]
SwapchainOutput,
PresentLease(u32),
Deposit(u32),
}
impl ResourceId {
pub(crate) fn canonical_buffer_handle(self) -> Option<BufferHandle> {
match self {
ResourceId::Buffer(h) => Some(h),
ResourceId::BufferRange { parent, .. } => Some(parent),
ResourceId::Texture(_) => None,
ResourceId::RenderTarget(_) => None,
ResourceId::TransientBuffer(_) => None,
ResourceId::TransientTexture(_) => None,
ResourceId::SwapchainOutput => None,
ResourceId::PresentLease(_) => None,
ResourceId::Deposit(_) => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ResolvedTransientBuffer {
pub parent: BufferHandle,
pub offset: u64,
pub len: u64,
pub uav_index: u32,
pub srv_index: u32,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ResolvedTransientTexture {
pub handle: TextureHandle,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ResolvedSwapchain {
pub handle: TextureHandle,
pub uav_index: u32,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ResolvedDeposit {
pub parent: BufferHandle,
pub offset: u64,
pub len: u64,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct SlotResolver {
pub buffers: std::collections::HashMap<u32, ResolvedTransientBuffer>,
pub textures: std::collections::HashMap<u32, ResolvedTransientTexture>,
pub swapchain: Option<ResolvedSwapchain>,
pub present_leases: std::collections::HashMap<u32, ResolvedSwapchain>,
pub deposits: std::collections::HashMap<u32, ResolvedDeposit>,
}
impl SlotResolver {
pub fn new() -> Self {
Self::default()
}
pub fn resolve(&self, id: ResourceId) -> ResourceId {
match id {
ResourceId::TransientBuffer(t) => {
let r = &self.buffers[&t.0];
ResourceId::BufferRange {
parent: r.parent,
offset: r.offset,
len: r.len,
}
}
ResourceId::TransientTexture(t) => ResourceId::Texture(self.textures[&t.0].handle),
ResourceId::SwapchainOutput => {
let sc = self
.swapchain
.as_ref()
.expect("SlotResolver::resolve: SwapchainOutput accessed before swapchain acquired");
ResourceId::Texture(sc.handle)
}
ResourceId::PresentLease(id) => {
let sc = self
.present_leases
.get(&id)
.expect("SlotResolver::resolve: PresentLease accessed before pool acquire");
ResourceId::Texture(sc.handle)
}
ResourceId::Deposit(id) => {
let u = self
.deposits
.get(&id)
.expect("SlotResolver::resolve: Deposit accessed before stage/submit resolve");
ResourceId::BufferRange {
parent: u.parent,
offset: u.offset,
len: u.len,
}
}
other => other,
}
}
pub fn resolve_slots(&self, resource_slots: &[u32], bindings: &[ir::ResourceBinding]) -> Vec<u32> {
let mut out = resource_slots.to_vec();
for (i, b) in bindings.iter().enumerate() {
if i >= out.len() {
break;
}
if let ResourceId::TransientBuffer(t) = b.resource {
let r = &self.buffers[&t.0];
let is_read_only = b.access == ir::NodeAccess::Read;
out[i] = if is_read_only { r.srv_index } else { r.uav_index };
}
}
let present_ids: Vec<u32> = bindings
.iter()
.filter_map(|b| match b.resource {
ResourceId::PresentLease(id) => Some(id),
_ => None,
})
.collect();
let mut present_iter = present_ids.into_iter();
for slot in &mut out {
if *slot != PRESENT_LEASE_SLOT_PLACEHOLDER {
continue;
}
let id = present_iter
.next()
.expect("SlotResolver::resolve_slots: PRESENT_LEASE_SLOT_PLACEHOLDER without PresentLease binding");
let sc = self
.present_leases
.get(&id)
.expect("SlotResolver::resolve_slots: PresentLease before pool acquire");
*slot = sc.uav_index;
}
debug_assert!(
present_iter.next().is_none(),
"SlotResolver::resolve_slots: PresentLease binding without PRESENT_LEASE_SLOT_PLACEHOLDER"
);
let has_swapchain_binding = bindings
.iter()
.any(|b| matches!(b.resource, ResourceId::SwapchainOutput));
if has_swapchain_binding {
for slot in &mut out {
if *slot != SWAPCHAIN_SLOT_PLACEHOLDER {
continue;
}
let sc = self
.swapchain
.as_ref()
.expect("SlotResolver::resolve_slots: SwapchainOutput before acquire");
*slot = sc.uav_index;
}
}
out
}
}
#[cfg(test)]
mod resolve_slots_tests {
use super::*;
use crate::task_graph::ir::{NodeAccess, ResourceBinding};
fn present_binding(id: u32) -> ResourceBinding {
ResourceBinding {
resource: ResourceId::PresentLease(id),
access: NodeAccess::Write,
}
}
fn buffer_binding(handle: u64) -> ResourceBinding {
ResourceBinding {
resource: ResourceId::Buffer(handle),
access: NodeAccess::Read,
}
}
#[test]
fn present_placeholder_resolves_when_sampler_slot_precedes_it() {
let mut resolver = SlotResolver::new();
resolver.present_leases.insert(
0,
ResolvedSwapchain {
handle: 42,
uav_index: 7,
},
);
let slots = [11u32, PRESENT_LEASE_SLOT_PLACEHOLDER];
let bindings = [present_binding(0)];
let resolved = resolver.resolve_slots(&slots, &bindings);
assert_eq!(resolved, vec![11, 7]);
}
#[test]
fn present_placeholder_resolves_when_dependency_binding_precedes_it() {
let mut resolver = SlotResolver::new();
resolver.present_leases.insert(
0,
ResolvedSwapchain {
handle: 42,
uav_index: 9,
},
);
let slots = [PRESENT_LEASE_SLOT_PLACEHOLDER];
let bindings = [buffer_binding(1), present_binding(0)];
let resolved = resolver.resolve_slots(&slots, &bindings);
assert_eq!(resolved, vec![9]);
}
#[test]
fn multiple_present_placeholders_match_bindings_in_order() {
let mut resolver = SlotResolver::new();
resolver.present_leases.insert(
0,
ResolvedSwapchain {
handle: 1,
uav_index: 3,
},
);
resolver.present_leases.insert(
1,
ResolvedSwapchain {
handle: 2,
uav_index: 5,
},
);
let slots = [
10u32,
PRESENT_LEASE_SLOT_PLACEHOLDER,
12u32,
PRESENT_LEASE_SLOT_PLACEHOLDER,
];
let bindings = [present_binding(0), present_binding(1)];
let resolved = resolver.resolve_slots(&slots, &bindings);
assert_eq!(resolved, vec![10, 3, 12, 5]);
}
#[test]
fn aligned_present_binding_and_slot_still_resolves() {
let mut resolver = SlotResolver::new();
resolver.present_leases.insert(
0,
ResolvedSwapchain {
handle: 42,
uav_index: 4,
},
);
let slots = [1u32, PRESENT_LEASE_SLOT_PLACEHOLDER];
let bindings = [buffer_binding(1), present_binding(0)];
let resolved = resolver.resolve_slots(&slots, &bindings);
assert_eq!(resolved, vec![1, 4]);
}
}