use std::marker::PhantomData;
use concinnity_core::gfx::render_types::{DrawIndex, SkinnedIndex};
type Mat4 = [[f32; 4]; 4];
pub(crate) trait Slot: Copy {
fn position(self) -> usize;
}
impl Slot for DrawIndex {
fn position(self) -> usize {
self.index()
}
}
impl Slot for SkinnedIndex {
fn position(self) -> usize {
self.index()
}
}
pub(crate) struct ModelPushCache<I> {
pushed: Vec<Option<Mat4>>,
family: PhantomData<I>,
}
impl<I> Default for ModelPushCache<I> {
fn default() -> Self {
Self {
pushed: Vec::new(),
family: PhantomData,
}
}
}
impl<I: Slot> ModelPushCache<I> {
pub(crate) fn push_changed(&mut self, batch: &mut Vec<(I, Mat4)>, slot: I, model: Mat4) {
if *self.entry(slot) == Some(model) {
return;
}
self.push(batch, slot, model);
}
pub(crate) fn push(&mut self, batch: &mut Vec<(I, Mat4)>, slot: I, model: Mat4) {
*self.entry(slot) = Some(model);
batch.push((slot, model));
}
fn entry(&mut self, slot: I) -> &mut Option<Mat4> {
let i = slot.position();
if self.pushed.len() <= i {
self.pushed.resize(i + 1, None);
}
&mut self.pushed[i]
}
}
#[cfg(test)]
mod tests {
use super::*;
fn translated(x: f32) -> Mat4 {
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[x, 0.0, 0.0, 1.0],
]
}
#[test]
fn first_push_always_lands_and_repeats_are_dropped() {
let mut cache = ModelPushCache::<DrawIndex>::default();
let mut batch = Vec::new();
cache.push_changed(&mut batch, DrawIndex(3), translated(1.0));
assert_eq!(batch, vec![(DrawIndex(3), translated(1.0))]);
batch.clear();
cache.push_changed(&mut batch, DrawIndex(3), translated(1.0));
assert!(batch.is_empty(), "unchanged matrix is not re-sent");
cache.push_changed(&mut batch, DrawIndex(3), translated(2.0));
assert_eq!(
batch,
vec![(DrawIndex(3), translated(2.0))],
"a move is sent once"
);
}
#[test]
fn unconditional_push_updates_the_gate() {
let mut cache = ModelPushCache::<DrawIndex>::default();
let mut batch = Vec::new();
cache.push(&mut batch, DrawIndex(0), translated(5.0));
assert_eq!(batch.len(), 1);
batch.clear();
cache.push_changed(&mut batch, DrawIndex(0), translated(5.0));
assert!(batch.is_empty());
cache.push_changed(&mut batch, DrawIndex(0), translated(6.0));
assert_eq!(batch.len(), 1);
}
#[test]
fn double_push_of_one_slot_keeps_both_entries_in_order() {
let mut cache = ModelPushCache::<DrawIndex>::default();
let mut batch = Vec::new();
cache.push_changed(&mut batch, DrawIndex(1), translated(1.0));
cache.push_changed(&mut batch, DrawIndex(1), translated(0.0));
assert_eq!(
batch,
vec![
(DrawIndex(1), translated(1.0)),
(DrawIndex(1), translated(0.0))
]
);
}
}