1use crate::gfx::mesh_payload::Vertex;
16use crate::gfx::render_types::{DrawObject, InstancedCluster};
17use alloc::vec::Vec;
18
19const VERTEX_STRIDE: usize = core::mem::size_of::<Vertex>();
20const INDEX_STRIDE: usize = core::mem::size_of::<u32>();
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct MeshSeedRegion {
28 pub vtx_offset: u64,
30 pub vtx_bytes: u64,
32 pub idx_offset: u64,
34 pub idx_bytes: u64,
36}
37
38pub fn plan_seed_bytes(mesh_byte_sizes: &[(u64, u64)], cap: usize) -> Option<(u64, u64)> {
58 let n = mesh_byte_sizes.len();
59 let cap = cap.max(1);
60 if cap >= n {
61 return None;
64 }
65 let margin = (cap / 4).max(1);
66 let residency = (cap + margin).min(n);
67 if residency >= n {
68 return None;
70 }
71 let mut vtx: Vec<u64> = mesh_byte_sizes.iter().map(|&(v, _)| v).collect();
72 let mut idx: Vec<u64> = mesh_byte_sizes.iter().map(|&(_, i)| i).collect();
73 vtx.sort_unstable_by(|a, b| b.cmp(a));
76 idx.sort_unstable_by(|a, b| b.cmp(a));
77 let seed_vtx: u64 = vtx.iter().take(residency).sum();
78 let seed_idx: u64 = idx.iter().take(residency).sum();
79 Some((seed_vtx, seed_idx))
80}
81
82struct Region<'a> {
86 v_off_bytes: usize,
87 v_count: usize,
88 lod0: (usize, usize),
89 alts: &'a [(usize, usize)],
90}
91
92fn relocate_region(
98 src_v: &[Vertex],
99 src_i: &[u32],
100 dst_v: &mut Vec<Vertex>,
101 dst_i: &mut Vec<u32>,
102 region: Region,
103) -> (usize, usize, Vec<usize>) {
104 let Region {
105 v_off_bytes,
106 v_count,
107 lod0,
108 alts,
109 } = region;
110 let old_vbase = v_off_bytes / VERTEX_STRIDE;
111 let new_vbase = dst_v.len();
112 dst_v.extend_from_slice(&src_v[old_vbase..old_vbase + v_count]);
113 let delta = new_vbase as i64 - old_vbase as i64;
118 let rebase = |i: u32| -> u32 { (i as i64 + delta) as u32 };
119
120 let (i0_off, i0_count) = lod0;
121 let new_i0_off = dst_i.len();
122 for &idx in &src_i[i0_off..i0_off + i0_count] {
123 dst_i.push(rebase(idx));
124 }
125 let mut new_alt_offsets = Vec::with_capacity(alts.len());
126 for &(a_off, a_count) in alts {
127 let new_a_off = dst_i.len();
128 for &idx in &src_i[a_off..a_off + a_count] {
129 dst_i.push(rebase(idx));
130 }
131 new_alt_offsets.push(new_a_off);
132 }
133 (new_vbase * VERTEX_STRIDE, new_i0_off, new_alt_offsets)
134}
135
136pub fn compact_for_streaming(
151 vertices: &mut Vec<Vertex>,
152 indices: &mut Vec<u32>,
153 draw_objects: &mut [DrawObject],
154 clusters: &mut [InstancedCluster],
155 streamed: &[bool],
156 seed_vtx_bytes: u64,
157 seed_idx_bytes: u64,
158) -> MeshSeedRegion {
159 let mut new_v: Vec<Vertex> = Vec::with_capacity(vertices.len());
160 let mut new_i: Vec<u32> = Vec::with_capacity(indices.len());
161
162 for (i, obj) in draw_objects.iter_mut().enumerate() {
163 if streamed.get(i).copied().unwrap_or(false) {
164 obj.vertex_offset = 0;
170 obj.index_offset = 0;
171 obj.resident = false;
172 obj.lod_alternates.clear();
173 continue;
174 }
175 let alts: Vec<(usize, usize)> = obj
176 .lod_alternates
177 .iter()
178 .map(|s| (s.index_offset, s.index_count))
179 .collect();
180 let (new_v_off, new_i_off, new_alt_offs) = relocate_region(
181 vertices,
182 indices,
183 &mut new_v,
184 &mut new_i,
185 Region {
186 v_off_bytes: obj.vertex_offset,
187 v_count: obj.vertex_count,
188 lod0: (obj.index_offset, obj.index_count),
189 alts: &alts,
190 },
191 );
192 obj.vertex_offset = new_v_off;
193 obj.index_offset = new_i_off;
194 for (slice, new_off) in obj.lod_alternates.iter_mut().zip(new_alt_offs) {
195 slice.index_offset = new_off;
196 }
197 }
198
199 for c in clusters.iter_mut() {
202 let alts: Vec<(usize, usize)> = c
203 .lod_alternates
204 .iter()
205 .map(|s| (s.index_offset, s.index_count))
206 .collect();
207 let (new_v_off, new_i_off, new_alt_offs) = relocate_region(
208 vertices,
209 indices,
210 &mut new_v,
211 &mut new_i,
212 Region {
213 v_off_bytes: c.vertex_offset,
214 v_count: c.vertex_count,
215 lod0: (c.index_offset, c.index_count),
216 alts: &alts,
217 },
218 );
219 c.vertex_offset = new_v_off;
220 c.index_offset = new_i_off;
221 for (slice, new_off) in c.lod_alternates.iter_mut().zip(new_alt_offs) {
222 slice.index_offset = new_off;
223 }
224 }
225
226 let vtx_offset = (new_v.len() * VERTEX_STRIDE) as u64;
230 let idx_offset = (new_i.len() * INDEX_STRIDE) as u64;
231 let zero_v = Vertex {
232 pos: [0.0; 3],
233 normal: [0.0; 3],
234 tangent: [0.0; 3],
235 color: [0.0; 3],
236 uv: [0.0; 2],
237 };
238 let seed_v_count = (seed_vtx_bytes as usize) / VERTEX_STRIDE;
239 let seed_i_count = (seed_idx_bytes as usize) / INDEX_STRIDE;
240 new_v.extend(core::iter::repeat_n(zero_v, seed_v_count));
241 new_i.extend(core::iter::repeat_n(0u32, seed_i_count));
242
243 *vertices = new_v;
244 *indices = new_i;
245 MeshSeedRegion {
246 vtx_offset,
247 vtx_bytes: seed_vtx_bytes,
248 idx_offset,
249 idx_bytes: seed_idx_bytes,
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::gfx::render_types::{LodSlice, MaterialUniforms};
257 use alloc::vec;
258
259 fn vtx(x: f32) -> Vertex {
260 Vertex {
261 pos: [x, 0.0, 0.0],
262 normal: [0.0, 1.0, 0.0],
263 tangent: [1.0, 0.0, 0.0],
264 color: [1.0, 1.0, 1.0],
265 uv: [0.0, 0.0],
266 }
267 }
268
269 fn push_verts(v: &mut Vec<Vertex>, n: usize, tag: f32) -> (usize, u32) {
272 let base = v.len() as u32;
273 for k in 0..n {
274 v.push(vtx(tag + k as f32 / 100.0));
275 }
276 (base as usize * VERTEX_STRIDE, base)
277 }
278
279 fn push_idx(i: &mut Vec<u32>, base: u32, n: usize) -> (usize, usize) {
282 let off = i.len();
283 for k in 0..n {
284 i.push(base + (k as u32 % 3));
285 }
286 (off, n)
287 }
288
289 fn draw(
290 v_off: usize,
291 v_count: usize,
292 i_off: usize,
293 i_count: usize,
294 resident: bool,
295 lods: Vec<LodSlice>,
296 ) -> DrawObject {
297 DrawObject {
298 vertex_offset: v_off,
299 vertex_count: v_count,
300 index_offset: i_off,
301 index_count: i_count,
302 shader_bucket: 0,
303 base_vertex: 0,
304 geometry_generation: 0,
305 model: [[0.0; 4]; 4],
306 texture_slot: 0,
307 normal_map_slot: 0,
308 material: MaterialUniforms::DEFAULT,
309 visible: true,
310 resident,
311 bb_min: [0.0; 3],
312 bb_max: [0.0; 3],
313 cull_distance: 0.0,
314 lod_alternates: lods,
315 }
316 }
317
318 fn cluster(v_off: usize, v_count: usize, i_off: usize, i_count: usize) -> InstancedCluster {
319 InstancedCluster {
320 vertex_offset: v_off,
321 vertex_count: v_count,
322 index_offset: i_off,
323 index_count: i_count,
324 texture_slot: 0,
325 normal_map_slot: 0,
326 material: MaterialUniforms::DEFAULT,
327 cluster_bb_min: [0.0; 3],
328 cluster_bb_max: [0.0; 3],
329 local_bb_min: [0.0; 3],
330 local_bb_max: [0.0; 3],
331 cull_distance: 0.0,
332 instances: vec![[[0.0; 4]; 4]],
333 lod_alternates: Vec::new(),
334 }
335 }
336
337 #[test]
338 fn plan_returns_none_when_cap_holds_every_mesh() {
339 let sizes = vec![(100, 40), (100, 40), (100, 40)];
341 assert_eq!(plan_seed_bytes(&sizes, 4), None);
342 assert_eq!(plan_seed_bytes(&sizes, 3), None);
344 }
345
346 #[test]
347 fn plan_returns_none_when_margin_covers_the_set() {
348 let sizes = vec![(100, 40); 5];
350 assert_eq!(plan_seed_bytes(&sizes, 4), None);
351 }
352
353 #[test]
354 fn plan_sizes_seed_to_the_largest_residency_meshes() {
355 let sizes = vec![(100u64, 40u64); 8];
357 let (sv, si) = plan_seed_bytes(&sizes, 4).expect("shrink");
358 assert_eq!(sv, 100 * 5);
359 assert_eq!(si, 40 * 5);
360 assert!(sv < 100 * 8);
362 assert!(si < 40 * 8);
363 }
364
365 #[test]
366 fn plan_picks_the_biggest_meshes_independently_per_buffer() {
367 let sizes = vec![
370 (1000, 4), (4, 1000), (10, 10), (10, 10),
374 ];
375 let (sv, si) = plan_seed_bytes(&sizes, 1).expect("shrink");
376 assert_eq!(sv, 1010);
378 assert_eq!(si, 1010);
379 }
380
381 #[test]
382 fn compact_removes_streamed_gaps_and_rewrites_resident_offsets() {
383 let mut v: Vec<Vertex> = Vec::new();
385 let mut i: Vec<u32> = Vec::new();
386 let (a_voff, a_base) = push_verts(&mut v, 4, 1.0);
387 let (a_ioff, a_ic) = push_idx(&mut i, a_base, 6);
388 let (b_voff, b_base) = push_verts(&mut v, 10, 2.0);
389 let (b_ioff, b_ic) = push_idx(&mut i, b_base, 12);
390 let (c_voff, c_base) = push_verts(&mut v, 5, 3.0);
391 let (c_ioff, c_ic) = push_idx(&mut i, c_base, 9);
392 let (d_voff, d_base) = push_verts(&mut v, 6, 4.0);
393 let (d_ioff, d_ic) = push_idx(&mut i, d_base, 6);
394
395 let mut draws = vec![
396 draw(a_voff, 4, a_ioff, a_ic, true, vec![]),
397 draw(b_voff, 10, b_ioff, b_ic, true, vec![]),
398 draw(c_voff, 5, c_ioff, c_ic, true, vec![]),
399 ];
400 let streamed = [false, true, false];
402 let mut clusters = vec![cluster(d_voff, 6, d_ioff, d_ic)];
403
404 let pos = |s: &[Vertex]| -> Vec<[f32; 3]> { s.iter().map(|x| x.pos).collect() };
407 let a_verts = pos(&v[a_base as usize..a_base as usize + 4]);
408 let c_verts = pos(&v[c_base as usize..c_base as usize + 5]);
409 let d_verts = pos(&v[d_base as usize..d_base as usize + 6]);
410
411 let region = compact_for_streaming(
412 &mut v,
413 &mut i,
414 &mut draws,
415 &mut clusters,
416 &streamed,
417 0,
418 0,
419 );
420
421 assert!(!draws[1].resident);
423 assert_eq!(draws[1].vertex_offset, 0);
424 assert_eq!(draws[1].index_offset, 0);
425
426 assert!(draws[0].resident);
428 assert_eq!(draws[0].vertex_offset, 0);
429 let a0 = draws[0].vertex_offset / VERTEX_STRIDE;
430 assert_eq!(pos(&v[a0..a0 + 4]), a_verts);
431
432 let c0 = draws[2].vertex_offset / VERTEX_STRIDE;
435 assert_eq!(pos(&v[c0..c0 + 5]), c_verts);
436 for k in 0..c_ic {
437 let idx = i[draws[2].index_offset + k] as usize;
438 assert!(
439 idx >= c0 && idx < c0 + 5,
440 "C index {} out of its region",
441 idx
442 );
443 }
444
445 let d0 = clusters[0].vertex_offset / VERTEX_STRIDE;
447 assert_eq!(pos(&v[d0..d0 + 6]), d_verts);
448 for k in 0..d_ic {
449 let idx = i[clusters[0].index_offset + k] as usize;
450 assert!(
451 idx >= d0 && idx < d0 + 6,
452 "D index {} out of its region",
453 idx
454 );
455 }
456
457 assert_eq!(v.len(), 4 + 5 + 6); assert_eq!(i.len(), a_ic + c_ic + d_ic);
460 assert_eq!(region.vtx_offset, (v.len() * VERTEX_STRIDE) as u64);
462 assert_eq!(region.vtx_bytes, 0);
463 }
464
465 #[test]
466 fn compact_appends_seed_headroom_after_resident_geometry() {
467 let mut v: Vec<Vertex> = Vec::new();
468 let mut i: Vec<u32> = Vec::new();
469 let (a_voff, a_base) = push_verts(&mut v, 3, 1.0);
470 let (a_ioff, a_ic) = push_idx(&mut i, a_base, 3);
471 let mut draws = vec![draw(a_voff, 3, a_ioff, a_ic, true, vec![])];
472 let mut clusters: Vec<InstancedCluster> = Vec::new();
473 let streamed = [false];
474
475 let seed_v_bytes = (10 * VERTEX_STRIDE) as u64;
476 let seed_i_bytes = (24 * INDEX_STRIDE) as u64;
477 let region = compact_for_streaming(
478 &mut v,
479 &mut i,
480 &mut draws,
481 &mut clusters,
482 &streamed,
483 seed_v_bytes,
484 seed_i_bytes,
485 );
486
487 assert_eq!(region.vtx_offset, (3 * VERTEX_STRIDE) as u64);
489 assert_eq!(region.idx_offset, (3 * INDEX_STRIDE) as u64);
490 assert_eq!(region.vtx_bytes, seed_v_bytes);
491 assert_eq!(region.idx_bytes, seed_i_bytes);
492 assert_eq!(v.len(), 3 + 10);
494 assert_eq!(i.len(), 3 + 24);
495 assert_eq!(region.vtx_offset as usize % VERTEX_STRIDE, 0);
497 }
498
499 #[test]
500 fn compact_relocates_lod_alternate_index_ranges() {
501 let mut v: Vec<Vertex> = Vec::new();
503 let mut i: Vec<u32> = Vec::new();
504 let (a_voff, a_base) = push_verts(&mut v, 8, 1.0);
505 let (a_ioff, a_ic) = push_idx(&mut i, a_base, 12);
506 let (b_voff, b_base) = push_verts(&mut v, 4, 2.0);
507 let (b_ioff, b_ic) = push_idx(&mut i, b_base, 6); let (b_alt_off, b_alt_ic) = push_idx(&mut i, b_base, 3); let mut draws = vec![
511 draw(a_voff, 8, a_ioff, a_ic, true, vec![]),
512 draw(
513 b_voff,
514 4,
515 b_ioff,
516 b_ic,
517 true,
518 vec![LodSlice {
519 index_offset: b_alt_off,
520 index_count: b_alt_ic,
521 switch_distance: 10.0,
522 }],
523 ),
524 ];
525 let mut clusters: Vec<InstancedCluster> = Vec::new();
526 let streamed = [true, false];
527
528 compact_for_streaming(&mut v, &mut i, &mut draws, &mut clusters, &streamed, 0, 0);
529
530 let b0 = draws[1].vertex_offset / VERTEX_STRIDE;
531 for k in 0..b_ic {
533 let idx = i[draws[1].index_offset + k] as usize;
534 assert!(idx >= b0 && idx < b0 + 4);
535 }
536 let alt = draws[1].lod_alternates[0];
537 for k in 0..alt.index_count {
538 let idx = i[alt.index_offset + k] as usize;
539 assert!(idx >= b0 && idx < b0 + 4);
540 }
541 assert_eq!(alt.switch_distance, 10.0);
543 }
544}