use crate::renderer::types::MAX_POINT_SHADOW_LIGHTS;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct LightKey(pub u32);
#[derive(Copy, Clone, Debug)]
struct Slot {
light: LightKey,
last_frame_used: u64,
}
pub struct PointShadowPool {
slots: [Option<Slot>; MAX_POINT_SHADOW_LIGHTS as usize],
current_frame: u64,
}
impl PointShadowPool {
pub fn new() -> Self {
Self {
slots: [None; MAX_POINT_SHADOW_LIGHTS as usize],
current_frame: 0,
}
}
pub fn begin_frame(&mut self, frame_id: u64) {
self.current_frame = frame_id;
}
pub fn acquire(&mut self, light: LightKey) -> Option<u32> {
if self.slots.is_empty() {
return None;
}
for (i, s) in self.slots.iter_mut().enumerate() {
if let Some(slot) = s {
if slot.light == light {
slot.last_frame_used = self.current_frame;
return Some(i as u32);
}
}
}
for (i, s) in self.slots.iter_mut().enumerate() {
if s.is_none() {
*s = Some(Slot {
light,
last_frame_used: self.current_frame,
});
return Some(i as u32);
}
}
let (victim, _) = self
.slots
.iter()
.enumerate()
.map(|(i, s)| (i, s.unwrap().last_frame_used))
.min_by_key(|(_, f)| *f)
.unwrap();
self.slots[victim] = Some(Slot {
light,
last_frame_used: self.current_frame,
});
Some(victim as u32)
}
#[cfg(test)]
pub fn active_slots(&self) -> impl Iterator<Item = (u32, LightKey)> + '_ {
self.slots.iter().enumerate().filter_map(|(i, s)| {
s.and_then(|slot| {
if slot.last_frame_used == self.current_frame {
Some((i as u32, slot.light))
} else {
None
}
})
})
}
}
impl Default for PointShadowPool {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allocate_then_reuse() {
let mut p = PointShadowPool::new();
p.begin_frame(1);
let a = p.acquire(LightKey(0)).unwrap();
let b = p.acquire(LightKey(0)).unwrap();
assert_eq!(a, b);
}
#[test]
fn lru_eviction() {
let cap = MAX_POINT_SHADOW_LIGHTS;
let mut p = PointShadowPool::new();
for i in 0..cap {
p.begin_frame(i as u64 + 1);
p.acquire(LightKey(i)).unwrap();
}
p.begin_frame(100);
let _ = p.acquire(LightKey(999));
p.begin_frame(101);
let _ = p.acquire(LightKey(0));
let live: Vec<u32> = p.active_slots().map(|(_, k)| k.0).collect();
assert!(live.contains(&0));
assert!(!live.contains(&1));
}
}