1use crate::{
12 asset_data::{bounds, AssetLease, Material},
13 scene_storage::reserved,
14 MeshAsset, MeshVertex, PreparedInstance, PreparedScene, RenderError, RenderLimits,
15 ResourceArena, ResourceId, SceneSnapshot,
16};
17use std::sync::{
18 atomic::{AtomicU64, Ordering},
19 Arc,
20};
21#[derive(Debug, Clone, Copy)]
23pub struct AssetLimits {
24 pub max_assets: u32,
26 pub max_vertices: u32,
28 pub max_indices: u32,
30 pub max_instances: u32,
32 pub managed_cpu_bytes: u64,
34}
35impl Default for AssetLimits {
36 fn default() -> Self {
37 Self {
38 max_assets: 512,
39 max_vertices: 65536,
40 max_indices: 196608,
41 max_instances: 8192,
42 managed_cpu_bytes: 128 * 1024 * 1024,
43 }
44 }
45}
46impl AssetLimits {
47 pub fn validate(self) -> Result<Self, RenderError> {
49 if !(1..=65536).contains(&self.max_assets)
50 || !(3..=65536).contains(&self.max_vertices)
51 || !(3..=196608).contains(&self.max_indices)
52 || !(1..=65536).contains(&self.max_instances)
53 || self.managed_cpu_bytes == 0
54 || self.managed_cpu_bytes > 512 * 1024 * 1024
55 {
56 return Err(RenderError::InvalidLimits);
57 }
58 Ok(self)
59 }
60}
61pub struct AssetRegistry {
64 limits: AssetLimits,
65 meshes: ResourceArena<Arc<MeshAsset>>,
66 materials: ResourceArena<Arc<Material>>,
67 bytes: Arc<AtomicU64>,
68}
69impl AssetRegistry {
70 pub fn new(limits: AssetLimits) -> Result<Self, RenderError> {
72 let limits = limits.validate()?;
73 let arena = RenderLimits {
74 resource_slots: limits.max_assets,
75 managed_bytes: limits.managed_cpu_bytes,
76 ..Default::default()
77 };
78 Ok(Self {
79 limits,
80 meshes: ResourceArena::new(arena)?,
81 materials: ResourceArena::new(arena)?,
82 bytes: Arc::new(AtomicU64::new(0)),
83 })
84 }
85 fn reserve(&self, bytes: u64) -> Result<AssetLease, RenderError> {
86 self.bytes
87 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |n| {
88 n.checked_add(bytes)
89 .filter(|total| *total <= self.limits.managed_cpu_bytes)
90 })
91 .map_err(|_| RenderError::BudgetExceeded)?;
92 Ok(AssetLease {
93 counter: self.bytes.clone(),
94 bytes,
95 })
96 }
97 pub fn managed_bytes(&self) -> u64 {
99 self.bytes.load(Ordering::Acquire)
100 }
101 pub fn register_mesh(
103 &mut self,
104 vertices: Vec<MeshVertex>,
105 indices: Vec<u32>,
106 ) -> Result<ResourceId, RenderError> {
107 if vertices.len() < 3
108 || vertices.capacity() > self.limits.max_vertices as usize
109 || indices.is_empty()
110 || !indices.len().is_multiple_of(3)
111 || indices.capacity() > self.limits.max_indices as usize
112 || indices.iter().any(|i| *i as usize >= vertices.len())
113 {
114 return Err(RenderError::InvalidLimits);
115 }
116 for triangle in indices.chunks_exact(3) {
117 let a = vertices[triangle[0] as usize].position();
118 let b = vertices[triangle[1] as usize].position();
119 let c = vertices[triangle[2] as usize].position();
120 let u: [f32; 3] = std::array::from_fn(|i| b[i] - a[i]);
121 let v: [f32; 3] = std::array::from_fn(|i| c[i] - a[i]);
122 let cross = [
123 u[1] * v[2] - u[2] * v[1],
124 u[2] * v[0] - u[0] * v[2],
125 u[0] * v[1] - u[1] * v[0],
126 ];
127 if cross.iter().all(|v| *v == 0.0) {
128 return Err(RenderError::InvalidLimits);
129 }
130 }
131 let bytes = vertices.capacity() as u64 * std::mem::size_of::<MeshVertex>() as u64
132 + indices.capacity() as u64 * 4;
133 let lease = self.reserve(bytes)?;
134 let sphere = bounds(&vertices);
135 self.meshes.insert(
136 Arc::new(MeshAsset {
137 vertices,
138 indices,
139 bounds: sphere,
140 _lease: lease,
141 }),
142 bytes,
143 )
144 }
145 pub fn register_material(&mut self, color: [f32; 4]) -> Result<ResourceId, RenderError> {
147 if !color
148 .iter()
149 .all(|v| v.is_finite() && (0.0..=1.0).contains(v))
150 {
151 return Err(RenderError::InvalidLimits);
152 }
153 let bytes = 16;
154 let lease = self.reserve(bytes)?;
155 self.materials.insert(
156 Arc::new(Material {
157 color,
158 _lease: lease,
159 }),
160 bytes,
161 )
162 }
163 pub fn remove_mesh(&mut self, id: ResourceId) -> Result<(), RenderError> {
165 self.meshes.remove(id)?;
166 Ok(())
167 }
168 pub fn remove_material(&mut self, id: ResourceId) -> Result<(), RenderError> {
170 self.materials.remove(id)?;
171 Ok(())
172 }
173 pub fn prepare(&self, snapshot: SceneSnapshot) -> Result<PreparedScene, RenderError> {
176 if snapshot.instances().len() > self.limits.max_instances as usize {
177 return Err(RenderError::BudgetExceeded);
178 }
179 for instance in snapshot.instances() {
180 self.meshes.get(instance.binding.mesh)?;
181 self.materials.get(instance.binding.material)?;
182 if instance.binding.animation != 0 || instance.binding.morph != 0 {
183 return Err(RenderError::InvalidResource);
184 }
185 instance.binding.validate()?;
186 }
187 let lease = self.reserve(
188 snapshot.instances().len() as u64 * std::mem::size_of::<PreparedInstance>() as u64,
189 )?;
190 let mut instances = reserved(snapshot.instances().len())?;
191 for instance in snapshot.instances() {
192 instances.push(PreparedInstance {
193 entity: instance.entity,
194 transform: instance.transform,
195 mesh_id: instance.binding.mesh,
196 mesh: self.meshes.get(instance.binding.mesh)?.clone(),
197 material: self.materials.get(instance.binding.material)?.clone(),
198 tint: instance.binding.tint,
199 });
200 }
201 Ok(PreparedScene {
202 instances,
203 _snapshot: snapshot,
204 _lease: lease,
205 })
206 }
207}