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}