Skip to main content

bevy_render/
slab_allocator.rs

1//! A general-purpose allocator that manages a set of GPU buffer slabs.
2
3use alloc::borrow::Cow;
4use bevy_derive::{Deref, DerefMut};
5use bevy_log::error;
6use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
7use core::{
8    cmp::Ordering,
9    fmt::{self, Debug, Display, Formatter},
10    hash::{Hash, Hasher},
11    marker::PhantomData,
12    ops::Range,
13};
14use nonmax::NonMaxU32;
15use offset_allocator::{Allocation, Allocator};
16use wgpu::{BufferDescriptor, BufferSize, BufferUsages, CommandEncoderDescriptor, WriteOnly};
17
18use crate::{
19    render_resource::Buffer,
20    renderer::{RenderDevice, RenderQueue},
21};
22
23/// A general-purpose allocator that manages a set of GPU buffer slabs.
24///
25/// You can use this allocator to pack data that needs to be accessible by the
26/// GPU into a small set of buffers, known as *slabs*. Each individual slab is
27/// expected to contain homogeneous data of a single type. However, you can use
28/// a single allocator to manage multiple slabs, each of which can have a
29/// different data layout. Objects managed by the allocator are referenced with
30/// a *key* that you can define.
31///
32/// To use this allocator, implement the [`SlabItem`] trait; see the
33/// documentation of that trait for details.
34///
35/// For performance, you'll want to batch your allocation and deallocation
36/// operations to be performed at a single point in the frame. To perform
37/// allocation, call [`Self::stage_allocation`] to obtain an
38/// [`AllocationStage`], call [`AllocationStage::allocate`] to allocate
39/// individual objects, and then *commit* the allocation transaction using
40/// [`AllocationStage::commit`]. Likewise, to perform deallocation, call
41/// [`Self::stage_deallocation`] to obtain a [`DeallocationStage`], call
42/// [`DeallocationStage::free`] to free objects, and then call
43/// [`DeallocationStage::commit`]. Once you've committed an allocation stage,
44/// you can copy new data into the slabs via [`Self::copy_element_data`].
45///
46/// Within each slab, or hardware buffer, the underlying allocation algorithm
47/// is [`offset_allocator`], a Rust port of Sebastian Aaltonen's hard-real-time
48/// C++ `OffsetAllocator`. Slabs start small and then grow as their contents
49/// fill up, up to a maximum size limit. To reduce fragmentation, objects that
50/// are too large bypass this system and receive their own buffers.
51///
52/// The [`SlabAllocatorSettings`] allows you to tune the behavior of the
53/// allocator for better performance with your use case.
54///
55/// See [`crate::mesh::allocator::MeshAllocator`] for an example of usage.
56pub struct SlabAllocator<I>
57where
58    I: SlabItem,
59{
60    /// Holds all buffers and allocators.
61    pub slabs: HashMap<SlabId<I>, Slab<I>>,
62
63    /// The next slab ID to assign.
64    next_slab_id: SlabId<I>,
65
66    /// Maps slab allocation keys to the ID of the slabs that hold their data.
67    pub key_to_slab: HashMap<I::Key, SlabId<I>>,
68
69    /// Maps a layout to the slabs that hold elements of that layout.
70    ///
71    /// This is used when allocating, so that we can find the appropriate slab
72    /// to place an object in.
73    slab_layouts: HashMap<I::Layout, Vec<SlabId<I>>>,
74
75    /// Additional buffer usages to add to any vertex or index buffers created.
76    pub extra_buffer_usages: BufferUsages,
77}
78
79/// Describes the type of the data that a [`SlabAllocator`] will store.
80///
81/// The actual type that you implement this trait on doesn't matter; only the
82/// associated types [`Self::Key`] and [`Self::Layout`] do. Typically, you
83/// implement this trait on a unit struct.
84///
85/// See [`crate::mesh::allocator::MeshSlabItem`] for an example of usage.
86pub trait SlabItem {
87    /// The key that's used to look up items in the allocator.
88    type Key: Clone + PartialEq + Eq + Hash;
89
90    /// A type that describes the layout of items within a single slab.
91    ///
92    /// If this slab allocator only allocates items of a single type, this type
93    /// can simply be a unit struct. However, if you wish to have a single slab
94    /// allocator that manages slabs of differing types, you can store metadata
95    /// within values of this type that describes the size and alignment
96    /// requirements of the objects within the slab. Each slab that the slab
97    /// allocator manages contains an instance of this value so that it can
98    /// track size and alignment requirements for that slab.
99    type Layout: SlabItemLayout;
100
101    /// Returns a suitable debugging label describing the type of elements that
102    /// this slab item stores.
103    fn label() -> Cow<'static, str>;
104}
105
106/// A trait that defines information necessary to determine the size and
107/// alignment of objects within a slab.
108pub trait SlabItemLayout: Clone + PartialEq + Eq + Hash {
109    /// The size in bytes of a single element.
110    ///
111    /// This is the smallest size that this allocator can allocate, and all
112    /// allocations must have a byte size that is a multiple of this value.
113    fn size(&self) -> u64;
114
115    /// The number of elements that make up a single slot.
116    fn elements_per_slot(&self) -> u32;
117
118    /// The `wgpu` buffer usages that the slab allocator will specify when
119    /// creating buffers.
120    ///
121    /// `BufferUsages::COPY_DST` and `BufferUsages::COPY_SRC` are always
122    /// included, regardless of what you specify here.
123    fn buffer_usages(&self) -> BufferUsages;
124}
125
126/// Internal helper methods for [`SlabItemLayout`]s.
127trait SlabItemLayoutExt {
128    /// Returns the size in bytes of a single slot.
129    fn slot_size(&self) -> u64;
130}
131
132impl<I> SlabItemLayoutExt for I
133where
134    I: SlabItemLayout,
135{
136    fn slot_size(&self) -> u64 {
137        self.size() * self.elements_per_slot() as u64
138    }
139}
140
141/// Tunable parameters that customize the behavior of the allocator.
142///
143/// Generally, these parameters adjust the tradeoff between memory fragmentation
144/// and performance. You can adjust them as desired for your application. Most
145/// applications can stick with the default values.
146pub struct SlabAllocatorSettings {
147    /// The minimum size of a slab (hardware buffer), in bytes.
148    ///
149    /// The default value is 1 MiB.
150    pub min_slab_size: u64,
151
152    /// The maximum size of a slab (hardware buffer), in bytes.
153    ///
154    /// When a slab reaches this limit, a new slab is created.
155    ///
156    /// The default value is 512 MiB.
157    pub max_slab_size: u64,
158
159    /// The maximum size of vertex or index data that can be placed in a general
160    /// slab, in bytes.
161    ///
162    /// If an allocation exceeds this size limit, that data is placed in its own
163    /// slab. This reduces fragmentation at the cost of more buffer management
164    /// overhead.
165    ///
166    /// The default value is 256 MiB.
167    pub large_threshold: u64,
168
169    /// The factor by which we scale a slab when growing it.
170    ///
171    /// This value must be greater than 1. Higher values result in more
172    /// fragmentation but fewer expensive copy operations when growing the
173    /// buffer.
174    ///
175    /// The default value is 1.5.
176    pub growth_factor: f64,
177}
178
179impl Default for SlabAllocatorSettings {
180    fn default() -> Self {
181        Self {
182            // 1 MiB
183            min_slab_size: 1024 * 1024,
184            // 512 MiB
185            max_slab_size: 1024 * 1024 * 512,
186            // 256 MiB
187            large_threshold: 1024 * 1024 * 256,
188            // 1.5× growth
189            growth_factor: 1.5,
190        }
191    }
192}
193
194/// The index of a single slab.
195#[derive(Deref, DerefMut)]
196#[repr(transparent)]
197pub struct SlabId<I>
198where
199    I: SlabItem,
200{
201    /// A value that represents the ID of the slab.
202    #[deref]
203    pub id: NonMaxU32,
204    phantom: PhantomData<I>,
205}
206
207impl<I> Clone for SlabId<I>
208where
209    I: SlabItem,
210{
211    fn clone(&self) -> Self {
212        *self
213    }
214}
215
216impl<I> Copy for SlabId<I> where I: SlabItem {}
217
218impl<I> Default for SlabId<I>
219where
220    I: SlabItem,
221{
222    fn default() -> Self {
223        SlabId {
224            id: NonMaxU32::default(),
225            phantom: PhantomData,
226        }
227    }
228}
229
230impl<I> PartialEq for SlabId<I>
231where
232    I: SlabItem,
233{
234    fn eq(&self, other: &Self) -> bool {
235        self.id == other.id
236    }
237}
238
239impl<I> Eq for SlabId<I> where I: SlabItem {}
240
241impl<I> PartialOrd for SlabId<I>
242where
243    I: SlabItem,
244{
245    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
246        Some(self.cmp(other))
247    }
248}
249
250impl<I> Ord for SlabId<I>
251where
252    I: SlabItem,
253{
254    fn cmp(&self, other: &Self) -> Ordering {
255        self.id.cmp(other)
256    }
257}
258
259impl<I> Hash for SlabId<I>
260where
261    I: SlabItem,
262{
263    fn hash<H: Hasher>(&self, state: &mut H) {
264        self.id.hash(state);
265    }
266}
267
268impl<I> Debug for SlabId<I>
269where
270    I: SlabItem,
271{
272    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
273        f.debug_struct("SlabId").field("id", &self.id).finish()
274    }
275}
276
277/// Data for a single slab.
278#[expect(
279    clippy::large_enum_variant,
280    reason = "See https://github.com/bevyengine/bevy/issues/19220"
281)]
282pub enum Slab<I>
283where
284    I: SlabItem,
285{
286    /// A slab that can contain multiple objects.
287    General(GeneralSlab<I>),
288    /// A slab that contains a single object.
289    LargeObject(LargeObjectSlab<I>),
290}
291
292/// A resizable slab that can contain multiple objects.
293///
294/// This is the normal type of slab used for objects that are below the
295/// [`SlabAllocatorSettings::large_threshold`]. Slabs are divided into *slots*,
296/// which are described in detail in the [`SlabItemLayout`] documentation.
297pub struct GeneralSlab<I>
298where
299    I: SlabItem,
300{
301    /// The [`Allocator`] that manages the objects in this slab.
302    allocator: Allocator,
303
304    /// The GPU buffer that backs this slab.
305    ///
306    /// This may be `None` if the buffer hasn't been created yet. We delay
307    /// creation of buffers until performing all the allocations for a single
308    /// frame, so that we don't needlessly create and resize buffers when many
309    /// objects are allocated all at once.
310    buffer: Option<Buffer>,
311
312    /// Allocations that are on the GPU.
313    ///
314    /// The range is in slots.
315    resident_allocations: HashMap<I::Key, SlabAllocation>,
316
317    /// Allocations that are waiting to be uploaded to the GPU.
318    ///
319    /// The range is in slots.
320    pending_allocations: HashMap<I::Key, SlabAllocation>,
321
322    /// The layout of a single element (vertex or index).
323    element_layout: I::Layout,
324
325    /// The size of this slab in slots.
326    current_slot_capacity: u32,
327}
328
329/// A slab that contains a single object.
330///
331/// Typically, this is for objects that exceed the
332/// [`SlabAllocatorSettings::large_threshold`]. Additionally, some uses of the
333/// slab allocator may wish to force objects to possess their own slab. For
334/// instance, due to platform limitations (vertex arrays on WebGL 2), the mesh
335/// allocator sometimes needs to place meshes that would otherwise be allocated
336/// together with other meshes in their own slab.
337pub struct LargeObjectSlab<I>
338where
339    I: SlabItem,
340{
341    /// The GPU buffer that backs this slab.
342    ///
343    /// This may be `None` if the buffer hasn't been created yet.
344    buffer: Option<Buffer>,
345
346    /// The layout of a single element (vertex or index).
347    element_layout: I::Layout,
348}
349
350/// The location of an allocation and the slab it's contained in.
351struct SlabItemAllocation<I>
352where
353    I: SlabItem,
354{
355    /// The ID of the slab.
356    slab_id: SlabId<I>,
357    /// Holds the actual allocation.
358    slab_allocation: SlabAllocation,
359}
360
361impl<I> Slab<I>
362where
363    I: SlabItem,
364{
365    /// Returns the GPU buffer corresponding to this slab, if it's been
366    /// uploaded.
367    pub fn buffer(&self) -> Option<&Buffer> {
368        match self {
369            Slab::General(general_slab) => general_slab.buffer.as_ref(),
370            Slab::LargeObject(large_object_slab) => large_object_slab.buffer.as_ref(),
371        }
372    }
373
374    /// Returns the size of this slab in bytes.
375    pub fn buffer_size(&self) -> u64 {
376        match self.buffer() {
377            Some(buffer) => buffer.size(),
378            None => 0,
379        }
380    }
381
382    /// Returns the [`SlabItemLayout`] associated with this slab.
383    pub fn element_layout(&self) -> &I::Layout {
384        match self {
385            Slab::General(general_slab) => &general_slab.element_layout,
386            Slab::LargeObject(large_object_slab) => &large_object_slab.element_layout,
387        }
388    }
389}
390
391/// An object that allows batched allocation.
392///
393/// In order to perform allocations, you create one of these objects with
394/// [`SlabAllocator::stage_allocation`], allocate into it with
395/// [`Self::allocate`], and finally commit it with [`Self::commit`]. Always
396/// make sure to call [`Self::commit`]; if you don't, buffers that were
397/// supposed to be enlarged won't be.
398pub struct AllocationStage<'a, I>
399where
400    I: SlabItem,
401{
402    /// The allocator that we're allocating objects into.
403    pub allocator: &'a mut SlabAllocator<I>,
404    /// The set of slabs that have grown and need to be reallocated.
405    slabs_to_reallocate: HashMap<SlabId<I>, SlabToReallocate>,
406    /// IDs of slabs that became empty because everything in them was
407    /// reallocated elsewhere.
408    empty_slabs: HashSet<SlabId<I>>,
409}
410
411impl<'a, I> Drop for AllocationStage<'a, I>
412where
413    I: SlabItem,
414{
415    fn drop(&mut self) {
416        if !self.slabs_to_reallocate.is_empty() || !self.empty_slabs.is_empty() {
417            error!(
418                "Dropping an `AllocationStage` with uncommitted reallocations or slab free \
419                operations. You should call `AllocationStage::commit`."
420            );
421        }
422    }
423}
424
425impl<'a, I> AllocationStage<'a, I>
426where
427    I: SlabItem,
428{
429    /// Allocates space for an object of the given size with the given key and layout.
430    ///
431    /// If the key already corresponds to a live allocation, that allocation is
432    /// freed first. Prefer freeing it through a [`DeallocationStage`] before the
433    /// allocation stage begins, so that the allocator has every one of the
434    /// frame's holes to choose from rather than just this object's.
435    pub fn allocate(
436        &mut self,
437        key: &I::Key,
438        data_byte_len: u64,
439        layout: I::Layout,
440        settings: &SlabAllocatorSettings,
441    ) {
442        self.allocator
443            .free_existing_allocation(key, &mut self.empty_slabs);
444        self.allocator.allocate(
445            key,
446            data_byte_len,
447            layout,
448            &mut self.slabs_to_reallocate,
449            settings,
450        );
451    }
452
453    /// Allocates an object into its own dedicated slab.
454    ///
455    /// As with [`Self::allocate`], a live allocation under the same key is freed
456    /// first.
457    pub fn allocate_large(&mut self, key: &I::Key, layout: I::Layout) {
458        self.allocator
459            .free_existing_allocation(key, &mut self.empty_slabs);
460        self.allocator.allocate_large(key, layout);
461    }
462
463    /// Completes the transaction, performing any queued resize operations.
464    pub fn commit(mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
465        // Drop slabs that were emptied by their contents being reallocated
466        // elsewhere. Do this before growing anything, so that we never create a
467        // buffer for a slab we're about to throw away.
468        self.allocator.free_empty_slabs(self.empty_slabs.drain());
469
470        for (slab_id, slab_to_grow) in self.slabs_to_reallocate.drain() {
471            // The slab may have been freed just above.
472            if !self.allocator.slabs.contains_key(&slab_id) {
473                continue;
474            }
475            self.allocator
476                .reallocate_slab(render_device, render_queue, slab_id, slab_to_grow);
477        }
478    }
479}
480
481/// An object that enables batched deallocation.
482///
483/// To free objects from a [`SlabAllocator`], call
484/// [`SlabAllocator::stage_deallocation`] to create a [`DeallocationStage`],
485/// call [`Self::free`] to deallocate objects, and finally call
486/// [`Self::commit`]. You must call [`Self::commit`] in order to ensure that
487/// newly-empty slabs are deallocated.
488pub struct DeallocationStage<'a, I>
489where
490    I: SlabItem,
491{
492    /// The allocator in which objects are to be freed.
493    pub allocator: &'a mut SlabAllocator<I>,
494    /// IDs of slabs that have become empty.
495    empty_slabs: HashSet<SlabId<I>>,
496}
497
498impl<'a, I> Drop for DeallocationStage<'a, I>
499where
500    I: SlabItem,
501{
502    fn drop(&mut self) {
503        if !self.empty_slabs.is_empty() {
504            error!(
505                "Dropping a `DeallocationStage` with uncommitted slab free operations. You should \
506                call `DeallocationStage::commit`."
507            );
508        }
509    }
510}
511
512impl<'a, I> DeallocationStage<'a, I>
513where
514    I: SlabItem,
515{
516    /// Schedules a free operation for the allocation with the given key.
517    ///
518    /// Freeing a key that holds no allocation is a no-op, so callers are free to
519    /// speculatively free keys that may never have been allocated.
520    pub fn free(&mut self, key: &I::Key) {
521        self.allocator
522            .free_existing_allocation(key, &mut self.empty_slabs);
523    }
524
525    /// Performs all the free operations.
526    ///
527    /// You must call this method if you called [`Self::free`].
528    pub fn commit(mut self) {
529        self.allocator.free_empty_slabs(self.empty_slabs.drain());
530    }
531}
532
533/// An allocation within a slab.
534#[derive(Clone)]
535struct SlabAllocation {
536    /// The actual [`Allocator`] handle, needed to free the allocation.
537    allocation: Allocation,
538    /// The number of slots that this allocation takes up.
539    slot_count: u32,
540    /// The number of slots at the end of the allocation that are considered
541    /// padding.
542    padding: u32,
543}
544
545/// The hardware buffer that slab-allocated data lives in, as well as the range
546/// within that buffer.
547pub struct SlabAllocationBufferSlice<'a, I>
548where
549    I: SlabItem,
550{
551    /// The buffer that the data resides in.
552    pub buffer: &'a Buffer,
553
554    /// The range of elements within this buffer that the data resides in,
555    /// measured in elements.
556    ///
557    /// This is an element range, not a byte range. For vertex data, this is
558    /// measured in increments of a single vertex. (Thus, if a vertex is 32
559    /// bytes long, then this range is in units of 32 bytes each.) For index
560    /// data, this is measured in increments of a single index value (2 or 4
561    /// bytes). Draw commands generally take their ranges in elements, not
562    /// bytes, so this is the most convenient unit in this case.
563    pub range: Range<u32>,
564
565    phantom: PhantomData<I>,
566}
567
568/// Holds information about a slab that's scheduled to be allocated or
569/// reallocated.
570#[derive(Default)]
571pub struct SlabToReallocate {
572    /// The capacity of the slab before we decided to grow it.
573    old_slot_capacity: u32,
574}
575
576impl<I> Display for SlabId<I>
577where
578    I: SlabItem,
579{
580    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
581        Debug::fmt(&self.id, f)
582    }
583}
584
585impl<I> Default for SlabAllocator<I>
586where
587    I: SlabItem,
588{
589    fn default() -> Self {
590        Self {
591            slabs: HashMap::default(),
592            next_slab_id: SlabId {
593                id: NonMaxU32::default(),
594                phantom: PhantomData,
595            },
596            key_to_slab: HashMap::default(),
597            slab_layouts: HashMap::default(),
598            extra_buffer_usages: BufferUsages::empty(),
599        }
600    }
601}
602
603impl<I> SlabAllocator<I>
604where
605    I: SlabItem,
606{
607    /// Creates a new empty slab allocator.
608    pub fn new() -> Self {
609        Self::default()
610    }
611
612    /// Creates an [`AllocationStage`], enabling batched allocation of objects
613    /// in this slab.
614    ///
615    /// Allocation of objects in the slab requires calling this function,
616    /// calling [`AllocationStage::allocate`] on the resulting
617    /// [`AllocationStage`], and finally calling [`AllocationStage::commit`].
618    /// Grouping allocations into a batch, preferably at most one per frame, is
619    /// the most efficient way to perform many allocations at once.
620    pub fn stage_allocation(&'_ mut self) -> AllocationStage<'_, I> {
621        AllocationStage {
622            allocator: self,
623            slabs_to_reallocate: HashMap::default(),
624            empty_slabs: HashSet::default(),
625        }
626    }
627
628    /// Creates a [`DeallocationStage`], enabling batched deallocation.
629    ///
630    /// Deallocation of objects in the slab requires calling this function,
631    /// calling [`DeallocationStage::free`] on the resulting
632    /// [`DeallocationStage`], and finally calling
633    /// [`DeallocationStage::commit`]. Grouping deallocations into a batch,
634    /// preferably at most one per frame, is the most efficient way to perform
635    /// many deallocations at once.
636    pub fn stage_deallocation(&'_ mut self) -> DeallocationStage<'_, I> {
637        DeallocationStage {
638            allocator: self,
639            empty_slabs: HashSet::default(),
640        }
641    }
642
643    /// Allocates space for data with the given byte size and layout in the
644    /// appropriate slab, creating that slab if necessary.
645    fn allocate(
646        &mut self,
647        key: &I::Key,
648        data_byte_len: u64,
649        layout: I::Layout,
650        slabs_to_grow: &mut HashMap<SlabId<I>, SlabToReallocate>,
651        settings: &SlabAllocatorSettings,
652    ) {
653        debug_assert!(!self.key_to_slab.contains_key(key));
654
655        let data_element_count = data_byte_len.div_ceil(layout.size()) as u32;
656        let data_slot_count = data_element_count.div_ceil(layout.elements_per_slot());
657        let padding = data_slot_count * layout.elements_per_slot() - data_element_count;
658
659        // If the data is too large for a slab, give it a slab of its own.
660        if data_slot_count as u64 * layout.slot_size()
661            >= settings.large_threshold.min(settings.max_slab_size)
662        {
663            self.allocate_large(key, layout);
664        } else {
665            self.allocate_general(
666                key,
667                data_slot_count,
668                padding,
669                layout,
670                slabs_to_grow,
671                settings,
672            );
673        }
674    }
675
676    /// Allocates space for data with the given slot size and layout in the
677    /// appropriate general slab.
678    fn allocate_general(
679        &mut self,
680        key: &I::Key,
681        data_slot_count: u32,
682        padding: u32,
683        layout: I::Layout,
684        slabs_to_grow: &mut HashMap<SlabId<I>, SlabToReallocate>,
685        settings: &SlabAllocatorSettings,
686    ) {
687        let candidate_slabs = self.slab_layouts.entry(layout.clone()).or_default();
688
689        // Loop through the slabs that accept elements of the appropriate type
690        // and try to allocate the data inside them. We go with the first one
691        // that succeeds.
692        let mut data_allocation = None;
693        for &slab_id in &*candidate_slabs {
694            let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {
695                unreachable!("Slab not found")
696            };
697
698            let Some(allocation) = slab.allocator.allocate(data_slot_count) else {
699                continue;
700            };
701
702            // Try to fit the object in the slab, growing if necessary.
703            match slab.grow_if_necessary(allocation.offset + data_slot_count, settings) {
704                SlabGrowthResult::NoGrowthNeeded => {}
705                SlabGrowthResult::NeededGrowth(slab_to_reallocate) => {
706                    // If we already grew the slab this frame, don't replace the
707                    // `SlabToReallocate` entry. We want to keep the entry
708                    // corresponding to the size that the slab had at the start
709                    // of the frame, so that we can copy only the used portion
710                    // of the initial buffer to the new one.
711                    if let Entry::Vacant(vacant_entry) = slabs_to_grow.entry(slab_id) {
712                        vacant_entry.insert(slab_to_reallocate);
713                    }
714                }
715                SlabGrowthResult::CantGrow => continue,
716            }
717
718            data_allocation = Some(SlabItemAllocation {
719                slab_id,
720                slab_allocation: SlabAllocation {
721                    allocation,
722                    slot_count: data_slot_count,
723                    padding,
724                },
725            });
726            break;
727        }
728
729        // If we still have no allocation, make a new slab.
730        if data_allocation.is_none() {
731            let new_slab_id = self.next_slab_id;
732            self.next_slab_id.id =
733                NonMaxU32::new(self.next_slab_id.id.get() + 1).unwrap_or_default();
734
735            let new_slab = GeneralSlab::new(
736                new_slab_id,
737                &mut data_allocation,
738                settings,
739                layout,
740                data_slot_count,
741                padding,
742            );
743
744            self.slabs.insert(new_slab_id, Slab::General(new_slab));
745            candidate_slabs.push(new_slab_id);
746            slabs_to_grow.insert(new_slab_id, SlabToReallocate::default());
747        }
748
749        let data_allocation = data_allocation.expect("Should have been able to allocate");
750
751        // Mark the allocation as pending. Don't copy it in just yet; further
752        // data loaded this frame may result in its final allocation location
753        // changing.
754        if let Some(Slab::General(general_slab)) = self.slabs.get_mut(&data_allocation.slab_id) {
755            general_slab
756                .pending_allocations
757                .insert(key.clone(), data_allocation.slab_allocation);
758        };
759
760        self.record_allocation(key, data_allocation.slab_id);
761    }
762
763    /// Allocates an object into its own dedicated slab.
764    fn allocate_large(&mut self, key: &I::Key, layout: I::Layout) {
765        let new_slab_id = self.next_slab_id;
766        self.next_slab_id.id = NonMaxU32::new(self.next_slab_id.id.get() + 1).unwrap_or_default();
767
768        self.record_allocation(key, new_slab_id);
769
770        self.slabs.insert(
771            new_slab_id,
772            Slab::LargeObject(LargeObjectSlab {
773                buffer: None,
774                element_layout: layout,
775            }),
776        );
777    }
778
779    /// Frees whatever allocation the given key currently holds, if any.
780    ///
781    /// If this empties the allocation's slab, that slab is added to the
782    /// `empty_slabs` set for the caller to reclaim on commit.
783    fn free_existing_allocation(&mut self, key: &I::Key, empty_slabs: &mut HashSet<SlabId<I>>) {
784        if let Some(slab_id) = self.key_to_slab.remove(key) {
785            self.free_allocation_in_slab(key, slab_id, empty_slabs);
786        }
787    }
788
789    /// Given a slab and the key corresponding to an object within it, marks
790    /// the allocation as free.
791    ///
792    /// If this results in the slab becoming empty, this function adds the slab
793    /// to the `empty_slabs` set.
794    fn free_allocation_in_slab(
795        &mut self,
796        key: &I::Key,
797        slab_id: SlabId<I>,
798        empty_slabs: &mut HashSet<SlabId<I>>,
799    ) {
800        let Some(slab) = self.slabs.get_mut(&slab_id) else {
801            error!("Double free: attempted to free data in a nonexistent slab");
802            return;
803        };
804
805        match *slab {
806            Slab::General(ref mut general_slab) => {
807                let Some(slab_allocation) = general_slab
808                    .resident_allocations
809                    .remove(key)
810                    .or_else(|| general_slab.pending_allocations.remove(key))
811                else {
812                    return;
813                };
814
815                general_slab.allocator.free(slab_allocation.allocation);
816
817                if general_slab.is_empty() {
818                    empty_slabs.insert(slab_id);
819                }
820            }
821            Slab::LargeObject(_) => {
822                empty_slabs.insert(slab_id);
823            }
824        }
825    }
826
827    /// Reallocates a slab that needs to be resized, or allocates a new slab.
828    ///
829    /// This performs the actual growth operation that
830    /// [`GeneralSlab::grow_if_necessary`] scheduled. We do the growth in two
831    /// phases so that, if a slab grows multiple times in the same frame, only
832    /// one new buffer is reallocated, rather than reallocating the buffer
833    /// multiple times.
834    fn reallocate_slab(
835        &mut self,
836        render_device: &RenderDevice,
837        render_queue: &RenderQueue,
838        slab_id: SlabId<I>,
839        slab_to_grow: SlabToReallocate,
840    ) {
841        let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {
842            error!("Couldn't find slab {} to grow", slab_id);
843            return;
844        };
845
846        let old_buffer = slab.buffer.take();
847
848        let buffer_usages =
849            BufferUsages::COPY_SRC | BufferUsages::COPY_DST | slab.element_layout.buffer_usages();
850
851        // Create the buffer.
852        let new_buffer = render_device.create_buffer(&BufferDescriptor {
853            label: Some(&format!(
854                "general {} slab {} ({}buffer)",
855                I::label(),
856                slab_id,
857                buffer_usages_to_str(buffer_usages)
858            )),
859            size: slab.current_slot_capacity as u64 * slab.element_layout.slot_size(),
860            usage: buffer_usages | self.extra_buffer_usages,
861            mapped_at_creation: false,
862        });
863
864        slab.buffer = Some(new_buffer.clone());
865
866        let Some(old_buffer) = old_buffer else { return };
867
868        // In order to do buffer copies, we need a command encoder.
869        let mut encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
870            label: Some(&*format!("{} slab resize encoder", I::label())),
871        });
872
873        // Copy the data from the old buffer into the new one.
874        encoder.copy_buffer_to_buffer(
875            &old_buffer,
876            0,
877            &new_buffer,
878            0,
879            slab_to_grow.old_slot_capacity as u64 * slab.element_layout.slot_size(),
880        );
881
882        let command_buffer = encoder.finish();
883        render_queue.submit([command_buffer]);
884    }
885
886    /// Records the location of the given newly-allocated data in the
887    /// [`Self::key_to_slab`] table.
888    fn record_allocation(&mut self, key: &I::Key, slab_id: SlabId<I>) {
889        self.key_to_slab.insert(key.clone(), slab_id);
890    }
891
892    /// Returns the GPU buffer corresponding to the slab with the given ID if
893    /// that slab has been uploaded to the GPU.
894    pub fn buffer_for_slab(&self, slab_id: SlabId<I>) -> Option<&Buffer> {
895        self.slabs.get(&slab_id).and_then(|slab| slab.buffer())
896    }
897
898    /// Given a slab and the key of data located with it, returns the buffer
899    /// and range of that data within the slab.
900    pub fn slab_allocation_slice(
901        &self,
902        key: &I::Key,
903        slab_id: SlabId<I>,
904    ) -> Option<SlabAllocationBufferSlice<'_, I>> {
905        match self.slabs.get(&slab_id)? {
906            Slab::General(general_slab) => {
907                let slab_allocation = general_slab.resident_allocations.get(key)?;
908                Some(SlabAllocationBufferSlice {
909                    buffer: general_slab.buffer.as_ref()?,
910                    range: (slab_allocation.allocation.offset
911                        * general_slab.element_layout.elements_per_slot())
912                        ..((slab_allocation.allocation.offset + slab_allocation.slot_count)
913                            * general_slab.element_layout.elements_per_slot())
914                            - slab_allocation.padding,
915                    phantom: PhantomData,
916                })
917            }
918
919            Slab::LargeObject(large_object_slab) => {
920                let buffer = large_object_slab.buffer.as_ref()?;
921                Some(SlabAllocationBufferSlice {
922                    buffer,
923                    range: 0..((buffer.size() / large_object_slab.element_layout.size()) as u32),
924                    phantom: PhantomData,
925                })
926            }
927        }
928    }
929
930    fn free_empty_slabs(&mut self, empty_slabs: impl Iterator<Item = SlabId<I>>) {
931        for empty_slab in empty_slabs {
932            // The slab may have been refilled since it was marked empty, because
933            // a reallocated object usually lands back in the hole it just left.
934            // Destroying the slab here would take live data with it, so skip.
935            if let Some(Slab::General(general_slab)) = self.slabs.get(&empty_slab)
936                && !general_slab.is_empty()
937            {
938                continue;
939            }
940
941            self.slab_layouts.values_mut().for_each(|slab_ids| {
942                let idx = slab_ids.iter().position(|&slab_id| slab_id == empty_slab);
943                if let Some(idx) = idx {
944                    slab_ids.remove(idx);
945                }
946            });
947            self.slabs.remove(&empty_slab);
948        }
949    }
950
951    /// Get the number of allocated slabs
952    pub fn slab_count(&self) -> usize {
953        self.slabs.len()
954    }
955
956    /// Get the total size of all allocated slabs
957    pub fn slabs_size(&self) -> u64 {
958        self.slabs.iter().map(|slab| slab.1.buffer_size()).sum()
959    }
960
961    /// Copies data into an allocated slab.
962    ///
963    /// `len` specifies the size of the data to be copied *in bytes*. The given
964    /// `fill_data` callback is expected to write the data into the given slice;
965    /// this callback approach avoids a copy.
966    pub fn copy_element_data(
967        &mut self,
968        key: &I::Key,
969        len: usize,
970        fill_data: impl Fn(WriteOnly<[u8]>),
971        render_device: &RenderDevice,
972        render_queue: &RenderQueue,
973    ) {
974        let Some(slab_id) = self.key_to_slab.get(key) else {
975            error!("Use-after-free: attempted to copy element data for an unallocated key");
976            return;
977        };
978        let Some(slab) = self.slabs.get_mut(slab_id) else {
979            error!("Use-after-free: attempted to copy element data into a nonexistent slab");
980            return;
981        };
982
983        match *slab {
984            Slab::General(ref mut general_slab) => {
985                let (Some(buffer), Some(allocated_range)) = (
986                    &general_slab.buffer,
987                    general_slab.pending_allocations.remove(key),
988                ) else {
989                    return;
990                };
991
992                let slot_size = general_slab.element_layout.slot_size();
993
994                // round up size to a multiple of the slot size to satisfy wgpu
995                // alignment requirements
996                if let Some(size) = BufferSize::new((len as u64).next_multiple_of(slot_size)) {
997                    // Write the data in.
998                    if let Some(mut buffer) = render_queue.write_buffer_with(
999                        buffer,
1000                        allocated_range.allocation.offset as u64 * slot_size,
1001                        size,
1002                    ) {
1003                        let slice = buffer.slice(..len);
1004                        fill_data(slice);
1005                    }
1006                }
1007
1008                // Mark the allocation as resident.
1009                general_slab
1010                    .resident_allocations
1011                    .insert(key.clone(), allocated_range);
1012            }
1013
1014            Slab::LargeObject(ref mut large_object_slab) => {
1015                debug_assert!(large_object_slab.buffer.is_none());
1016
1017                // Create the buffer and its data in one go.
1018                let buffer_usages = large_object_slab.element_layout.buffer_usages();
1019                let buffer = render_device.create_buffer(&BufferDescriptor {
1020                    label: Some(&format!(
1021                        "large {} slab {} ({}buffer)",
1022                        I::label(),
1023                        slab_id,
1024                        buffer_usages_to_str(buffer_usages)
1025                    )),
1026                    size: len as u64,
1027                    usage: buffer_usages | BufferUsages::COPY_DST,
1028                    mapped_at_creation: true,
1029                });
1030                {
1031                    let mut slice = buffer.slice(..).get_mapped_range_mut();
1032
1033                    fill_data(slice.slice(..len));
1034                }
1035                buffer.unmap();
1036                large_object_slab.buffer = Some(buffer);
1037            }
1038        }
1039    }
1040}
1041
1042/// The results of [`GeneralSlab::grow_if_necessary`].
1043enum SlabGrowthResult {
1044    /// The data already fits in the slab; the slab doesn't need to grow.
1045    NoGrowthNeeded,
1046    /// The slab needed to grow.
1047    ///
1048    /// The [`SlabToReallocate`] contains the old capacity of the slab.
1049    NeededGrowth(SlabToReallocate),
1050    /// The slab wanted to grow but couldn't because it hit its maximum size.
1051    CantGrow,
1052}
1053
1054impl<I> GeneralSlab<I>
1055where
1056    I: SlabItem,
1057{
1058    /// Creates a new growable slab big enough to hold a single element of
1059    /// `data_slot_count` size with the given `layout`.
1060    fn new(
1061        new_slab_id: SlabId<I>,
1062        maybe_slab_item_allocation: &mut Option<SlabItemAllocation<I>>,
1063        settings: &SlabAllocatorSettings,
1064        layout: I::Layout,
1065        data_slot_count: u32,
1066        padding: u32,
1067    ) -> GeneralSlab<I> {
1068        let initial_slab_slot_capacity = (settings.min_slab_size.div_ceil(layout.slot_size())
1069            as u32)
1070            .max(offset_allocator::ext::min_allocator_size(data_slot_count));
1071        let max_slab_slot_capacity = (settings.max_slab_size.div_ceil(layout.slot_size()) as u32)
1072            .max(offset_allocator::ext::min_allocator_size(data_slot_count));
1073
1074        let mut new_slab = GeneralSlab {
1075            allocator: Allocator::new(max_slab_slot_capacity),
1076            buffer: None,
1077            resident_allocations: HashMap::default(),
1078            pending_allocations: HashMap::default(),
1079            element_layout: layout,
1080            current_slot_capacity: initial_slab_slot_capacity,
1081        };
1082
1083        // This should never fail.
1084        if let Some(allocation) = new_slab.allocator.allocate(data_slot_count) {
1085            *maybe_slab_item_allocation = Some(SlabItemAllocation {
1086                slab_id: new_slab_id,
1087                slab_allocation: SlabAllocation {
1088                    slot_count: data_slot_count,
1089                    allocation,
1090                    padding,
1091                },
1092            });
1093        }
1094
1095        new_slab
1096    }
1097
1098    /// Checks to see if the size of this slab is at least `new_size_in_slots`
1099    /// and grows the slab if it isn't.
1100    ///
1101    /// The returned [`SlabGrowthResult`] describes whether the slab needed to
1102    /// grow and whether, if so, it was successful in doing so.
1103    fn grow_if_necessary(
1104        &mut self,
1105        new_size_in_slots: u32,
1106        settings: &SlabAllocatorSettings,
1107    ) -> SlabGrowthResult {
1108        // Is the slab big enough already?
1109        let initial_slot_capacity = self.current_slot_capacity;
1110        if self.current_slot_capacity >= new_size_in_slots {
1111            return SlabGrowthResult::NoGrowthNeeded;
1112        }
1113
1114        // Try to grow in increments of `SlabAllocatorSettings::growth_factor`
1115        // until we're big enough.
1116        while self.current_slot_capacity < new_size_in_slots {
1117            let new_slab_slot_capacity =
1118                ((self.current_slot_capacity as f64 * settings.growth_factor).ceil() as u32)
1119                    .min((settings.max_slab_size / self.element_layout.slot_size()) as u32);
1120            if new_slab_slot_capacity == self.current_slot_capacity {
1121                // The slab is full.
1122                return SlabGrowthResult::CantGrow;
1123            }
1124
1125            self.current_slot_capacity = new_slab_slot_capacity;
1126        }
1127
1128        // Tell our caller what we did.
1129        SlabGrowthResult::NeededGrowth(SlabToReallocate {
1130            old_slot_capacity: initial_slot_capacity,
1131        })
1132    }
1133
1134    /// Returns true if this slab is empty.
1135    fn is_empty(&self) -> bool {
1136        self.resident_allocations.is_empty() && self.pending_allocations.is_empty()
1137    }
1138}
1139
1140/// Returns a string describing the given buffer usages.
1141fn buffer_usages_to_str(buffer_usages: BufferUsages) -> &'static str {
1142    if buffer_usages.contains(BufferUsages::VERTEX) {
1143        "vertex "
1144    } else if buffer_usages.contains(BufferUsages::INDEX) {
1145        "index "
1146    } else if buffer_usages.contains(BufferUsages::STORAGE) {
1147        "storage "
1148    } else {
1149        ""
1150    }
1151}
1152
1153#[cfg(test)]
1154mod tests {
1155    use super::*;
1156    use crate::test_utils::create_dummy_device;
1157
1158    /// A [`SlabItem`] for tests, keyed by a plain integer.
1159    struct TestItem;
1160
1161    impl SlabItem for TestItem {
1162        type Key = u32;
1163        type Layout = TestLayout;
1164
1165        fn label() -> Cow<'static, str> {
1166            "test".into()
1167        }
1168    }
1169
1170    /// A four-byte element, one element per slot.
1171    #[derive(Clone, PartialEq, Eq, Hash)]
1172    struct TestLayout;
1173
1174    impl SlabItemLayout for TestLayout {
1175        fn size(&self) -> u64 {
1176            4
1177        }
1178
1179        fn elements_per_slot(&self) -> u32 {
1180            1
1181        }
1182
1183        fn buffer_usages(&self) -> BufferUsages {
1184            BufferUsages::VERTEX
1185        }
1186    }
1187
1188    /// Small slabs, so that a leak shows up as slab growth within a few rounds.
1189    fn test_settings() -> SlabAllocatorSettings {
1190        SlabAllocatorSettings {
1191            // 256 slots.
1192            min_slab_size: 1024,
1193            // 1024 slots.
1194            max_slab_size: 4096,
1195            large_threshold: 4096,
1196            growth_factor: 1.5,
1197        }
1198    }
1199
1200    /// Allocates `byte_len` bytes under each of `keys` and marks the results
1201    /// resident, as a frame of [`MeshAllocator`](crate::mesh::MeshAllocator)
1202    /// would.
1203    fn allocate_round(
1204        allocator: &mut SlabAllocator<TestItem>,
1205        keys: impl Iterator<Item = u32> + Clone,
1206        byte_len: u64,
1207        device: &RenderDevice,
1208        queue: &RenderQueue,
1209    ) {
1210        let mut stage = allocator.stage_allocation();
1211        for key in keys.clone() {
1212            stage.allocate(&key, byte_len, TestLayout, &test_settings());
1213        }
1214        stage.commit(device, queue);
1215
1216        for key in keys {
1217            allocator.copy_element_data(&key, byte_len as usize, |_| {}, device, queue);
1218        }
1219    }
1220
1221    /// Reallocating a key that's still live must free its previous allocation
1222    /// rather than orphaning it.
1223    #[test]
1224    fn reallocating_a_live_key_frees_the_old_allocation() {
1225        let (device, queue) = create_dummy_device();
1226        let mut allocator = SlabAllocator::<TestItem>::new();
1227
1228        allocate_round(&mut allocator, 0..8, 256, &device, &queue);
1229
1230        let baseline_size = allocator.slabs_size();
1231        let baseline_slabs = allocator.slab_count();
1232        assert!(baseline_size > 0, "nothing was allocated");
1233
1234        // Reallocate the same keys, without ever freeing them, many times over.
1235        for _ in 0..32 {
1236            allocate_round(&mut allocator, 0..8, 256, &device, &queue);
1237        }
1238
1239        assert_eq!(
1240            allocator.slabs_size(),
1241            baseline_size,
1242            "reallocating live keys grew the slabs, so old allocations leaked"
1243        );
1244        assert_eq!(allocator.slab_count(), baseline_slabs);
1245        assert_eq!(allocator.key_to_slab.len(), 8);
1246
1247        // Every key must still be readable.
1248        for key in 0..8u32 {
1249            let slab_id = allocator.key_to_slab[&key];
1250            assert!(allocator.slab_allocation_slice(&key, slab_id).is_some());
1251        }
1252    }
1253
1254    /// A slab emptied by a reallocation that lands back in that same slab must
1255    /// not be destroyed on commit.
1256    #[test]
1257    fn slab_refilled_during_allocation_is_not_freed() {
1258        let (device, queue) = create_dummy_device();
1259        let mut allocator = SlabAllocator::<TestItem>::new();
1260
1261        allocate_round(&mut allocator, 0..1, 256, &device, &queue);
1262        let slab_id = allocator.key_to_slab[&0];
1263
1264        // Freeing the sole occupant marks the slab empty mid-stage; the
1265        // reallocation then drops straight back into it.
1266        allocate_round(&mut allocator, 0..1, 256, &device, &queue);
1267
1268        assert_eq!(allocator.slab_count(), 1, "the live slab was destroyed");
1269        assert_eq!(allocator.key_to_slab[&0], slab_id);
1270        assert!(allocator.slab_allocation_slice(&0, slab_id).is_some());
1271    }
1272
1273    /// Allocates a dedicated slab for each of `keys` through
1274    /// [`AllocationStage::allocate_large`].
1275    fn allocate_large_round(
1276        allocator: &mut SlabAllocator<TestItem>,
1277        keys: impl Iterator<Item = u32>,
1278        device: &RenderDevice,
1279        queue: &RenderQueue,
1280    ) {
1281        let mut stage = allocator.stage_allocation();
1282        for key in keys {
1283            stage.allocate_large(&key, TestLayout);
1284        }
1285        stage.commit(device, queue);
1286    }
1287
1288    /// [`AllocationStage::allocate_large`] must free a live allocation under the
1289    /// same key, just as [`AllocationStage::allocate`] does. Each one takes a
1290    /// brand new slab, so a missed free leaks a whole slab per round.
1291    #[test]
1292    fn reallocating_a_live_key_with_allocate_large_frees_the_old_allocation() {
1293        let (device, queue) = create_dummy_device();
1294        let mut allocator = SlabAllocator::<TestItem>::new();
1295
1296        allocate_large_round(&mut allocator, 0..4, &device, &queue);
1297        assert_eq!(allocator.slab_count(), 4);
1298
1299        for _ in 0..32 {
1300            allocate_large_round(&mut allocator, 0..4, &device, &queue);
1301        }
1302
1303        assert_eq!(
1304            allocator.slab_count(),
1305            4,
1306            "reallocating live keys left the previous dedicated slabs behind"
1307        );
1308        assert_eq!(allocator.key_to_slab.len(), 4);
1309    }
1310
1311    /// A slab genuinely emptied during an allocation stage must be reclaimed, or
1312    /// the fix for reallocation would just trade one leak for another.
1313    #[test]
1314    fn slab_emptied_during_allocation_is_freed() {
1315        let (device, queue) = create_dummy_device();
1316        let mut allocator = SlabAllocator::<TestItem>::new();
1317
1318        allocate_round(&mut allocator, 0..1, 256, &device, &queue);
1319        assert_eq!(allocator.slab_count(), 1);
1320
1321        // Reallocate the sole occupant at a size that forces it into a slab of
1322        // its own, leaving the general slab empty.
1323        allocate_round(&mut allocator, 0..1, 8192, &device, &queue);
1324
1325        assert_eq!(
1326            allocator.slab_count(),
1327            1,
1328            "the emptied general slab was not reclaimed"
1329        );
1330        let slab_id = allocator.key_to_slab[&0];
1331        assert!(matches!(
1332            allocator.slabs.get(&slab_id),
1333            Some(Slab::LargeObject(_))
1334        ));
1335    }
1336}