From 51a2e69f74d2b85ce10b041040b69a4e9659122b Mon Sep 17 00:00:00 2001 From: janis Date: Sun, 25 Oct 2026 01:36:34 +0200 Subject: [PATCH] fewer btrees --- crates/renderer_rendergraph/src/graph.rs | 100 ++++++++++--------- crates/renderer_rendergraph/src/resources.rs | 6 ++ 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/crates/renderer_rendergraph/src/graph.rs b/crates/renderer_rendergraph/src/graph.rs index afaa967..de59057 100644 --- a/crates/renderer_rendergraph/src/graph.rs +++ b/crates/renderer_rendergraph/src/graph.rs @@ -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>, + edges: Vec>, indegree: Vec, - ready: BTreeSet, + ready: BinaryHeap>, + // TODO: bitvec + is_ready: Vec, 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 { - 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, + resources: Vec, 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, + resources: &'a [ResourceDesc], accesses: &'a [Vec], command_count: usize, } impl<'a> GraphCompiler<'a> { fn new( - resources: &'a BTreeMap, + resources: &'a [ResourceDesc], accesses: &'a [Vec], 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[¤t.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, + resources: Vec, resolved: HashMap, commands: CommandList, order: Vec, @@ -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), - 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 resources = vec![ + ResourceDesc::CreatedBuffer(GraphBufferDesc { + size: 8, + ..Default::default() + }), + ResourceDesc::CreatedBuffer(GraphBufferDesc { + size: 8, + ..Default::default() + }), + ResourceDesc::CreatedBuffer(GraphBufferDesc { + size: 8, + ..Default::default() + }), + ]; let accesses = vec![ vec![resource_access_for(ResourceId(0))], vec![ diff --git a/crates/renderer_rendergraph/src/resources.rs b/crates/renderer_rendergraph/src/resources.rs index 107ce8a..b5bb8db 100644 --- a/crates/renderer_rendergraph/src/resources.rs +++ b/crates/renderer_rendergraph/src/resources.rs @@ -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);