ferravex_graphics/
cache.rs1use crate::{Bounds, Mat4, Mesh, MeshError};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct MeshId {
6 pub index: u32,
7 pub generation: u64,
8}
9struct Entry {
10 mesh: Mesh,
11 bounds: Bounds,
12}
13struct Slot {
14 generation: u64,
15 entry: Option<Entry>,
16}
17#[derive(Default)]
18pub struct MeshCache {
19 slots: Vec<Slot>,
20 free: Vec<u32>,
21 live: usize,
22}
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub struct CacheStats {
25 pub meshes: usize,
26 pub allocated_slots: usize,
27 pub vertices: usize,
28 pub indices: usize,
29}
30impl MeshCache {
31 pub fn new() -> Self {
32 Self::default()
33 }
34 pub fn insert(&mut self, mesh: Mesh) -> Result<MeshId, MeshError> {
35 let bounds = mesh.validate()?;
36 let entry = Some(Entry { mesh, bounds });
37 let id = if let Some(index) = self.free.pop() {
38 let slot = &mut self.slots[index as usize];
39 slot.entry = entry;
40 MeshId {
41 index,
42 generation: slot.generation,
43 }
44 } else {
45 let index = u32::try_from(self.slots.len()).map_err(|_| MeshError::TooLarge)?;
46 self.slots.push(Slot {
47 generation: 1,
48 entry,
49 });
50 MeshId {
51 index,
52 generation: 1,
53 }
54 };
55 self.live += 1;
56 Ok(id)
57 }
58 pub fn get(&self, id: MeshId) -> Option<&Mesh> {
59 self.entry(id).map(|entry| &entry.mesh)
60 }
61 pub fn bounds(&self, id: MeshId) -> Option<Bounds> {
62 self.entry(id).map(|entry| entry.bounds)
63 }
64 fn entry(&self, id: MeshId) -> Option<&Entry> {
65 let slot = self.slots.get(id.index as usize)?;
66 if slot.generation != id.generation {
67 return None;
68 }
69 slot.entry.as_ref()
70 }
71 pub fn update(&mut self, id: MeshId, mesh: Mesh) -> Result<bool, MeshError> {
73 let bounds = mesh.validate()?;
74 let Some(slot) = self.slots.get_mut(id.index as usize) else {
75 return Ok(false);
76 };
77 if slot.generation != id.generation || slot.entry.is_none() {
78 return Ok(false);
79 }
80 slot.entry = Some(Entry { mesh, bounds });
81 Ok(true)
82 }
83 pub fn remove(&mut self, id: MeshId) -> Option<Mesh> {
84 let slot = self.slots.get_mut(id.index as usize)?;
85 if slot.generation != id.generation {
86 return None;
87 }
88 let entry = slot.entry.take()?;
89 self.live -= 1;
90 if let Some(next) = slot.generation.checked_add(1) {
92 slot.generation = next;
93 self.free.push(id.index);
94 }
95 Some(entry.mesh)
96 }
97 pub fn stats(&self) -> CacheStats {
98 let mut result = CacheStats {
99 meshes: self.live,
100 allocated_slots: self.slots.len(),
101 ..Default::default()
102 };
103 for entry in self.slots.iter().filter_map(|slot| slot.entry.as_ref()) {
104 result.vertices += entry.mesh.vertices.len();
105 result.indices += entry.mesh.indices.len();
106 }
107 result
108 }
109}
110#[derive(Clone, Copy, Debug)]
111pub struct RenderItem {
112 pub mesh: MeshId,
113 pub transform: Mat4,
114}
115impl RenderItem {
116 pub const fn new(mesh: MeshId, transform: Mat4) -> Self {
117 Self { mesh, transform }
118 }
119}
120#[derive(Debug)]
121pub struct InstanceBatch {
122 pub mesh: MeshId,
123 pub transforms: Vec<Mat4>,
124}
125pub fn instance_batches(items: &[RenderItem]) -> Vec<InstanceBatch> {
127 let mut sorted = items.to_vec();
128 sorted.sort_by_key(|item| item.mesh);
129 let mut batches: Vec<InstanceBatch> = Vec::new();
130 for item in sorted {
131 if let Some(last) = batches.last_mut().filter(|batch| batch.mesh == item.mesh) {
132 last.transforms.push(item.transform);
133 } else {
134 batches.push(InstanceBatch {
135 mesh: item.mesh,
136 transforms: vec![item.transform],
137 });
138 }
139 }
140 batches
141}