Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/slab_allocator.rs
30635 views
1
//! A general-purpose allocator that manages a set of GPU buffer slabs.
2
3
use alloc::borrow::Cow;
4
use bevy_derive::{Deref, DerefMut};
5
use bevy_log::error;
6
use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
7
use core::{
8
cmp::Ordering,
9
fmt::{self, Debug, Display, Formatter},
10
hash::{Hash, Hasher},
11
marker::PhantomData,
12
ops::Range,
13
};
14
use nonmax::NonMaxU32;
15
use offset_allocator::{Allocation, Allocator};
16
use wgpu::{BufferDescriptor, BufferSize, BufferUsages, CommandEncoderDescriptor, WriteOnly};
17
18
use 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.
56
pub struct SlabAllocator<I>
57
where
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.
86
pub 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.
108
pub 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.
127
trait SlabItemLayoutExt {
128
/// Returns the size in bytes of a single slot.
129
fn slot_size(&self) -> u64;
130
}
131
132
impl<I> SlabItemLayoutExt for I
133
where
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.
146
pub 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
179
impl 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)]
197
pub struct SlabId<I>
198
where
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
207
impl<I> Clone for SlabId<I>
208
where
209
I: SlabItem,
210
{
211
fn clone(&self) -> Self {
212
*self
213
}
214
}
215
216
impl<I> Copy for SlabId<I> where I: SlabItem {}
217
218
impl<I> Default for SlabId<I>
219
where
220
I: SlabItem,
221
{
222
fn default() -> Self {
223
SlabId {
224
id: NonMaxU32::default(),
225
phantom: PhantomData,
226
}
227
}
228
}
229
230
impl<I> PartialEq for SlabId<I>
231
where
232
I: SlabItem,
233
{
234
fn eq(&self, other: &Self) -> bool {
235
self.id == other.id
236
}
237
}
238
239
impl<I> Eq for SlabId<I> where I: SlabItem {}
240
241
impl<I> PartialOrd for SlabId<I>
242
where
243
I: SlabItem,
244
{
245
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
246
Some(self.cmp(other))
247
}
248
}
249
250
impl<I> Ord for SlabId<I>
251
where
252
I: SlabItem,
253
{
254
fn cmp(&self, other: &Self) -> Ordering {
255
self.id.cmp(other)
256
}
257
}
258
259
impl<I> Hash for SlabId<I>
260
where
261
I: SlabItem,
262
{
263
fn hash<H: Hasher>(&self, state: &mut H) {
264
self.id.hash(state);
265
}
266
}
267
268
impl<I> Debug for SlabId<I>
269
where
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
)]
282
pub enum Slab<I>
283
where
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.
297
pub struct GeneralSlab<I>
298
where
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.
337
pub struct LargeObjectSlab<I>
338
where
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.
351
struct SlabItemAllocation<I>
352
where
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
361
impl<I> Slab<I>
362
where
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.
398
pub struct AllocationStage<'a, I>
399
where
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
408
impl<'a, I> Drop for AllocationStage<'a, I>
409
where
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
422
impl<'a, I> AllocationStage<'a, I>
423
where
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.
468
pub struct DeallocationStage<'a, I>
469
where
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
478
impl<'a, I> Drop for DeallocationStage<'a, I>
479
where
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
492
impl<'a, I> DeallocationStage<'a, I>
493
where
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)]
517
struct 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.
529
pub struct SlabAllocationBufferSlice<'a, I>
530
where
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)]
553
pub struct SlabToReallocate {
554
/// The capacity of the slab before we decided to grow it.
555
old_slot_capacity: u32,
556
}
557
558
impl<I> Display for SlabId<I>
559
where
560
I: SlabItem,
561
{
562
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
563
Debug::fmt(&self.id, f)
564
}
565
}
566
567
impl<I> Default for SlabAllocator<I>
568
where
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
585
impl<I> SlabAllocator<I>
586
where
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`].
1005
enum 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
1016
impl<I> GeneralSlab<I>
1017
where
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.
1103
fn 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
}
1114
1115