concinnity_core/resource/
handles.rs1use alloc::vec::Vec;
25use hashbrown::HashMap;
26
27use crate::ecs::ResourceKind;
28use crate::ecs::asset_id::AssetId;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
35pub enum MeshBlock {
36 Mesh,
38 ProceduralMesh,
40 VoxelChunk,
42 File,
44 Runtime,
49}
50
51impl MeshBlock {
52 pub fn order(self) -> u8 {
54 self as u8
55 }
56}
57
58#[derive(Debug, Default, Clone)]
60pub struct ResourceHandles {
61 next: HashMap<u8, u32>,
63 map: HashMap<(u8, AssetId), u32>,
65 shader_map: HashMap<AssetId, u32>,
67 shader_next: u32,
68}
69
70impl ResourceHandles {
71 pub fn assign(&mut self, kind: ResourceKind, id: AssetId) -> u32 {
74 let next = self.next.entry(kind as u8).or_insert(0);
75 let handle = *next;
76 *next += 1;
77 self.map.insert((kind as u8, id), handle);
78 handle
79 }
80
81 pub fn get(&self, kind: ResourceKind, id: AssetId) -> Option<u32> {
83 self.map.get(&(kind as u8, id)).copied()
84 }
85
86 pub fn count(&self, kind: ResourceKind) -> u32 {
88 self.next.get(&(kind as u8)).copied().unwrap_or(0)
89 }
90
91 pub fn from_assets(assets: impl IntoIterator<Item = (AssetId, ResourceKind)>) -> Self {
95 let mut handles = Self::default();
96 for (id, kind) in assets {
97 handles.assign(kind, id);
98 }
99 handles
100 }
101
102 pub fn assign_mesh_sources(&mut self, sources: impl IntoIterator<Item = (AssetId, MeshBlock)>) {
106 let mut sources: Vec<(AssetId, MeshBlock)> = sources.into_iter().collect();
107 sources.sort_by_key(|(_, block)| block.order());
109 for (id, _) in sources {
110 self.assign(ResourceKind::Mesh, id);
111 }
112 }
113
114 pub fn assign_shader(&mut self, id: AssetId) -> u32 {
116 let handle = self.shader_next;
117 self.shader_next += 1;
118 self.shader_map.insert(id, handle);
119 handle
120 }
121
122 pub fn shader(&self, id: AssetId) -> Option<u32> {
124 self.shader_map.get(&id).copied()
125 }
126
127 pub fn shader_count(&self) -> u32 {
129 self.shader_next
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn handles_are_dense_per_kind_in_declaration_order() {
139 let handles = ResourceHandles::from_assets([
142 (AssetId(10), ResourceKind::Texture),
143 (AssetId(11), ResourceKind::Mesh),
144 (AssetId(12), ResourceKind::Texture),
145 (AssetId(20), ResourceKind::AudioClip),
146 (AssetId(14), ResourceKind::Texture),
147 ]);
148
149 assert_eq!(handles.get(ResourceKind::Texture, AssetId(10)), Some(0));
150 assert_eq!(handles.get(ResourceKind::Texture, AssetId(12)), Some(1));
151 assert_eq!(handles.get(ResourceKind::Texture, AssetId(14)), Some(2));
152 assert_eq!(handles.get(ResourceKind::Mesh, AssetId(11)), Some(0));
153 assert_eq!(handles.get(ResourceKind::AudioClip, AssetId(20)), Some(0));
154
155 assert_eq!(handles.count(ResourceKind::Texture), 3);
156 assert_eq!(handles.count(ResourceKind::Mesh), 1);
157 assert_eq!(handles.count(ResourceKind::Material), 0);
158 assert_eq!(handles.get(ResourceKind::Texture, AssetId(99)), None);
159 }
160
161 #[test]
162 fn the_same_id_in_two_kinds_gets_independent_handles() {
163 let mut handles = ResourceHandles::default();
164 assert_eq!(handles.assign(ResourceKind::Texture, AssetId(1)), 0);
165 assert_eq!(handles.assign(ResourceKind::Mesh, AssetId(1)), 0);
166 assert_eq!(handles.get(ResourceKind::Texture, AssetId(1)), Some(0));
167 assert_eq!(handles.get(ResourceKind::Mesh, AssetId(1)), Some(0));
168 }
169
170 #[test]
173 fn mesh_sources_are_block_ordered_across_kinds() {
174 let mut handles = ResourceHandles::default();
175 handles.assign_mesh_sources([
176 (AssetId(0), MeshBlock::Mesh),
177 (AssetId(1), MeshBlock::ProceduralMesh),
178 (AssetId(2), MeshBlock::VoxelChunk),
179 (AssetId(3), MeshBlock::Mesh),
180 (AssetId(4), MeshBlock::File),
181 (AssetId(5), MeshBlock::ProceduralMesh),
182 ]);
183
184 let h = |id: u32| handles.get(ResourceKind::Mesh, AssetId(id));
185 assert_eq!(h(0), Some(0));
186 assert_eq!(h(3), Some(1));
187 assert_eq!(h(1), Some(2));
188 assert_eq!(h(5), Some(3));
189 assert_eq!(h(2), Some(4));
190 assert_eq!(h(4), Some(5));
191 assert_eq!(handles.count(ResourceKind::Mesh), 6);
192 }
193
194 #[test]
195 fn mesh_blocks_run_mesh_procedural_voxel_file_then_runtime() {
196 assert_eq!(MeshBlock::Mesh.order(), 0);
197 assert_eq!(MeshBlock::ProceduralMesh.order(), 1);
198 assert_eq!(MeshBlock::VoxelChunk.order(), 2);
199 assert_eq!(MeshBlock::File.order(), 3);
200 assert_eq!(MeshBlock::Runtime.order(), 4);
203 }
204
205 #[test]
206 fn shaders_count_in_a_space_of_their_own() {
207 let mut handles = ResourceHandles::default();
208 handles.assign(ResourceKind::Material, AssetId(7));
209 assert_eq!(handles.assign_shader(AssetId(7)), 0);
210 assert_eq!(handles.assign_shader(AssetId(8)), 1);
211 assert_eq!(handles.shader(AssetId(7)), Some(0));
212 assert_eq!(handles.shader(AssetId(8)), Some(1));
213 assert_eq!(handles.shader(AssetId(9)), None);
214 assert_eq!(handles.shader_count(), 2);
215 }
216}