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