fewer btrees

This commit is contained in:
janis 2026-10-25 01:36:34 +02:00
parent 6a41cccf8b
commit 51a2e69f74
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
2 changed files with 61 additions and 45 deletions

View file

@ -1,5 +1,6 @@
use std::{
collections::{BTreeMap, BTreeSet, HashMap},
cmp::Reverse,
collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, VecDeque},
sync::Arc,
};
@ -103,9 +104,11 @@ impl ResourceInterference {
/// Deterministic Kahn topological-sort state for command dependencies.
struct Kahn {
edges: Vec<BTreeSet<usize>>,
edges: Vec<Vec<usize>>,
indegree: Vec<usize>,
ready: BTreeSet<usize>,
ready: BinaryHeap<Reverse<usize>>,
// TODO: bitvec
is_ready: Vec<bool>,
emitted: usize,
total: usize,
}
@ -113,23 +116,34 @@ struct Kahn {
impl Kahn {
fn new(node_count: usize) -> Self {
Self {
edges: vec![BTreeSet::new(); node_count],
edges: vec![Vec::new(); node_count],
indegree: vec![0; node_count],
ready: (0..node_count).collect(),
ready: (0..node_count).map(Reverse).collect(),
is_ready: vec![true; node_count],
emitted: 0,
total: node_count,
}
}
fn add_edge(&mut self, from: usize, to: usize) {
if self.edges[from].insert(to) {
let edges = &mut self.edges[from];
if let Err(index) = edges.binary_search(&to) {
edges.insert(index, to);
self.indegree[to] += 1;
self.ready.remove(&to);
if self.is_ready[to] {
self.is_ready[to] = false;
}
}
}
fn pop_ready(&mut self) -> Option<usize> {
self.ready.pop_first()
while let Some(Reverse(node)) = self.ready.pop() {
if self.is_ready[node] {
self.is_ready[node] = false;
return Some(node);
}
}
None
}
fn complete(&mut self, node: usize) {
@ -137,7 +151,8 @@ impl Kahn {
for &dependent in &self.edges[node] {
self.indegree[dependent] -= 1;
if self.indegree[dependent] == 0 {
self.ready.insert(dependent);
self.is_ready[dependent] = true;
self.ready.push(Reverse(dependent));
}
}
}
@ -149,7 +164,7 @@ impl Kahn {
pub struct RenderGraph {
next_id: u64,
resources: BTreeMap<ResourceId, ResourceDesc>,
resources: Vec<ResourceDesc>,
pub commands: CommandList,
}
@ -157,7 +172,7 @@ impl RenderGraph {
pub fn new() -> Self {
Self {
next_id: 0,
resources: BTreeMap::new(),
resources: Vec::new(),
commands: CommandList::new(),
}
}
@ -165,7 +180,7 @@ impl RenderGraph {
fn add_resource(&mut self, desc: ResourceDesc) -> ResourceId {
let id = ResourceId(self.next_id);
self.next_id = self.next_id.checked_add(1).expect("resource id overflow");
self.resources.insert(id, desc);
self.resources.push(desc);
id
}
@ -225,14 +240,14 @@ impl RenderGraph {
}
struct GraphCompiler<'a> {
resources: &'a BTreeMap<ResourceId, ResourceDesc>,
resources: &'a [ResourceDesc],
accesses: &'a [Vec<crate::recorder::ResourceAccess>],
command_count: usize,
}
impl<'a> GraphCompiler<'a> {
fn new(
resources: &'a BTreeMap<ResourceId, ResourceDesc>,
resources: &'a [ResourceDesc],
accesses: &'a [Vec<crate::recorder::ResourceAccess>],
command_count: usize,
) -> Self {
@ -277,7 +292,7 @@ impl<'a> GraphCompiler<'a> {
fn validate_resources(&self) -> Result<(), GraphError> {
for command_accesses in self.accesses {
for access in command_accesses {
if !self.resources.contains_key(&access.resource) {
if access.resource.index() >= self.resources.len() {
return Err(GraphError::UnknownResource(access.resource));
}
}
@ -313,7 +328,7 @@ impl<'a> GraphCompiler<'a> {
continue;
}
let desc = &self.resources[&current.resource];
let desc = &self.resources[current.resource.0 as usize];
let initial = desc.initial_state();
let (old_layout, new_layout) = Self::layouts_from_state(initial, current.scope);
let layout_transition = old_layout != new_layout && new_layout.is_some();
@ -397,12 +412,13 @@ impl<'a> GraphCompiler<'a> {
ids.sort_by_key(|id| lifetimes[id].first_command);
for id in ids {
let lifetime = lifetimes.get_mut(&id).expect("lifetime key exists");
let size = match &self.resources[&id] {
let desc = &self.resources[id.0 as usize];
let size = match desc {
ResourceDesc::CreatedBuffer(desc) => desc.size,
ResourceDesc::CreatedTexture(desc) => texture_size_estimate(desc),
ResourceDesc::ImportedBuffer { .. } | ResourceDesc::ImportedTexture { .. } => 0,
};
if self.resources[&id].is_created()
if desc.is_created()
&& let Some((offset, last_use, capacity)) = slots
.iter_mut()
.find(|(_, last, cap)| !interference.interferes(*last, id) && *cap >= size)
@ -412,7 +428,7 @@ impl<'a> GraphCompiler<'a> {
let _ = capacity;
} else {
lifetime.memory_offset = next_offset;
if self.resources[&id].is_created() {
if desc.is_created() {
slots.push((next_offset, id, size));
next_offset = next_offset.saturating_add(size);
}
@ -429,7 +445,7 @@ impl Default for RenderGraph {
}
pub struct CompiledGraph {
resources: BTreeMap<ResourceId, ResourceDesc>,
resources: Vec<ResourceDesc>,
resolved: HashMap<ResourceId, ResolvedResource>,
commands: CommandList,
order: Vec<CommandIndex>,
@ -464,7 +480,8 @@ impl CompiledGraph {
/// of a Vulkan device, while command recording requires this resolution step
/// for any created resources used by commands.
pub fn resolve(&mut self, device: &Device) -> renderer_resources::Result<()> {
for (&id, desc) in &self.resources {
for (index, desc) in self.resources.iter().enumerate() {
let id = ResourceId(index as u64);
if self.resolved.contains_key(&id) {
continue;
}
@ -529,7 +546,8 @@ impl CompiledGraph {
&mut self,
recorder: &mut CommandRecorder<'_>,
) -> Result<(), RecordError> {
for (&id, desc) in &self.resources {
for (index, desc) in self.resources.iter().enumerate() {
let id = ResourceId(index as u64);
if !self.lifetimes.contains_key(&id) {
continue;
}
@ -801,6 +819,7 @@ mod tests {
fn kahn_emits_dag_in_deterministic_order() {
let mut kahn = Kahn::new(4);
kahn.add_edge(0, 2);
kahn.add_edge(0, 2);
kahn.add_edge(1, 2);
kahn.add_edge(2, 3);
@ -867,29 +886,20 @@ mod tests {
#[test]
fn aliasing_reuses_non_interfering_slot_and_separates_interfering_resources() {
let resources = BTreeMap::from([
(
ResourceId(0),
let resources = vec![
ResourceDesc::CreatedBuffer(GraphBufferDesc {
size: 8,
..Default::default()
}),
),
(
ResourceId(1),
ResourceDesc::CreatedBuffer(GraphBufferDesc {
size: 8,
..Default::default()
}),
),
(
ResourceId(2),
ResourceDesc::CreatedBuffer(GraphBufferDesc {
size: 8,
..Default::default()
}),
),
]);
];
let accesses = vec![
vec![resource_access_for(ResourceId(0))],
vec![

View file

@ -72,6 +72,12 @@ impl Default for GraphTextureDesc {
#[repr(transparent)]
pub struct ResourceId(pub u64);
impl ResourceId {
pub(crate) fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Buffer(pub ResourceId);