@@ -16,7 +16,7 @@ use bevy_ecs::{
1616 system:: { Res , ResMut } ,
1717 world:: { FromWorld , World } ,
1818} ;
19- use bevy_platform_support:: collections:: { HashMap , HashSet } ;
19+ use bevy_platform_support:: collections:: { hash_map :: Entry , HashMap , HashSet } ;
2020use bevy_utils:: default;
2121use offset_allocator:: { Allocation , Allocator } ;
2222use tracing:: error;
@@ -196,7 +196,7 @@ struct GeneralSlab {
196196 element_layout : ElementLayout ,
197197
198198 /// The size of this slab in slots.
199- slot_capacity : u32 ,
199+ current_slot_capacity : u32 ,
200200}
201201
202202/// A slab that contains a single object.
@@ -224,6 +224,18 @@ enum ElementClass {
224224 Index ,
225225}
226226
227+ /// The results of [`GeneralSlab::grow_if_necessary`].
228+ enum SlabGrowthResult {
229+ /// The mesh data already fits in the slab; the slab doesn't need to grow.
230+ NoGrowthNeeded ,
231+ /// The slab needed to grow.
232+ ///
233+ /// The [`SlabToReallocate`] contains the old capacity of the slab.
234+ NeededGrowth ( SlabToReallocate ) ,
235+ /// The slab wanted to grow but couldn't because it hit its maximum size.
236+ CantGrow ,
237+ }
238+
227239/// Information about the size of individual elements (vertices or indices)
228240/// within a slab.
229241///
@@ -278,9 +290,8 @@ struct SlabsToReallocate(HashMap<SlabId, SlabToReallocate>);
278290/// reallocated.
279291#[ derive( Default ) ]
280292struct SlabToReallocate {
281- /// Maps all allocations that need to be relocated to their positions within
282- /// the *new* slab.
283- allocations_to_copy : HashMap < AssetId < Mesh > , SlabAllocation > ,
293+ /// The capacity of the slab before we decided to grow it.
294+ old_slot_capacity : u32 ,
284295}
285296
286297impl Display for SlabId {
@@ -694,32 +705,39 @@ impl MeshAllocator {
694705 // and try to allocate the mesh inside them. We go with the first one
695706 // that succeeds.
696707 let mut mesh_allocation = None ;
697- ' slab: for & slab_id in & * candidate_slabs {
698- loop {
699- let Some ( Slab :: General ( ref mut slab) ) = self . slabs . get_mut ( & slab_id) else {
700- unreachable ! ( "Slab not found" )
701- } ;
708+ for & slab_id in & * candidate_slabs {
709+ let Some ( Slab :: General ( ref mut slab) ) = self . slabs . get_mut ( & slab_id) else {
710+ unreachable ! ( "Slab not found" )
711+ } ;
702712
703- if let Some ( allocation) = slab. allocator . allocate ( data_slot_count) {
704- mesh_allocation = Some ( MeshAllocation {
705- slab_id,
706- slab_allocation : SlabAllocation {
707- allocation,
708- slot_count : data_slot_count,
709- } ,
710- } ) ;
711- break ' slab;
712- }
713+ let Some ( allocation) = slab. allocator . allocate ( data_slot_count) else {
714+ continue ;
715+ } ;
713716
714- // Try to grow the slab. If this fails, the slab is full; go on
715- // to the next slab.
716- match slab. try_grow ( settings) {
717- Ok ( new_mesh_allocation_records) => {
718- slabs_to_grow. insert ( slab_id, new_mesh_allocation_records) ;
717+ // Try to fit the object in the slab, growing if necessary.
718+ match slab. grow_if_necessary ( allocation. offset + data_slot_count, settings) {
719+ SlabGrowthResult :: NoGrowthNeeded => { }
720+ SlabGrowthResult :: NeededGrowth ( slab_to_reallocate) => {
721+ // If we already grew the slab this frame, don't replace the
722+ // `SlabToReallocate` entry. We want to keep the entry
723+ // corresponding to the size that the slab had at the start
724+ // of the frame, so that we can copy only the used portion
725+ // of the initial buffer to the new one.
726+ if let Entry :: Vacant ( vacant_entry) = slabs_to_grow. entry ( slab_id) {
727+ vacant_entry. insert ( slab_to_reallocate) ;
719728 }
720- Err ( ( ) ) => continue ' slab,
721729 }
730+ SlabGrowthResult :: CantGrow => continue ,
722731 }
732+
733+ mesh_allocation = Some ( MeshAllocation {
734+ slab_id,
735+ slab_allocation : SlabAllocation {
736+ allocation,
737+ slot_count : data_slot_count,
738+ } ,
739+ } ) ;
740+ break ;
723741 }
724742
725743 // If we still have no allocation, make a new slab.
@@ -774,10 +792,11 @@ impl MeshAllocator {
774792
775793 /// Reallocates a slab that needs to be resized, or allocates a new slab.
776794 ///
777- /// This performs the actual growth operation that [`GeneralSlab::try_grow`]
778- /// scheduled. We do the growth in two phases so that, if a slab grows
779- /// multiple times in the same frame, only one new buffer is reallocated,
780- /// rather than reallocating the buffer multiple times.
795+ /// This performs the actual growth operation that
796+ /// [`GeneralSlab::grow_if_necessary`] scheduled. We do the growth in two
797+ /// phases so that, if a slab grows multiple times in the same frame, only
798+ /// one new buffer is reallocated, rather than reallocating the buffer
799+ /// multiple times.
781800 fn reallocate_slab (
782801 & mut self ,
783802 render_device : & RenderDevice ,
@@ -805,38 +824,28 @@ impl MeshAllocator {
805824 slab_id,
806825 buffer_usages_to_str( buffer_usages)
807826 ) ) ,
808- size : slab. slot_capacity as u64 * slab. element_layout . slot_size ( ) ,
827+ size : slab. current_slot_capacity as u64 * slab. element_layout . slot_size ( ) ,
809828 usage : buffer_usages,
810829 mapped_at_creation : false ,
811830 } ) ;
812831
813832 slab. buffer = Some ( new_buffer. clone ( ) ) ;
814833
834+ let Some ( old_buffer) = old_buffer else { return } ;
835+
815836 // In order to do buffer copies, we need a command encoder.
816837 let mut encoder = render_device. create_command_encoder ( & CommandEncoderDescriptor {
817838 label : Some ( "slab resize encoder" ) ,
818839 } ) ;
819840
820- // If we have no objects to copy over, we're done.
821- let Some ( old_buffer) = old_buffer else {
822- return ;
823- } ;
824-
825- for ( mesh_id, src_slab_allocation) in & mut slab. resident_allocations {
826- let Some ( dest_slab_allocation) = slab_to_grow. allocations_to_copy . get ( mesh_id) else {
827- continue ;
828- } ;
829-
830- encoder. copy_buffer_to_buffer (
831- & old_buffer,
832- src_slab_allocation. allocation . offset as u64 * slab. element_layout . slot_size ( ) ,
833- & new_buffer,
834- dest_slab_allocation. allocation . offset as u64 * slab. element_layout . slot_size ( ) ,
835- dest_slab_allocation. slot_count as u64 * slab. element_layout . slot_size ( ) ,
836- ) ;
837- // Now that we've done the copy, we can update the allocation record.
838- * src_slab_allocation = dest_slab_allocation. clone ( ) ;
839- }
841+ // Copy the data from the old buffer into the new one.
842+ encoder. copy_buffer_to_buffer (
843+ & old_buffer,
844+ 0 ,
845+ & new_buffer,
846+ 0 ,
847+ slab_to_grow. old_slot_capacity as u64 * slab. element_layout . slot_size ( ) ,
848+ ) ;
840849
841850 let command_buffer = encoder. finish ( ) ;
842851 render_queue. submit ( [ command_buffer] ) ;
@@ -872,16 +881,19 @@ impl GeneralSlab {
872881 layout : ElementLayout ,
873882 data_slot_count : u32 ,
874883 ) -> GeneralSlab {
875- let slab_slot_capacity = ( settings. min_slab_size . div_ceil ( layout. slot_size ( ) ) as u32 )
884+ let initial_slab_slot_capacity = ( settings. min_slab_size . div_ceil ( layout. slot_size ( ) )
885+ as u32 )
886+ . max ( offset_allocator:: ext:: min_allocator_size ( data_slot_count) ) ;
887+ let max_slab_slot_capacity = ( settings. max_slab_size . div_ceil ( layout. slot_size ( ) ) as u32 )
876888 . max ( offset_allocator:: ext:: min_allocator_size ( data_slot_count) ) ;
877889
878890 let mut new_slab = GeneralSlab {
879- allocator : Allocator :: new ( slab_slot_capacity ) ,
891+ allocator : Allocator :: new ( max_slab_slot_capacity ) ,
880892 buffer : None ,
881893 resident_allocations : HashMap :: default ( ) ,
882894 pending_allocations : HashMap :: default ( ) ,
883895 element_layout : layout,
884- slot_capacity : slab_slot_capacity ,
896+ current_slot_capacity : initial_slab_slot_capacity ,
885897 } ;
886898
887899 // This should never fail.
@@ -898,68 +910,40 @@ impl GeneralSlab {
898910 new_slab
899911 }
900912
901- /// Attempts to grow a slab that's just run out of space.
913+ /// Checks to see if the size of this slab is at least `new_size_in_slots`
914+ /// and grows the slab if it isn't.
902915 ///
903- /// Returns a structure the allocations that need to be relocated if the
904- /// growth succeeded. If the slab is full, returns `Err`.
905- fn try_grow ( & mut self , settings : & MeshAllocatorSettings ) -> Result < SlabToReallocate , ( ) > {
906- // In extremely rare cases due to allocator fragmentation, it may happen
907- // that we fail to re-insert every object that was in the slab after
908- // growing it. Even though this will likely never happen, we use this
909- // loop to handle this unlikely event properly if it does.
910- ' grow: loop {
911- let new_slab_slot_capacity = ( ( self . slot_capacity as f64 * settings. growth_factor )
912- . ceil ( ) as u32 )
913- . min ( ( settings. max_slab_size / self . element_layout . slot_size ( ) ) as u32 ) ;
914- if new_slab_slot_capacity == self . slot_capacity {
915- // The slab is full.
916- return Err ( ( ) ) ;
917- }
918-
919- // Grow the slab.
920- self . allocator = Allocator :: new ( new_slab_slot_capacity) ;
921- self . slot_capacity = new_slab_slot_capacity;
922-
923- let mut slab_to_grow = SlabToReallocate :: default ( ) ;
924-
925- // Place every resident allocation that was in the old slab in the
926- // new slab.
927- for ( allocated_mesh_id, old_allocation_range) in & self . resident_allocations {
928- let allocation_size = old_allocation_range. slot_count ;
929- match self . allocator . allocate ( allocation_size) {
930- Some ( allocation) => {
931- slab_to_grow. allocations_to_copy . insert (
932- * allocated_mesh_id,
933- SlabAllocation {
934- allocation,
935- slot_count : allocation_size,
936- } ,
937- ) ;
938- }
939- None => {
940- // We failed to insert one of the allocations that we
941- // had before.
942- continue ' grow;
943- }
944- }
945- }
916+ /// The returned [`SlabGrowthResult`] describes whether the slab needed to
917+ /// grow and whether, if so, it was successful in doing so.
918+ fn grow_if_necessary (
919+ & mut self ,
920+ new_size_in_slots : u32 ,
921+ settings : & MeshAllocatorSettings ,
922+ ) -> SlabGrowthResult {
923+ // Is the slab big enough already?
924+ let initial_slot_capacity = self . current_slot_capacity ;
925+ if self . current_slot_capacity >= new_size_in_slots {
926+ return SlabGrowthResult :: NoGrowthNeeded ;
927+ }
946928
947- // Move every allocation that was pending in the old slab to the new
948- // slab.
949- for slab_allocation in self . pending_allocations . values_mut ( ) {
950- let allocation_size = slab_allocation. slot_count ;
951- match self . allocator . allocate ( allocation_size) {
952- Some ( allocation) => slab_allocation. allocation = allocation,
953- None => {
954- // We failed to insert one of the allocations that we
955- // had before.
956- continue ' grow;
957- }
958- }
929+ // Try to grow in increments of `MeshAllocatorSettings::growth_factor`
930+ // until we're big enough.
931+ while self . current_slot_capacity < new_size_in_slots {
932+ let new_slab_slot_capacity =
933+ ( ( self . current_slot_capacity as f64 * settings. growth_factor ) . ceil ( ) as u32 )
934+ . min ( ( settings. max_slab_size / self . element_layout . slot_size ( ) ) as u32 ) ;
935+ if new_slab_slot_capacity == self . current_slot_capacity {
936+ // The slab is full.
937+ return SlabGrowthResult :: CantGrow ;
959938 }
960939
961- return Ok ( slab_to_grow ) ;
940+ self . current_slot_capacity = new_slab_slot_capacity ;
962941 }
942+
943+ // Tell our caller what we did.
944+ SlabGrowthResult :: NeededGrowth ( SlabToReallocate {
945+ old_slot_capacity : initial_slot_capacity,
946+ } )
963947 }
964948}
965949
0 commit comments