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}
407
408impl<'a, I> Drop for AllocationStage<'a, I>
409where
410    I: SlabItem,
411{
412    fn drop(&mut self) {
413        if !self.slabs_to_reallocate.is_empty() {
414            error!(
415                "Dropping an `AllocationStage` with uncommitted reallocations. You should call \
416                `AllocationStage::commit`."
417            );
418        }
419    }
420}
421
422impl<'a, I> AllocationStage<'a, I>
423where
424    I: SlabItem,
425{
426    /// Allocates space for an object of the given size with the given key and layout.
427    ///
428    /// The key must not correspond to any current allocation.
429    pub fn allocate(
430        &mut self,
431        key: &I::Key,
432        data_byte_len: u64,
433        layout: I::Layout,
434        settings: &SlabAllocatorSettings,
435    ) {
436        self.allocator.allocate(
437            key,
438            data_byte_len,
439            layout,
440            &mut self.slabs_to_reallocate,
441            settings,
442        );
443    }
444
445    /// Allocates an object into its own dedicated slab.
446    ///
447    /// The key must not correspond to any current allocation.
448    pub fn allocate_large(&mut self, key: &I::Key, layout: I::Layout) {
449        self.allocator.allocate_large(key, layout);
450    }
451
452    /// Completes the transaction, performing any queued resize operations.
453    pub fn commit(mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
454        for (slab_id, slab_to_grow) in self.slabs_to_reallocate.drain() {
455            self.allocator
456                .reallocate_slab(render_device, render_queue, slab_id, slab_to_grow);
457        }
458    }
459}
460
461/// An object that enables batched deallocation.
462///
463/// To free objects from a [`SlabAllocator`], call
464/// [`SlabAllocator::stage_deallocation`] to create a [`DeallocationStage`],
465/// call [`Self::free`] to deallocate objects, and finally call
466/// [`Self::commit`]. You must call [`Self::commit`] in order to ensure that
467/// newly-empty slabs are deallocated.
468pub struct DeallocationStage<'a, I>
469where
470    I: SlabItem,
471{
472    /// The allocator in which objects are to be freed.
473    pub allocator: &'a mut SlabAllocator<I>,
474    /// IDs of slabs that have become empty.
475    empty_slabs: HashSet<SlabId<I>>,
476}
477
478impl<'a, I> Drop for DeallocationStage<'a, I>
479where
480    I: SlabItem,
481{
482    fn drop(&mut self) {
483        if !self.empty_slabs.is_empty() {
484            error!(
485                "Dropping a `DeallocationStage` with uncommitted slab free operations. You should \
486                call `DeallocationStage::commit`."
487            );
488        }
489    }
490}
491
492impl<'a, I> DeallocationStage<'a, I>
493where
494    I: SlabItem,
495{
496    /// Schedules a free operation for the allocation with the given key.
497    ///
498    /// The key must correspond to a live allocation. An error will be emitted
499    /// to the log otherwise.
500    pub fn free(&mut self, key: &I::Key) {
501        if let Some(slab_id) = self.allocator.key_to_slab.remove(key) {
502            self.allocator
503                .free_allocation_in_slab(key, slab_id, &mut self.empty_slabs);
504        }
505    }
506
507    /// Performs all the free operations.
508    ///
509    /// You must call this method if you called [`Self::free`].
510    pub fn commit(mut self) {
511        self.allocator.free_empty_slabs(self.empty_slabs.drain());
512    }
513}
514
515/// An allocation within a slab.
516#[derive(Clone)]
517struct SlabAllocation {
518    /// The actual [`Allocator`] handle, needed to free the allocation.
519    allocation: Allocation,
520    /// The number of slots that this allocation takes up.
521    slot_count: u32,
522    /// The number of slots at the end of the allocation that are considered
523    /// padding.
524    padding: u32,
525}
526
527/// The hardware buffer that slab-allocated data lives in, as well as the range
528/// within that buffer.
529pub struct SlabAllocationBufferSlice<'a, I>
530where
531    I: SlabItem,
532{
533    /// The buffer that the data resides in.
534    pub buffer: &'a Buffer,
535
536    /// The range of elements within this buffer that the data resides in,
537    /// measured in elements.
538    ///
539    /// This is an element range, not a byte range. For vertex data, this is
540    /// measured in increments of a single vertex. (Thus, if a vertex is 32
541    /// bytes long, then this range is in units of 32 bytes each.) For index
542    /// data, this is measured in increments of a single index value (2 or 4
543    /// bytes). Draw commands generally take their ranges in elements, not
544    /// bytes, so this is the most convenient unit in this case.
545    pub range: Range<u32>,
546
547    phantom: PhantomData<I>,
548}
549
550/// Holds information about a slab that's scheduled to be allocated or
551/// reallocated.
552#[derive(Default)]
553pub struct SlabToReallocate {
554    /// The capacity of the slab before we decided to grow it.
555    old_slot_capacity: u32,
556}
557
558impl<I> Display for SlabId<I>
559where
560    I: SlabItem,
561{
562    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
563        Debug::fmt(&self.id, f)
564    }
565}
566
567impl<I> Default for SlabAllocator<I>
568where
569    I: SlabItem,
570{
571    fn default() -> Self {
572        Self {
573            slabs: HashMap::default(),
574            next_slab_id: SlabId {
575                id: NonMaxU32::default(),
576                phantom: PhantomData,
577            },
578            key_to_slab: HashMap::default(),
579            slab_layouts: HashMap::default(),
580            extra_buffer_usages: BufferUsages::empty(),
581        }
582    }
583}
584
585impl<I> SlabAllocator<I>
586where
587    I: SlabItem,
588{
589    /// Creates a new empty slab allocator.
590    pub fn new() -> Self {
591        Self::default()
592    }
593
594    /// Creates an [`AllocationStage`], enabling batched allocation of objects
595    /// in this slab.
596    ///
597    /// Allocation of objects in the slab requires calling this function,
598    /// calling [`AllocationStage::allocate`] on the resulting
599    /// [`AllocationStage`], and finally calling [`AllocationStage::commit`].
600    /// Grouping allocations into a batch, preferably at most one per frame, is
601    /// the most efficient way to perform many allocations at once.
602    pub fn stage_allocation(&'_ mut self) -> AllocationStage<'_, I> {
603        AllocationStage {
604            allocator: self,
605            slabs_to_reallocate: HashMap::default(),
606        }
607    }
608
609    /// Creates a [`DeallocationStage`], enabling batched deallocation.
610    ///
611    /// Deallocation of objects in the slab requires calling this function,
612    /// calling [`DeallocationStage::free`] on the resulting
613    /// [`DeallocationStage`], and finally calling
614    /// [`DeallocationStage::commit`]. Grouping deallocations into a batch,
615    /// preferably at most one per frame, is the most efficient way to perform
616    /// many deallocations at once.
617    pub fn stage_deallocation(&'_ mut self) -> DeallocationStage<'_, I> {
618        DeallocationStage {
619            allocator: self,
620            empty_slabs: HashSet::default(),
621        }
622    }
623
624    /// Allocates space for data with the given byte size and layout in the
625    /// appropriate slab, creating that slab if necessary.
626    fn allocate(
627        &mut self,
628        key: &I::Key,
629        data_byte_len: u64,
630        layout: I::Layout,
631        slabs_to_grow: &mut HashMap<SlabId<I>, SlabToReallocate>,
632        settings: &SlabAllocatorSettings,
633    ) {
634        debug_assert!(!self.key_to_slab.contains_key(key));
635
636        let data_element_count = data_byte_len.div_ceil(layout.size()) as u32;
637        let data_slot_count = data_element_count.div_ceil(layout.elements_per_slot());
638        let padding = data_slot_count * layout.elements_per_slot() - data_element_count;
639
640        // If the data is too large for a slab, give it a slab of its own.
641        if data_slot_count as u64 * layout.slot_size()
642            >= settings.large_threshold.min(settings.max_slab_size)
643        {
644            self.allocate_large(key, layout);
645        } else {
646            self.allocate_general(
647                key,
648                data_slot_count,
649                padding,
650                layout,
651                slabs_to_grow,
652                settings,
653            );
654        }
655    }
656
657    /// Allocates space for data with the given slot size and layout in the
658    /// appropriate general slab.
659    fn allocate_general(
660        &mut self,
661        key: &I::Key,
662        data_slot_count: u32,
663        padding: u32,
664        layout: I::Layout,
665        slabs_to_grow: &mut HashMap<SlabId<I>, SlabToReallocate>,
666        settings: &SlabAllocatorSettings,
667    ) {
668        let candidate_slabs = self.slab_layouts.entry(layout.clone()).or_default();
669
670        // Loop through the slabs that accept elements of the appropriate type
671        // and try to allocate the data inside them. We go with the first one
672        // that succeeds.
673        let mut data_allocation = None;
674        for &slab_id in &*candidate_slabs {
675            let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {
676                unreachable!("Slab not found")
677            };
678
679            let Some(allocation) = slab.allocator.allocate(data_slot_count) else {
680                continue;
681            };
682
683            // Try to fit the object in the slab, growing if necessary.
684            match slab.grow_if_necessary(allocation.offset + data_slot_count, settings) {
685                SlabGrowthResult::NoGrowthNeeded => {}
686                SlabGrowthResult::NeededGrowth(slab_to_reallocate) => {
687                    // If we already grew the slab this frame, don't replace the
688                    // `SlabToReallocate` entry. We want to keep the entry
689                    // corresponding to the size that the slab had at the start
690                    // of the frame, so that we can copy only the used portion
691                    // of the initial buffer to the new one.
692                    if let Entry::Vacant(vacant_entry) = slabs_to_grow.entry(slab_id) {
693                        vacant_entry.insert(slab_to_reallocate);
694                    }
695                }
696                SlabGrowthResult::CantGrow => continue,
697            }
698
699            data_allocation = Some(SlabItemAllocation {
700                slab_id,
701                slab_allocation: SlabAllocation {
702                    allocation,
703                    slot_count: data_slot_count,
704                    padding,
705                },
706            });
707            break;
708        }
709
710        // If we still have no allocation, make a new slab.
711        if data_allocation.is_none() {
712            let new_slab_id = self.next_slab_id;
713            self.next_slab_id.id =
714                NonMaxU32::new(self.next_slab_id.id.get() + 1).unwrap_or_default();
715
716            let new_slab = GeneralSlab::new(
717                new_slab_id,
718                &mut data_allocation,
719                settings,
720                layout,
721                data_slot_count,
722                padding,
723            );
724
725            self.slabs.insert(new_slab_id, Slab::General(new_slab));
726            candidate_slabs.push(new_slab_id);
727            slabs_to_grow.insert(new_slab_id, SlabToReallocate::default());
728        }
729
730        let data_allocation = data_allocation.expect("Should have been able to allocate");
731
732        // Mark the allocation as pending. Don't copy it in just yet; further
733        // data loaded this frame may result in its final allocation location
734        // changing.
735        if let Some(Slab::General(general_slab)) = self.slabs.get_mut(&data_allocation.slab_id) {
736            general_slab
737                .pending_allocations
738                .insert(key.clone(), data_allocation.slab_allocation);
739        };
740
741        self.record_allocation(key, data_allocation.slab_id);
742    }
743
744    /// Allocates an object into its own dedicated slab.
745    fn allocate_large(&mut self, key: &I::Key, layout: I::Layout) {
746        let new_slab_id = self.next_slab_id;
747        self.next_slab_id.id = NonMaxU32::new(self.next_slab_id.id.get() + 1).unwrap_or_default();
748
749        self.record_allocation(key, new_slab_id);
750
751        self.slabs.insert(
752            new_slab_id,
753            Slab::LargeObject(LargeObjectSlab {
754                buffer: None,
755                element_layout: layout,
756            }),
757        );
758    }
759
760    /// Given a slab and the key corresponding to an object within it, marks
761    /// the allocation as free.
762    ///
763    /// If this results in the slab becoming empty, this function adds the slab
764    /// to the `empty_slabs` set.
765    fn free_allocation_in_slab(
766        &mut self,
767        key: &I::Key,
768        slab_id: SlabId<I>,
769        empty_slabs: &mut HashSet<SlabId<I>>,
770    ) {
771        let Some(slab) = self.slabs.get_mut(&slab_id) else {
772            error!("Double free: attempted to free data in a nonexistent slab");
773            return;
774        };
775
776        match *slab {
777            Slab::General(ref mut general_slab) => {
778                let Some(slab_allocation) = general_slab
779                    .resident_allocations
780                    .remove(key)
781                    .or_else(|| general_slab.pending_allocations.remove(key))
782                else {
783                    return;
784                };
785
786                general_slab.allocator.free(slab_allocation.allocation);
787
788                if general_slab.is_empty() {
789                    empty_slabs.insert(slab_id);
790                }
791            }
792            Slab::LargeObject(_) => {
793                empty_slabs.insert(slab_id);
794            }
795        }
796    }
797
798    /// Reallocates a slab that needs to be resized, or allocates a new slab.
799    ///
800    /// This performs the actual growth operation that
801    /// [`GeneralSlab::grow_if_necessary`] scheduled. We do the growth in two
802    /// phases so that, if a slab grows multiple times in the same frame, only
803    /// one new buffer is reallocated, rather than reallocating the buffer
804    /// multiple times.
805    fn reallocate_slab(
806        &mut self,
807        render_device: &RenderDevice,
808        render_queue: &RenderQueue,
809        slab_id: SlabId<I>,
810        slab_to_grow: SlabToReallocate,
811    ) {
812        let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {
813            error!("Couldn't find slab {} to grow", slab_id);
814            return;
815        };
816
817        let old_buffer = slab.buffer.take();
818
819        let buffer_usages =
820            BufferUsages::COPY_SRC | BufferUsages::COPY_DST | slab.element_layout.buffer_usages();
821
822        // Create the buffer.
823        let new_buffer = render_device.create_buffer(&BufferDescriptor {
824            label: Some(&format!(
825                "general {} slab {} ({}buffer)",
826                I::label(),
827                slab_id,
828                buffer_usages_to_str(buffer_usages)
829            )),
830            size: slab.current_slot_capacity as u64 * slab.element_layout.slot_size(),
831            usage: buffer_usages | self.extra_buffer_usages,
832            mapped_at_creation: false,
833        });
834
835        slab.buffer = Some(new_buffer.clone());
836
837        let Some(old_buffer) = old_buffer else { return };
838
839        // In order to do buffer copies, we need a command encoder.
840        let mut encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
841            label: Some(&*format!("{} slab resize encoder", I::label())),
842        });
843
844        // Copy the data from the old buffer into the new one.
845        encoder.copy_buffer_to_buffer(
846            &old_buffer,
847            0,
848            &new_buffer,
849            0,
850            slab_to_grow.old_slot_capacity as u64 * slab.element_layout.slot_size(),
851        );
852
853        let command_buffer = encoder.finish();
854        render_queue.submit([command_buffer]);
855    }
856
857    /// Records the location of the given newly-allocated data in the
858    /// [`Self::key_to_slab`] table.
859    fn record_allocation(&mut self, key: &I::Key, slab_id: SlabId<I>) {
860        self.key_to_slab.insert(key.clone(), slab_id);
861    }
862
863    /// Returns the GPU buffer corresponding to the slab with the given ID if
864    /// that slab has been uploaded to the GPU.
865    pub fn buffer_for_slab(&self, slab_id: SlabId<I>) -> Option<&Buffer> {
866        self.slabs.get(&slab_id).and_then(|slab| slab.buffer())
867    }
868
869    /// Given a slab and the key of data located with it, returns the buffer
870    /// and range of that data within the slab.
871    pub fn slab_allocation_slice(
872        &self,
873        key: &I::Key,
874        slab_id: SlabId<I>,
875    ) -> Option<SlabAllocationBufferSlice<'_, I>> {
876        match self.slabs.get(&slab_id)? {
877            Slab::General(general_slab) => {
878                let slab_allocation = general_slab.resident_allocations.get(key)?;
879                Some(SlabAllocationBufferSlice {
880                    buffer: general_slab.buffer.as_ref()?,
881                    range: (slab_allocation.allocation.offset
882                        * general_slab.element_layout.elements_per_slot())
883                        ..((slab_allocation.allocation.offset + slab_allocation.slot_count)
884                            * general_slab.element_layout.elements_per_slot())
885                            - slab_allocation.padding,
886                    phantom: PhantomData,
887                })
888            }
889
890            Slab::LargeObject(large_object_slab) => {
891                let buffer = large_object_slab.buffer.as_ref()?;
892                Some(SlabAllocationBufferSlice {
893                    buffer,
894                    range: 0..((buffer.size() / large_object_slab.element_layout.size()) as u32),
895                    phantom: PhantomData,
896                })
897            }
898        }
899    }
900
901    fn free_empty_slabs(&mut self, empty_slabs: impl Iterator<Item = SlabId<I>>) {
902        for empty_slab in empty_slabs {
903            self.slab_layouts.values_mut().for_each(|slab_ids| {
904                let idx = slab_ids.iter().position(|&slab_id| slab_id == empty_slab);
905                if let Some(idx) = idx {
906                    slab_ids.remove(idx);
907                }
908            });
909            self.slabs.remove(&empty_slab);
910        }
911    }
912
913    /// Get the number of allocated slabs
914    pub fn slab_count(&self) -> usize {
915        self.slabs.len()
916    }
917
918    /// Get the total size of all allocated slabs
919    pub fn slabs_size(&self) -> u64 {
920        self.slabs.iter().map(|slab| slab.1.buffer_size()).sum()
921    }
922
923    /// Copies data into an allocated slab.
924    ///
925    /// `len` specifies the size of the data to be copied *in bytes*. The given
926    /// `fill_data` callback is expected to write the data into the given slice;
927    /// this callback approach avoids a copy.
928    pub fn copy_element_data(
929        &mut self,
930        key: &I::Key,
931        len: usize,
932        fill_data: impl Fn(WriteOnly<[u8]>),
933        render_device: &RenderDevice,
934        render_queue: &RenderQueue,
935    ) {
936        let Some(slab_id) = self.key_to_slab.get(key) else {
937            error!("Use-after-free: attempted to copy element data for an unallocated key");
938            return;
939        };
940        let Some(slab) = self.slabs.get_mut(slab_id) else {
941            error!("Use-after-free: attempted to copy element data into a nonexistent slab");
942            return;
943        };
944
945        match *slab {
946            Slab::General(ref mut general_slab) => {
947                let (Some(buffer), Some(allocated_range)) = (
948                    &general_slab.buffer,
949                    general_slab.pending_allocations.remove(key),
950                ) else {
951                    return;
952                };
953
954                let slot_size = general_slab.element_layout.slot_size();
955
956                // round up size to a multiple of the slot size to satisfy wgpu
957                // alignment requirements
958                if let Some(size) = BufferSize::new((len as u64).next_multiple_of(slot_size)) {
959                    // Write the data in.
960                    if let Some(mut buffer) = render_queue.write_buffer_with(
961                        buffer,
962                        allocated_range.allocation.offset as u64 * slot_size,
963                        size,
964                    ) {
965                        let slice = buffer.slice(..len);
966                        fill_data(slice);
967                    }
968                }
969
970                // Mark the allocation as resident.
971                general_slab
972                    .resident_allocations
973                    .insert(key.clone(), allocated_range);
974            }
975
976            Slab::LargeObject(ref mut large_object_slab) => {
977                debug_assert!(large_object_slab.buffer.is_none());
978
979                // Create the buffer and its data in one go.
980                let buffer_usages = large_object_slab.element_layout.buffer_usages();
981                let buffer = render_device.create_buffer(&BufferDescriptor {
982                    label: Some(&format!(
983                        "large {} slab {} ({}buffer)",
984                        I::label(),
985                        slab_id,
986                        buffer_usages_to_str(buffer_usages)
987                    )),
988                    size: len as u64,
989                    usage: buffer_usages | BufferUsages::COPY_DST,
990                    mapped_at_creation: true,
991                });
992                {
993                    let mut slice = buffer.slice(..).get_mapped_range_mut();
994
995                    fill_data(slice.slice(..len));
996                }
997                buffer.unmap();
998                large_object_slab.buffer = Some(buffer);
999            }
1000        }
1001    }
1002}
1003
1004/// The results of [`GeneralSlab::grow_if_necessary`].
1005enum SlabGrowthResult {
1006    /// The data already fits in the slab; the slab doesn't need to grow.
1007    NoGrowthNeeded,
1008    /// The slab needed to grow.
1009    ///
1010    /// The [`SlabToReallocate`] contains the old capacity of the slab.
1011    NeededGrowth(SlabToReallocate),
1012    /// The slab wanted to grow but couldn't because it hit its maximum size.
1013    CantGrow,
1014}
1015
1016impl<I> GeneralSlab<I>
1017where
1018    I: SlabItem,
1019{
1020    /// Creates a new growable slab big enough to hold a single element of
1021    /// `data_slot_count` size with the given `layout`.
1022    fn new(
1023        new_slab_id: SlabId<I>,
1024        maybe_slab_item_allocation: &mut Option<SlabItemAllocation<I>>,
1025        settings: &SlabAllocatorSettings,
1026        layout: I::Layout,
1027        data_slot_count: u32,
1028        padding: u32,
1029    ) -> GeneralSlab<I> {
1030        let initial_slab_slot_capacity = (settings.min_slab_size.div_ceil(layout.slot_size())
1031            as u32)
1032            .max(offset_allocator::ext::min_allocator_size(data_slot_count));
1033        let max_slab_slot_capacity = (settings.max_slab_size.div_ceil(layout.slot_size()) as u32)
1034            .max(offset_allocator::ext::min_allocator_size(data_slot_count));
1035
1036        let mut new_slab = GeneralSlab {
1037            allocator: Allocator::new(max_slab_slot_capacity),
1038            buffer: None,
1039            resident_allocations: HashMap::default(),
1040            pending_allocations: HashMap::default(),
1041            element_layout: layout,
1042            current_slot_capacity: initial_slab_slot_capacity,
1043        };
1044
1045        // This should never fail.
1046        if let Some(allocation) = new_slab.allocator.allocate(data_slot_count) {
1047            *maybe_slab_item_allocation = Some(SlabItemAllocation {
1048                slab_id: new_slab_id,
1049                slab_allocation: SlabAllocation {
1050                    slot_count: data_slot_count,
1051                    allocation,
1052                    padding,
1053                },
1054            });
1055        }
1056
1057        new_slab
1058    }
1059
1060    /// Checks to see if the size of this slab is at least `new_size_in_slots`
1061    /// and grows the slab if it isn't.
1062    ///
1063    /// The returned [`SlabGrowthResult`] describes whether the slab needed to
1064    /// grow and whether, if so, it was successful in doing so.
1065    fn grow_if_necessary(
1066        &mut self,
1067        new_size_in_slots: u32,
1068        settings: &SlabAllocatorSettings,
1069    ) -> SlabGrowthResult {
1070        // Is the slab big enough already?
1071        let initial_slot_capacity = self.current_slot_capacity;
1072        if self.current_slot_capacity >= new_size_in_slots {
1073            return SlabGrowthResult::NoGrowthNeeded;
1074        }
1075
1076        // Try to grow in increments of `SlabAllocatorSettings::growth_factor`
1077        // until we're big enough.
1078        while self.current_slot_capacity < new_size_in_slots {
1079            let new_slab_slot_capacity =
1080                ((self.current_slot_capacity as f64 * settings.growth_factor).ceil() as u32)
1081                    .min((settings.max_slab_size / self.element_layout.slot_size()) as u32);
1082            if new_slab_slot_capacity == self.current_slot_capacity {
1083                // The slab is full.
1084                return SlabGrowthResult::CantGrow;
1085            }
1086
1087            self.current_slot_capacity = new_slab_slot_capacity;
1088        }
1089
1090        // Tell our caller what we did.
1091        SlabGrowthResult::NeededGrowth(SlabToReallocate {
1092            old_slot_capacity: initial_slot_capacity,
1093        })
1094    }
1095
1096    /// Returns true if this slab is empty.
1097    fn is_empty(&self) -> bool {
1098        self.resident_allocations.is_empty() && self.pending_allocations.is_empty()
1099    }
1100}
1101
1102/// Returns a string describing the given buffer usages.
1103fn buffer_usages_to_str(buffer_usages: BufferUsages) -> &'static str {
1104    if buffer_usages.contains(BufferUsages::VERTEX) {
1105        "vertex "
1106    } else if buffer_usages.contains(BufferUsages::INDEX) {
1107        "index "
1108    } else if buffer_usages.contains(BufferUsages::STORAGE) {
1109        "storage "
1110    } else {
1111        ""
1112    }
1113}