use concinnity_core::gfx::render_types::{DrawIndex, SkinnedIndex};
use concinnity_core::render::draw_slot::{DrawSlotAllocator, SlotAlloc};
use concinnity_core::render::skinned_pool::SkinnedInstancePool;
pub(crate) struct RenderSlots {
draws: DrawSlotAllocator,
skinned: SkinnedInstancePool,
reuse_floor: usize,
}
impl RenderSlots {
pub(crate) fn new(
build_draw_count: usize,
reuses_build_slots: bool,
skinned_reservations: &[(SkinnedIndex, SkinnedIndex)],
) -> Self {
let mut skinned = SkinnedInstancePool::new();
for &(template, instance) in skinned_reservations {
skinned.reserve(template, instance);
}
Self {
draws: DrawSlotAllocator::with_len(build_draw_count),
skinned,
reuse_floor: if reuses_build_slots {
0
} else {
build_draw_count
},
}
}
pub(crate) fn allocate_draw(&mut self) -> SlotAlloc {
self.draws.allocate()
}
pub(crate) fn free_draw(&mut self, slot: DrawIndex) {
if slot.index() >= self.reuse_floor {
self.draws.free(slot);
}
}
pub(crate) fn claim_skinned(&mut self, template: SkinnedIndex) -> Option<SkinnedIndex> {
self.skinned.acquire(template)
}
pub(crate) fn release_skinned(&mut self, instance: SkinnedIndex) -> bool {
self.skinned.release(instance)
}
pub(crate) fn skinned_free(&self) -> usize {
self.skinned.total_free()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_slots_recycle_only_above_the_floor() {
let mut slots = RenderSlots::new(4, false, &[]);
slots.free_draw(DrawIndex(2));
assert_eq!(
slots.allocate_draw(),
SlotAlloc::Append(DrawIndex(4)),
"a freed build-time slot is not recycled"
);
slots.free_draw(DrawIndex(4));
assert_eq!(
slots.allocate_draw(),
SlotAlloc::Reuse(DrawIndex(4)),
"a runtime-append slot is recycled"
);
}
#[test]
fn build_slots_recycle_everywhere_without_the_floor() {
let mut slots = RenderSlots::new(4, true, &[]);
slots.free_draw(DrawIndex(2));
assert_eq!(slots.allocate_draw(), SlotAlloc::Reuse(DrawIndex(2)));
}
#[test]
fn skinned_pool_claims_and_releases_per_template() {
let mut slots = RenderSlots::new(
0,
true,
&[
(SkinnedIndex(0), SkinnedIndex(5)),
(SkinnedIndex(0), SkinnedIndex(6)),
(SkinnedIndex(3), SkinnedIndex(9)),
],
);
assert_eq!(slots.skinned_free(), 3);
let a = slots.claim_skinned(SkinnedIndex(0)).expect("first copy");
let b = slots.claim_skinned(SkinnedIndex(0)).expect("second copy");
assert!(
slots.claim_skinned(SkinnedIndex(0)).is_none(),
"reserve exhausted"
);
assert!(slots.release_skinned(a));
assert_eq!(
slots.claim_skinned(SkinnedIndex(0)),
Some(a),
"released slot recycles"
);
assert!(
!slots.release_skinned(SkinnedIndex(42)),
"unknown slot is not a pool slot"
);
let _ = b;
assert_eq!(slots.claim_skinned(SkinnedIndex(3)), Some(SkinnedIndex(9)));
}
}