#![cfg(all(feature = "par", feature = "gpu-wgpu", feature = "gpu-buffer-pool"))]
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct BufferLifetime {
pub alloc_id: usize,
pub start: usize,
pub end: usize,
}
pub fn lifetimes_overlap(a: &BufferLifetime, b: &BufferLifetime) -> bool {
a.start < b.end && b.start < a.end
}
pub struct InterferenceGraph {
edges: BTreeMap<usize, alloc::collections::BTreeSet<usize>>,
lifetimes: Vec<BufferLifetime>,
}
impl InterferenceGraph {
#[inline]
fn new() -> Self {
Self {
edges: BTreeMap::new(),
lifetimes: Vec::with_capacity(16),
}
}
#[inline]
fn add_interference(&mut self, a: usize, b: usize) {
self.edges.entry(a).or_default().insert(b);
self.edges.entry(b).or_default().insert(a);
}
#[inline]
fn get_interferences(&self, buffer_id: usize) -> Option<&alloc::collections::BTreeSet<usize>> {
self.edges.get(&buffer_id)
}
#[inline]
fn add_lifetime(&mut self, lifetime: BufferLifetime) {
self.lifetimes.push(lifetime);
}
#[inline]
fn get_all_lifetimes(&self) -> &[BufferLifetime] {
&self.lifetimes
}
}
pub struct BufferPool {
graph: InterferenceGraph,
colors: BTreeMap<usize, usize>,
}
impl BufferPool {
#[inline]
pub fn new() -> Self {
Self {
graph: InterferenceGraph::new(),
colors: BTreeMap::new(),
}
}
#[inline]
pub fn add_buffer(&mut self, alloc_id: usize, start: usize, end: usize) {
let lifetime = BufferLifetime {
alloc_id,
start,
end,
};
let mut interfering: Vec<usize> = Vec::new();
for other in self.graph.get_all_lifetimes() {
if other.alloc_id != alloc_id && lifetimes_overlap(&lifetime, other) {
interfering.push(other.alloc_id);
}
}
self.graph.add_lifetime(lifetime);
for other_id in interfering {
self.graph.add_interference(alloc_id, other_id);
}
}
pub fn compute_coloring(&mut self) {
let mut buffer_ids: alloc::collections::BTreeSet<usize> =
self.graph.edges.keys().copied().collect();
for lifetime in self.graph.get_all_lifetimes() {
buffer_ids.insert(lifetime.alloc_id);
}
let buffer_ids: Vec<usize> = buffer_ids.into_iter().collect();
let mut sorted_ids = buffer_ids;
sorted_ids.sort_by_key(|&id| {
let degree = self
.graph
.get_interferences(id)
.map_or(0, std::collections::BTreeSet::len);
core::cmp::Reverse(degree)
});
for buffer_id in sorted_ids {
let interferences = self.graph.get_interferences(buffer_id);
let mut color = 0;
loop {
let color_in_use = interferences
.into_iter()
.flatten()
.any(|&neighbor_id| self.colors.get(&neighbor_id).is_some_and(|&c| c == color));
if !color_in_use {
self.colors.insert(buffer_id, color);
break;
}
color += 1;
}
}
}
#[inline]
pub fn get_color(&self, buffer_id: usize) -> Option<usize> {
self.colors.get(&buffer_id).copied()
}
}
impl Default for BufferPool {
fn default() -> Self {
Self::new()
}
}