1use alloc::borrow::Cow;
4use bevy_app::{App, Plugin};
5use bevy_asset::AssetId;
6use bevy_derive::{Deref, DerefMut};
7use bevy_ecs::{
8 resource::Resource,
9 schedule::IntoScheduleConfigs as _,
10 system::{Res, ResMut},
11 world::{FromWorld, World},
12};
13use bevy_log::warn;
14use bevy_mesh::Indices;
15use bevy_shape::{Aabb2d, BoundingVolume};
16use glam::Vec4;
17use wgpu::{BufferUsages, DownlevelFlags, COPY_BUFFER_ALIGNMENT};
18
19#[cfg(feature = "morph")]
20use bevy_mesh::morph::MorphAttributes;
21
22use crate::{
23 mesh::{Mesh, MeshMetadata, MeshVertexBufferLayouts, RenderMesh},
24 render_asset::{prepare_assets, ExtractedAssets},
25 renderer::{RenderAdapter, RenderDevice, RenderQueue},
26 slab_allocator::{
27 Slab, SlabAllocationBufferSlice, SlabAllocator, SlabAllocatorSettings, SlabId, SlabItem,
28 SlabItemLayout,
29 },
30 GpuResourceAppExt, Render, RenderApp, RenderSystems,
31};
32
33pub struct MeshAllocatorPlugin;
35
36#[derive(Resource, Deref, DerefMut)]
47pub struct MeshAllocator {
48 #[deref]
50 slab_allocator: SlabAllocator<MeshSlabItem>,
51
52 general_vertex_slabs_supported: bool,
59}
60
61#[derive(Resource, Deref, DerefMut)]
67pub struct MeshAllocatorSettings {
68 #[deref]
69 pub slab_allocator_settings: SlabAllocatorSettings,
70
71 pub extra_buffer_usages: BufferUsages,
73}
74
75impl Default for MeshAllocatorSettings {
76 fn default() -> MeshAllocatorSettings {
77 MeshAllocatorSettings {
78 slab_allocator_settings: SlabAllocatorSettings::default(),
79 extra_buffer_usages: BufferUsages::empty(),
80 }
81 }
82}
83
84#[cfg(feature = "morph")]
89const MORPH_ATTRIBUTE_ELEMENT_LAYOUT: ElementLayout = ElementLayout {
90 class: ElementClass::MorphTarget,
91 size: size_of::<MorphAttributes>() as u64,
92 elements_per_slot: 1,
93 buffer_usages: ElementClass::MorphTarget.buffer_usages(true),
94};
95
96pub type MeshSlabId = SlabId<MeshSlabItem>;
98
99pub type MeshBufferSlice<'a> = SlabAllocationBufferSlice<'a, MeshSlabItem>;
102
103pub struct MeshSlabItem;
106
107impl SlabItem for MeshSlabItem {
108 type Key = MeshAllocationKey;
109 type Layout = ElementLayout;
110 fn label() -> Cow<'static, str> {
111 "mesh".into()
112 }
113}
114
115#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
117pub struct MeshSlabs {
118 pub vertex_slab_id: MeshSlabId,
120 pub index_slab_id: Option<MeshSlabId>,
122 pub metadata_slab_id: Option<MeshSlabId>,
124 #[cfg(feature = "morph")]
127 pub morph_target_slab_id: Option<MeshSlabId>,
128}
129
130impl Slab<MeshSlabItem> {
131 pub fn element_class(&self) -> ElementClass {
133 self.element_layout().class
134 }
135}
136
137#[derive(Clone, Copy, PartialEq, Eq, Hash)]
139pub struct MeshAllocationKey {
140 pub mesh_id: AssetId<Mesh>,
142 pub class: ElementClass,
144}
145
146impl MeshAllocationKey {
147 pub fn new(mesh_id: AssetId<Mesh>, class: ElementClass) -> Self {
150 Self { mesh_id, class }
151 }
152}
153
154#[derive(Clone, Copy, PartialEq, Eq, Hash)]
156pub enum ElementClass {
157 Metadata,
159 Vertex,
161 Index,
163 #[cfg(feature = "morph")]
164 MorphTarget,
166}
167
168#[derive(Clone, Copy, PartialEq, Eq, Hash)]
181pub struct ElementLayout {
182 class: ElementClass,
183
184 size: u64,
186
187 elements_per_slot: u32,
193
194 buffer_usages: BufferUsages,
195}
196
197impl Plugin for MeshAllocatorPlugin {
198 fn build(&self, app: &mut App) {
199 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
200 return;
201 };
202
203 render_app
204 .init_resource::<MeshAllocatorSettings>()
205 .add_systems(
206 Render,
207 allocate_and_free_meshes
208 .in_set(RenderSystems::PrepareAssets)
209 .before(prepare_assets::<RenderMesh>),
210 );
211 }
212
213 fn finish(&self, app: &mut App) {
214 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
215 return;
216 };
217
218 render_app.init_gpu_resource::<MeshAllocator>();
221 }
222}
223
224impl FromWorld for MeshAllocator {
225 fn from_world(world: &mut World) -> Self {
226 let render_adapter = world.resource::<RenderAdapter>();
229 let general_vertex_slabs_supported = render_adapter
230 .get_downlevel_capabilities()
231 .flags
232 .contains(DownlevelFlags::BASE_VERTEX);
233
234 let mesh_allocator_settings = world.resource::<MeshAllocatorSettings>();
237 let mut slab_allocator = SlabAllocator::new();
238 slab_allocator.extra_buffer_usages |= mesh_allocator_settings.extra_buffer_usages;
239
240 Self {
241 slab_allocator,
242 general_vertex_slabs_supported,
243 }
244 }
245}
246
247pub fn allocate_and_free_meshes(
250 mut mesh_allocator: ResMut<MeshAllocator>,
251 mesh_allocator_settings: Res<MeshAllocatorSettings>,
252 extracted_meshes: Res<ExtractedAssets<RenderMesh>>,
253 mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,
254 render_device: Res<RenderDevice>,
255 render_queue: Res<RenderQueue>,
256) {
257 mesh_allocator.clear_displaced_keys();
259
260 mesh_allocator.free_meshes(&extracted_meshes);
262
263 mesh_allocator.allocate_meshes(
265 &mesh_allocator_settings,
266 &extracted_meshes,
267 &mut mesh_vertex_buffer_layouts,
268 &render_device,
269 &render_queue,
270 );
271}
272
273impl MeshAllocator {
274 pub fn mesh_metadata_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {
279 self.slab_allocation_slice(
280 &MeshAllocationKey::new(*mesh_id, ElementClass::Metadata),
281 *self.mesh_id_to_metadata_slab(mesh_id)?,
282 )
283 }
284
285 pub fn meshes_displaced_by_slab_growth(&self) -> impl Iterator<Item = AssetId<Mesh>> {
299 self.keys_displaced_by_slab_growth()
300 .iter()
301 .filter(|key| matches!(key.class, ElementClass::Vertex | ElementClass::Index))
302 .map(|key| key.mesh_id)
303 }
304
305 pub fn mesh_vertex_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {
310 self.slab_allocation_slice(
311 &MeshAllocationKey::new(*mesh_id, ElementClass::Vertex),
312 *self.mesh_id_to_vertex_slab(mesh_id)?,
313 )
314 }
315
316 pub fn mesh_index_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {
321 self.slab_allocation_slice(
322 &MeshAllocationKey::new(*mesh_id, ElementClass::Index),
323 *self.mesh_id_to_index_slab(mesh_id)?,
324 )
325 }
326
327 #[cfg(feature = "morph")]
332 pub fn mesh_morph_target_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {
333 self.slab_allocation_slice(
334 &MeshAllocationKey::new(*mesh_id, ElementClass::MorphTarget),
335 *self.mesh_id_to_morph_target_slab(mesh_id)?,
336 )
337 }
338
339 pub fn mesh_slabs(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshSlabs> {
346 Some(MeshSlabs {
347 vertex_slab_id: self.mesh_id_to_vertex_slab(mesh_id).cloned()?,
348 index_slab_id: self.mesh_id_to_index_slab(mesh_id).cloned(),
349 metadata_slab_id: self.mesh_id_to_metadata_slab(mesh_id).cloned(),
350 #[cfg(feature = "morph")]
351 morph_target_slab_id: self.mesh_id_to_morph_target_slab(mesh_id).cloned(),
352 })
353 }
354
355 pub fn index_allocation_count(&self) -> usize {
358 self.key_to_slab
359 .keys()
360 .filter(|key| key.class == ElementClass::Index)
361 .count()
362 }
363
364 fn mesh_id_to_metadata_slab(&self, mesh_id: &AssetId<Mesh>) -> Option<&SlabId<MeshSlabItem>> {
367 self.key_to_slab
368 .get(&MeshAllocationKey::new(*mesh_id, ElementClass::Metadata))
369 }
370
371 fn mesh_id_to_vertex_slab(&self, mesh_id: &AssetId<Mesh>) -> Option<&SlabId<MeshSlabItem>> {
374 self.key_to_slab
375 .get(&MeshAllocationKey::new(*mesh_id, ElementClass::Vertex))
376 }
377
378 fn mesh_id_to_index_slab(&self, mesh_id: &AssetId<Mesh>) -> Option<&SlabId<MeshSlabItem>> {
381 self.key_to_slab
382 .get(&MeshAllocationKey::new(*mesh_id, ElementClass::Index))
383 }
384
385 #[cfg(feature = "morph")]
388 fn mesh_id_to_morph_target_slab(
389 &self,
390 mesh_id: &AssetId<Mesh>,
391 ) -> Option<&SlabId<MeshSlabItem>> {
392 self.key_to_slab
393 .get(&MeshAllocationKey::new(*mesh_id, ElementClass::MorphTarget))
394 }
395
396 #[cfg(feature = "morph")]
398 pub fn morph_target_slabs(&self) -> impl Iterator<Item = MeshSlabId> {
399 self.slabs.iter().filter_map(|(slab_id, slab)| {
400 if matches!(slab.element_class(), ElementClass::MorphTarget) {
401 Some(*slab_id)
402 } else {
403 None
404 }
405 })
406 }
407
408 pub fn metadata_slabs(&self) -> impl Iterator<Item = MeshSlabId> {
409 self.slabs.iter().filter_map(|(slab_id, slab)| {
410 if matches!(slab.element_class(), ElementClass::Metadata) {
411 Some(*slab_id)
412 } else {
413 None
414 }
415 })
416 }
417
418 fn allocate_meshes(
421 &mut self,
422 mesh_allocator_settings: &MeshAllocatorSettings,
423 extracted_meshes: &ExtractedAssets<RenderMesh>,
424 mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
425 render_device: &RenderDevice,
426 render_queue: &RenderQueue,
427 ) {
428 let mut allocation_stage = self.slab_allocator.stage_allocation();
429
430 for (mesh_id, mesh) in &extracted_meshes.extracted {
432 let vertex_buffer_size = mesh.get_vertex_buffer_size() as u64;
433 if vertex_buffer_size == 0 {
434 warn!("Mesh {:?} contains no vertices.", mesh_id);
435 continue;
436 }
437
438 if mesh.final_aabb.is_some() || mesh.final_uv_ranges.iter().any(Option::is_some) {
440 if crate::storage_buffers_are_unsupported(&render_device.limits()) {
442 allocation_stage.allocate_large(
443 &MeshAllocationKey::new(*mesh_id, ElementClass::Metadata),
444 ElementLayout::metadata(false),
445 );
446 } else {
447 allocation_stage.allocate(
448 &MeshAllocationKey::new(*mesh_id, ElementClass::Metadata),
449 size_of::<MeshMetadata>() as u64,
450 ElementLayout::metadata(true),
451 mesh_allocator_settings,
452 );
453 }
454 }
455
456 let vertex_element_layout = ElementLayout::vertex(mesh_vertex_buffer_layouts, mesh);
459 if self.general_vertex_slabs_supported {
460 allocation_stage.allocate(
461 &MeshAllocationKey::new(*mesh_id, ElementClass::Vertex),
462 vertex_buffer_size,
463 vertex_element_layout,
464 mesh_allocator_settings,
465 );
466 } else {
467 allocation_stage.allocate_large(
468 &MeshAllocationKey::new(*mesh_id, ElementClass::Vertex),
469 vertex_element_layout,
470 );
471 }
472
473 if let (Some(index_buffer_data), Some(index_element_layout)) =
475 (mesh.get_index_buffer_bytes(), ElementLayout::index(mesh))
476 {
477 allocation_stage.allocate(
478 &MeshAllocationKey::new(*mesh_id, ElementClass::Index),
479 index_buffer_data.len() as u64,
480 index_element_layout,
481 mesh_allocator_settings,
482 );
483 }
484
485 #[cfg(feature = "morph")]
487 if let Some(morph_targets) = mesh.get_morph_targets() {
488 allocation_stage.allocate(
489 &MeshAllocationKey::new(*mesh_id, ElementClass::MorphTarget),
490 morph_targets.len() as u64 * size_of::<MorphAttributes>() as u64,
491 MORPH_ATTRIBUTE_ELEMENT_LAYOUT,
492 mesh_allocator_settings,
493 );
494 }
495 }
496
497 allocation_stage.commit(render_device, render_queue);
499
500 for (mesh_id, mesh) in &extracted_meshes.extracted {
502 let vertex_buffer_size = mesh.get_vertex_buffer_size() as u64;
503 if vertex_buffer_size == 0 {
504 continue;
505 }
506 self.copy_mesh_metadata(mesh_id, mesh, render_device, render_queue);
507 self.copy_mesh_vertex_data(mesh_id, mesh, render_device, render_queue);
508 self.copy_mesh_index_data(mesh_id, mesh, render_device, render_queue);
509 #[cfg(feature = "morph")]
510 self.copy_mesh_morph_target_data(mesh_id, mesh, render_device, render_queue);
511 }
512 }
513
514 fn copy_mesh_metadata(
517 &mut self,
518 mesh_id: &AssetId<Mesh>,
519 mesh: &Mesh,
520 render_device: &RenderDevice,
521 render_queue: &RenderQueue,
522 ) {
523 const UV_RANGES_NONE: [Option<Aabb2d>; 2] = [None; 2];
524 let metadata = match (mesh.final_aabb, mesh.final_uv_ranges) {
525 (None, UV_RANGES_NONE) => return,
526 _ => {
527 let (aabb_center, aabb_half_extents) = mesh
528 .final_aabb
529 .map(|aabb| (aabb.center().into(), aabb.half_size().into()))
530 .unwrap_or_default();
531 let uv_channels_min_and_extents = mesh.final_uv_ranges.map(|maybe_uv| {
532 maybe_uv
533 .map(|aabb2d| {
534 Vec4::new(
535 aabb2d.min.x,
536 aabb2d.min.y,
537 aabb2d.max.x - aabb2d.min.x,
538 aabb2d.max.y - aabb2d.min.y,
539 )
540 })
541 .unwrap_or(Vec4::new(0.0, 0.0, 1.0, 1.0))
542 });
543 MeshMetadata {
544 aabb_center,
545 aabb_half_extents,
546 uv_channels_min_and_extents,
547 ..Default::default()
548 }
549 }
550 };
551 self.copy_element_data(
553 &MeshAllocationKey::new(*mesh_id, ElementClass::Metadata),
554 size_of::<MeshMetadata>(),
555 |mut slice| slice.copy_from_slice(bytemuck::cast_slice(&[metadata])),
556 render_device,
557 render_queue,
558 );
559 }
560
561 fn copy_mesh_vertex_data(
564 &mut self,
565 mesh_id: &AssetId<Mesh>,
566 mesh: &Mesh,
567 render_device: &RenderDevice,
568 render_queue: &RenderQueue,
569 ) {
570 self.copy_element_data(
572 &MeshAllocationKey::new(*mesh_id, ElementClass::Vertex),
573 mesh.get_vertex_buffer_size(),
574 |slice| mesh.write_packed_vertex_buffer_data(slice),
575 render_device,
576 render_queue,
577 );
578 }
579
580 fn copy_mesh_index_data(
583 &mut self,
584 mesh_id: &AssetId<Mesh>,
585 mesh: &Mesh,
586 render_device: &RenderDevice,
587 render_queue: &RenderQueue,
588 ) {
589 let Some(index_data) = mesh.get_index_buffer_bytes() else {
590 return;
591 };
592
593 self.copy_element_data(
595 &MeshAllocationKey::new(*mesh_id, ElementClass::Index),
596 index_data.len(),
597 |mut slice| slice.copy_from_slice(index_data),
598 render_device,
599 render_queue,
600 );
601 }
602
603 #[cfg(feature = "morph")]
606 fn copy_mesh_morph_target_data(
607 &mut self,
608 mesh_id: &AssetId<Mesh>,
609 mesh: &Mesh,
610 render_device: &RenderDevice,
611 render_queue: &RenderQueue,
612 ) {
613 let Some(morph_targets) = mesh.get_morph_targets() else {
614 return;
615 };
616
617 self.copy_element_data(
619 &MeshAllocationKey::new(*mesh_id, ElementClass::MorphTarget),
620 size_of_val(morph_targets),
621 |mut slice| slice.copy_from_slice(bytemuck::cast_slice(morph_targets)),
622 render_device,
623 render_queue,
624 );
625 }
626
627 fn free_meshes(&mut self, extracted_meshes: &ExtractedAssets<RenderMesh>) {
630 let mut deallocation_stage = self.slab_allocator.stage_deallocation();
631
632 let meshes_to_free = extracted_meshes
641 .removed
642 .iter()
643 .chain(extracted_meshes.modified.iter())
644 .chain(extracted_meshes.added.iter());
645
646 for mesh_id in meshes_to_free {
647 deallocation_stage.free(&MeshAllocationKey::new(*mesh_id, ElementClass::Metadata));
648 deallocation_stage.free(&MeshAllocationKey::new(*mesh_id, ElementClass::Vertex));
649 deallocation_stage.free(&MeshAllocationKey::new(*mesh_id, ElementClass::Index));
650 #[cfg(feature = "morph")]
651 deallocation_stage.free(&MeshAllocationKey::new(*mesh_id, ElementClass::MorphTarget));
652 }
653
654 deallocation_stage.commit();
655 }
656}
657
658impl ElementLayout {
659 fn new(class: ElementClass, size: u64) -> ElementLayout {
662 const {
663 assert!(4 == COPY_BUFFER_ALIGNMENT);
664 }
665
666 assert!(class != ElementClass::Metadata);
668
669 let elements_per_slot = [1, 4, 2, 4][size as usize & 3];
672 ElementLayout {
673 class,
674 size,
675 elements_per_slot,
678 buffer_usages: class.buffer_usages(true),
679 }
680 }
681
682 fn metadata(storage_buffers_are_usable: bool) -> ElementLayout {
683 ElementLayout {
684 class: ElementClass::Metadata,
685 size: size_of::<MeshMetadata>() as u64,
686 elements_per_slot: 1,
687 buffer_usages: ElementClass::Metadata.buffer_usages(storage_buffers_are_usable),
688 }
689 }
690
691 fn vertex(
694 mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
695 mesh: &Mesh,
696 ) -> ElementLayout {
697 let mesh_vertex_buffer_layout =
698 mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
699 ElementLayout::new(
700 ElementClass::Vertex,
701 mesh_vertex_buffer_layout.0.layout().array_stride,
702 )
703 }
704
705 fn index(mesh: &Mesh) -> Option<ElementLayout> {
708 let size = match mesh.indices()? {
709 Indices::U16(_) => 2,
710 Indices::U32(_) => 4,
711 };
712 Some(ElementLayout::new(ElementClass::Index, size))
713 }
714}
715
716impl SlabItemLayout for ElementLayout {
717 fn size(&self) -> u64 {
718 self.size
719 }
720
721 fn elements_per_slot(&self) -> u32 {
722 self.elements_per_slot
723 }
724
725 fn buffer_usages(&self) -> BufferUsages {
726 self.buffer_usages
727 }
728}
729
730impl ElementClass {
731 const fn buffer_usages(&self, metadata_use_storage_buffer: bool) -> BufferUsages {
734 match *self {
735 ElementClass::Metadata => {
736 if metadata_use_storage_buffer {
737 BufferUsages::STORAGE
738 } else {
739 BufferUsages::UNIFORM
740 }
741 }
742 ElementClass::Vertex => BufferUsages::VERTEX,
743 ElementClass::Index => BufferUsages::INDEX,
744 #[cfg(feature = "morph")]
745 ElementClass::MorphTarget => BufferUsages::STORAGE,
746 }
747 }
748}
749
750#[cfg(test)]
751mod tests {
752 use super::*;
753 use crate::test_utils::create_dummy_device;
754 use bevy_asset::{uuid::Uuid, RenderAssetUsages};
755 use bevy_mesh::PrimitiveTopology;
756 use bevy_shape::Aabb3d;
757 use glam::{Vec2, Vec3};
758
759 fn test_mesh() -> Mesh {
760 let mut mesh = Mesh::new(
761 PrimitiveTopology::TriangleList,
762 RenderAssetUsages::default(),
763 );
764 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.0f32, 0.0, 0.0]; 64]);
765 mesh
766 }
767
768 fn full_mesh() -> Mesh {
770 let mut mesh = test_mesh();
771 mesh.insert_indices(Indices::U32((0..64).collect()));
772 mesh.final_aabb = Some(Aabb3d::new(Vec3::ZERO, Vec3::ONE));
773 mesh.final_uv_ranges[0] = Some(Aabb2d::new(Vec2::ZERO, Vec2::ONE));
774 #[cfg(feature = "morph")]
775 mesh.set_morph_targets(vec![MorphAttributes::default(); 64]);
776 mesh
777 }
778
779 fn wider_vertex_mesh() -> Mesh {
782 let mut mesh = test_mesh();
783 mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0f32, 0.0, 1.0]; 64]);
784 mesh
785 }
786
787 fn mesh_allocator(general_vertex_slabs_supported: bool) -> MeshAllocator {
792 MeshAllocator {
793 slab_allocator: SlabAllocator::new(),
794 general_vertex_slabs_supported,
795 }
796 }
797
798 fn small_slab_settings() -> MeshAllocatorSettings {
801 MeshAllocatorSettings {
802 slab_allocator_settings: SlabAllocatorSettings {
803 min_slab_size: 1024,
804 max_slab_size: 4096,
805 large_threshold: 512,
806 growth_factor: 1.5,
807 },
808 extra_buffer_usages: BufferUsages::empty(),
809 }
810 }
811
812 fn mesh_id(id: u128) -> AssetId<Mesh> {
813 AssetId::<Mesh>::Uuid {
814 uuid: Uuid::from_u128(id),
815 }
816 }
817
818 fn has_allocation(
821 mesh_allocator: &MeshAllocator,
822 mesh_id: AssetId<Mesh>,
823 class: ElementClass,
824 ) -> bool {
825 mesh_allocator
826 .key_to_slab
827 .contains_key(&MeshAllocationKey::new(mesh_id, class))
828 }
829
830 fn extracted_mesh(id: AssetId<Mesh>, mesh: Mesh) -> ExtractedAssets<RenderMesh> {
836 let mut extracted_meshes = ExtractedAssets::<RenderMesh>::default();
837 extracted_meshes.extracted.push((id, mesh));
838 extracted_meshes.added.insert(id);
839 extracted_meshes
840 }
841
842 fn modified_mesh(id: AssetId<Mesh>, mesh: Mesh) -> ExtractedAssets<RenderMesh> {
848 let mut extracted_meshes = extracted_mesh(id, mesh);
849 extracted_meshes.modified.insert(id);
850 extracted_meshes
851 }
852
853 #[test]
856 fn free_meshes_releases_reextracted_meshes() {
857 let (render_device, render_queue) = create_dummy_device();
858 let settings = MeshAllocatorSettings::default();
859 let mut mesh_vertex_buffer_layouts = MeshVertexBufferLayouts::default();
860 let mut mesh_allocator = mesh_allocator(true);
861
862 let mesh_id = mesh_id(1);
863 let extracted_meshes = extracted_mesh(mesh_id, test_mesh());
864
865 mesh_allocator.allocate_meshes(
866 &settings,
867 &extracted_meshes,
868 &mut mesh_vertex_buffer_layouts,
869 &render_device,
870 &render_queue,
871 );
872 assert!(mesh_allocator.mesh_vertex_slice(&mesh_id).is_some());
873
874 mesh_allocator.free_meshes(&extracted_meshes);
877
878 assert!(
879 mesh_allocator.key_to_slab.is_empty(),
880 "a re-extracted mesh was not freed, so its old allocation would leak"
881 );
882 assert_eq!(mesh_allocator.slab_count(), 0);
883 }
884
885 #[test]
891 fn free_meshes_releases_modified_meshes_that_were_not_reextracted() {
892 let (render_device, render_queue) = create_dummy_device();
893 let settings = MeshAllocatorSettings::default();
894 let mut mesh_vertex_buffer_layouts = MeshVertexBufferLayouts::default();
895 let mut mesh_allocator = mesh_allocator(true);
896
897 let mesh_id = mesh_id(1);
898 mesh_allocator.allocate_meshes(
899 &settings,
900 &extracted_mesh(mesh_id, test_mesh()),
901 &mut mesh_vertex_buffer_layouts,
902 &render_device,
903 &render_queue,
904 );
905 assert!(mesh_allocator.mesh_vertex_slice(&mesh_id).is_some());
906
907 let mut extracted_meshes = ExtractedAssets::<RenderMesh>::default();
908 extracted_meshes.modified.insert(mesh_id);
909 mesh_allocator.free_meshes(&extracted_meshes);
910
911 assert!(
912 mesh_allocator.key_to_slab.is_empty(),
913 "a modified mesh that wasn't re-extracted was not freed"
914 );
915 assert_eq!(mesh_allocator.slab_count(), 0);
916 }
917
918 #[test]
921 fn free_meshes_releases_every_element_class() {
922 let (render_device, render_queue) = create_dummy_device();
923 let settings = MeshAllocatorSettings::default();
924 let mut mesh_vertex_buffer_layouts = MeshVertexBufferLayouts::default();
925 let mut mesh_allocator = mesh_allocator(true);
926
927 let mesh_id = mesh_id(1);
928 let extracted_meshes = extracted_mesh(mesh_id, full_mesh());
929 mesh_allocator.allocate_meshes(
930 &settings,
931 &extracted_meshes,
932 &mut mesh_vertex_buffer_layouts,
933 &render_device,
934 &render_queue,
935 );
936
937 assert!(has_allocation(
938 &mesh_allocator,
939 mesh_id,
940 ElementClass::Vertex
941 ));
942 assert!(has_allocation(
943 &mesh_allocator,
944 mesh_id,
945 ElementClass::Index
946 ));
947 assert!(has_allocation(
948 &mesh_allocator,
949 mesh_id,
950 ElementClass::Metadata
951 ));
952 #[cfg(feature = "morph")]
953 assert!(has_allocation(
954 &mesh_allocator,
955 mesh_id,
956 ElementClass::MorphTarget
957 ));
958
959 mesh_allocator.free_meshes(&extracted_meshes);
960
961 assert!(
962 mesh_allocator.key_to_slab.is_empty(),
963 "at least one element class was left allocated, so it would leak"
964 );
965 assert_eq!(mesh_allocator.slab_count(), 0);
966 }
967
968 #[test]
971 fn reextracting_a_mesh_that_drops_its_extra_data_frees_those_allocations() {
972 let (render_device, render_queue) = create_dummy_device();
973 let settings = MeshAllocatorSettings::default();
974 let mut mesh_vertex_buffer_layouts = MeshVertexBufferLayouts::default();
975 let mut mesh_allocator = mesh_allocator(true);
976
977 let mesh_id = mesh_id(1);
978 mesh_allocator.allocate_meshes(
979 &settings,
980 &extracted_mesh(mesh_id, full_mesh()),
981 &mut mesh_vertex_buffer_layouts,
982 &render_device,
983 &render_queue,
984 );
985 assert!(has_allocation(
986 &mesh_allocator,
987 mesh_id,
988 ElementClass::Index
989 ));
990
991 let extracted_meshes = extracted_mesh(mesh_id, test_mesh());
993 mesh_allocator.free_meshes(&extracted_meshes);
994 mesh_allocator.allocate_meshes(
995 &settings,
996 &extracted_meshes,
997 &mut mesh_vertex_buffer_layouts,
998 &render_device,
999 &render_queue,
1000 );
1001
1002 assert!(has_allocation(
1003 &mesh_allocator,
1004 mesh_id,
1005 ElementClass::Vertex
1006 ));
1007 assert!(
1008 !has_allocation(&mesh_allocator, mesh_id, ElementClass::Index),
1009 "index data was dropped by the mesh but its allocation survived"
1010 );
1011 assert!(
1012 !has_allocation(&mesh_allocator, mesh_id, ElementClass::Metadata),
1013 "metadata was dropped by the mesh but its allocation survived"
1014 );
1015 #[cfg(feature = "morph")]
1016 assert!(
1017 !has_allocation(&mesh_allocator, mesh_id, ElementClass::MorphTarget),
1018 "morph targets were dropped by the mesh but their allocation survived"
1019 );
1020 }
1021
1022 #[test]
1025 fn reextracting_a_mesh_with_a_new_vertex_layout_reclaims_the_old_slab() {
1026 let (render_device, render_queue) = create_dummy_device();
1027 let settings = MeshAllocatorSettings::default();
1028 let mut mesh_vertex_buffer_layouts = MeshVertexBufferLayouts::default();
1029 let mut mesh_allocator = mesh_allocator(true);
1030
1031 let mesh_id = mesh_id(1);
1032 mesh_allocator.allocate_meshes(
1033 &settings,
1034 &extracted_mesh(mesh_id, test_mesh()),
1035 &mut mesh_vertex_buffer_layouts,
1036 &render_device,
1037 &render_queue,
1038 );
1039 assert_eq!(mesh_allocator.slab_count(), 1);
1040 let original_slab =
1041 mesh_allocator.key_to_slab[&MeshAllocationKey::new(mesh_id, ElementClass::Vertex)];
1042
1043 let extracted_meshes = extracted_mesh(mesh_id, wider_vertex_mesh());
1046 mesh_allocator.free_meshes(&extracted_meshes);
1047 mesh_allocator.allocate_meshes(
1048 &settings,
1049 &extracted_meshes,
1050 &mut mesh_vertex_buffer_layouts,
1051 &render_device,
1052 &render_queue,
1053 );
1054
1055 let new_slab =
1056 mesh_allocator.key_to_slab[&MeshAllocationKey::new(mesh_id, ElementClass::Vertex)];
1057 assert_ne!(
1058 new_slab, original_slab,
1059 "the wider vertex should have landed in a slab with a different layout"
1060 );
1061 assert_eq!(
1062 mesh_allocator.slab_count(),
1063 1,
1064 "the slab the mesh moved out of was not reclaimed"
1065 );
1066 assert!(mesh_allocator.mesh_vertex_slice(&mesh_id).is_some());
1067 }
1068
1069 struct SteadyStateCase {
1071 build: fn(AssetId<Mesh>, Mesh) -> ExtractedAssets<RenderMesh>,
1073 build_mesh: fn() -> Mesh,
1074 settings: MeshAllocatorSettings,
1075 general_vertex_slabs_supported: bool,
1076 }
1077
1078 impl Default for SteadyStateCase {
1079 fn default() -> Self {
1080 Self {
1081 build: extracted_mesh,
1082 build_mesh: test_mesh,
1083 settings: MeshAllocatorSettings::default(),
1084 general_vertex_slabs_supported: true,
1085 }
1086 }
1087 }
1088
1089 fn assert_steady_state(case: SteadyStateCase, rounds: usize) {
1092 let SteadyStateCase {
1093 build,
1094 build_mesh,
1095 settings,
1096 general_vertex_slabs_supported,
1097 } = case;
1098
1099 let (render_device, render_queue) = create_dummy_device();
1100 let mut mesh_vertex_buffer_layouts = MeshVertexBufferLayouts::default();
1101 let mut mesh_allocator = mesh_allocator(general_vertex_slabs_supported);
1102
1103 let mesh_id = mesh_id(1);
1104
1105 let mut baseline = None;
1106 for _ in 0..rounds {
1107 let extracted_meshes = build(mesh_id, build_mesh());
1108 mesh_allocator.free_meshes(&extracted_meshes);
1109 mesh_allocator.allocate_meshes(
1110 &settings,
1111 &extracted_meshes,
1112 &mut mesh_vertex_buffer_layouts,
1113 &render_device,
1114 &render_queue,
1115 );
1116
1117 let size = mesh_allocator.slabs_size();
1118 let slab_count = mesh_allocator.slab_count();
1119 match baseline {
1120 None => baseline = Some((size, slab_count)),
1121 Some(baseline) => {
1122 assert_eq!(
1123 (size, slab_count),
1124 baseline,
1125 "slab memory grew across frames"
1126 );
1127 }
1128 }
1129 }
1130
1131 assert!(mesh_allocator.mesh_vertex_slice(&mesh_id).is_some());
1132 }
1133
1134 #[test]
1136 fn reextracting_the_same_mesh_does_not_grow_the_slabs() {
1137 assert_steady_state(SteadyStateCase::default(), 32);
1138 }
1139
1140 #[test]
1142 fn modifying_a_mesh_in_place_does_not_grow_the_slabs() {
1143 assert_steady_state(
1144 SteadyStateCase {
1145 build: modified_mesh,
1146 ..SteadyStateCase::default()
1147 },
1148 32,
1149 );
1150 }
1151
1152 #[test]
1155 fn reextracting_a_mesh_with_every_element_class_does_not_grow_the_slabs() {
1156 assert_steady_state(
1157 SteadyStateCase {
1158 build_mesh: full_mesh,
1159 ..SteadyStateCase::default()
1160 },
1161 32,
1162 );
1163 }
1164
1165 #[test]
1168 fn reextracting_a_mesh_too_large_for_a_general_slab_does_not_grow_the_slabs() {
1169 assert_steady_state(
1170 SteadyStateCase {
1171 build_mesh: full_mesh,
1172 settings: small_slab_settings(),
1173 ..SteadyStateCase::default()
1174 },
1175 32,
1176 );
1177 }
1178
1179 #[test]
1182 fn reextracting_a_mesh_without_general_vertex_slabs_does_not_grow_the_slabs() {
1183 assert_steady_state(
1184 SteadyStateCase {
1185 build_mesh: full_mesh,
1186 general_vertex_slabs_supported: false,
1187 ..SteadyStateCase::default()
1188 },
1189 32,
1190 );
1191 }
1192}