Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/mesh/allocator.rs
9331 views
1
//! Manages mesh vertex and index buffers.
2
3
use alloc::vec::Vec;
4
use bevy_mesh::Indices;
5
use core::{
6
fmt::{self, Display, Formatter},
7
ops::Range,
8
};
9
use nonmax::NonMaxU32;
10
11
use bevy_app::{App, Plugin};
12
use bevy_asset::AssetId;
13
use bevy_derive::{Deref, DerefMut};
14
use bevy_ecs::{
15
resource::Resource,
16
schedule::IntoScheduleConfigs as _,
17
system::{Res, ResMut},
18
world::{FromWorld, World},
19
};
20
use bevy_log::error;
21
use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
22
use bevy_utils::default;
23
use offset_allocator::{Allocation, Allocator};
24
use wgpu::{
25
BufferDescriptor, BufferSize, BufferUsages, CommandEncoderDescriptor, DownlevelFlags,
26
COPY_BUFFER_ALIGNMENT,
27
};
28
29
use crate::{
30
mesh::{Mesh, MeshVertexBufferLayouts, RenderMesh},
31
render_asset::{prepare_assets, ExtractedAssets},
32
render_resource::Buffer,
33
renderer::{RenderAdapter, RenderDevice, RenderQueue},
34
Render, RenderApp, RenderSystems,
35
};
36
37
/// A plugin that manages GPU memory for mesh data.
38
pub struct MeshAllocatorPlugin;
39
40
/// Manages the assignment of mesh data to GPU buffers.
41
///
42
/// The Bevy renderer tries to pack vertex and index data for multiple meshes
43
/// together so that multiple meshes can be drawn back-to-back without any
44
/// rebinding. This resource manages these buffers.
45
///
46
/// Within each slab, or hardware buffer, the underlying allocation algorithm is
47
/// [`offset_allocator`], a Rust port of Sebastian Aaltonen's hard-real-time C++
48
/// `OffsetAllocator`. Slabs start small and then grow as their contents fill
49
/// up, up to a maximum size limit. To reduce fragmentation, vertex and index
50
/// buffers that are too large bypass this system and receive their own buffers.
51
///
52
/// The [`MeshAllocatorSettings`] allows you to tune the behavior of the
53
/// allocator for better performance with your application. Most applications
54
/// won't need to change the settings from their default values.
55
#[derive(Resource)]
56
pub struct MeshAllocator {
57
/// Holds all buffers and allocators.
58
slabs: HashMap<SlabId, Slab>,
59
60
/// Maps a layout to the slabs that hold elements of that layout.
61
///
62
/// This is used when allocating, so that we can find the appropriate slab
63
/// to place an object in.
64
slab_layouts: HashMap<ElementLayout, Vec<SlabId>>,
65
66
/// Maps mesh asset IDs to the ID of the slabs that hold their vertex data.
67
mesh_id_to_vertex_slab: HashMap<AssetId<Mesh>, SlabId>,
68
69
/// Maps mesh asset IDs to the ID of the slabs that hold their index data.
70
mesh_id_to_index_slab: HashMap<AssetId<Mesh>, SlabId>,
71
72
/// The next slab ID to assign.
73
next_slab_id: SlabId,
74
75
/// Whether we can pack multiple vertex arrays into a single slab on this
76
/// platform.
77
///
78
/// This corresponds to [`DownlevelFlags::BASE_VERTEX`], which is unset on
79
/// WebGL 2. On this platform, we must give each vertex array its own
80
/// buffer, because we can't adjust the first vertex when we perform a draw.
81
general_vertex_slabs_supported: bool,
82
83
/// Additional buffer usages to add to any vertex or index buffers created.
84
pub extra_buffer_usages: BufferUsages,
85
}
86
87
/// Tunable parameters that customize the behavior of the allocator.
88
///
89
/// Generally, these parameters adjust the tradeoff between memory fragmentation
90
/// and performance. You can adjust them as desired for your application. Most
91
/// applications can stick with the default values.
92
#[derive(Resource)]
93
pub struct MeshAllocatorSettings {
94
/// The minimum size of a slab (hardware buffer), in bytes.
95
///
96
/// The default value is 1 MiB.
97
pub min_slab_size: u64,
98
99
/// The maximum size of a slab (hardware buffer), in bytes.
100
///
101
/// When a slab reaches this limit, a new slab is created.
102
///
103
/// The default value is 512 MiB.
104
pub max_slab_size: u64,
105
106
/// The maximum size of vertex or index data that can be placed in a general
107
/// slab, in bytes.
108
///
109
/// If a mesh has vertex or index data that exceeds this size limit, that
110
/// data is placed in its own slab. This reduces fragmentation, but incurs
111
/// more CPU-side binding overhead when drawing the mesh.
112
///
113
/// The default value is 256 MiB.
114
pub large_threshold: u64,
115
116
/// The factor by which we scale a slab when growing it.
117
///
118
/// This value must be greater than 1. Higher values result in more
119
/// fragmentation but fewer expensive copy operations when growing the
120
/// buffer.
121
///
122
/// The default value is 1.5.
123
pub growth_factor: f64,
124
}
125
126
impl Default for MeshAllocatorSettings {
127
fn default() -> Self {
128
Self {
129
// 1 MiB
130
min_slab_size: 1024 * 1024,
131
// 512 MiB
132
max_slab_size: 1024 * 1024 * 512,
133
// 256 MiB
134
large_threshold: 1024 * 1024 * 256,
135
// 1.5× growth
136
growth_factor: 1.5,
137
}
138
}
139
}
140
141
/// The hardware buffer that mesh data lives in, as well as the range within
142
/// that buffer.
143
pub struct MeshBufferSlice<'a> {
144
/// The buffer that the mesh data resides in.
145
pub buffer: &'a Buffer,
146
147
/// The range of elements within this buffer that the mesh data resides in,
148
/// measured in elements.
149
///
150
/// This is not a byte range; it's an element range. For vertex data, this
151
/// is measured in increments of a single vertex. (Thus, if a vertex is 32
152
/// bytes long, then this range is in units of 32 bytes each.) For index
153
/// data, this is measured in increments of a single index value (2 or 4
154
/// bytes). Draw commands generally take their ranges in elements, not
155
/// bytes, so this is the most convenient unit in this case.
156
pub range: Range<u32>,
157
}
158
159
/// The index of a single slab.
160
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
161
#[repr(transparent)]
162
pub struct SlabId(pub NonMaxU32);
163
164
/// Data for a single slab.
165
#[expect(
166
clippy::large_enum_variant,
167
reason = "See https://github.com/bevyengine/bevy/issues/19220"
168
)]
169
enum Slab {
170
/// A slab that can contain multiple objects.
171
General(GeneralSlab),
172
/// A slab that contains a single object.
173
LargeObject(LargeObjectSlab),
174
}
175
176
impl Slab {
177
pub fn buffer_size(&self) -> u64 {
178
match self {
179
Self::General(gs) => gs.buffer.as_ref().map(|buffer| buffer.size()).unwrap_or(0),
180
Self::LargeObject(lo) => lo.buffer.as_ref().map(|buffer| buffer.size()).unwrap_or(0),
181
}
182
}
183
}
184
185
/// A resizable slab that can contain multiple objects.
186
///
187
/// This is the normal type of slab used for objects that are below the
188
/// [`MeshAllocatorSettings::large_threshold`]. Slabs are divided into *slots*,
189
/// which are described in detail in the [`ElementLayout`] documentation.
190
struct GeneralSlab {
191
/// The [`Allocator`] that manages the objects in this slab.
192
allocator: Allocator,
193
194
/// The GPU buffer that backs this slab.
195
///
196
/// This may be `None` if the buffer hasn't been created yet. We delay
197
/// creation of buffers until allocating all the meshes for a single frame,
198
/// so that we don't needlessly create and resize buffers when many meshes
199
/// load all at once.
200
buffer: Option<Buffer>,
201
202
/// Allocations that are on the GPU.
203
///
204
/// The range is in slots.
205
resident_allocations: HashMap<AssetId<Mesh>, SlabAllocation>,
206
207
/// Allocations that are waiting to be uploaded to the GPU.
208
///
209
/// The range is in slots.
210
pending_allocations: HashMap<AssetId<Mesh>, SlabAllocation>,
211
212
/// The layout of a single element (vertex or index).
213
element_layout: ElementLayout,
214
215
/// The size of this slab in slots.
216
current_slot_capacity: u32,
217
}
218
219
/// A slab that contains a single object.
220
///
221
/// Typically, this is for objects that exceed the
222
/// [`MeshAllocatorSettings::large_threshold`]. This is also for objects that
223
/// would ordinarily receive their own slab but can't because of platform
224
/// limitations, most notably vertex arrays on WebGL 2.
225
struct LargeObjectSlab {
226
/// The GPU buffer that backs this slab.
227
///
228
/// This may be `None` if the buffer hasn't been created yet.
229
buffer: Option<Buffer>,
230
231
/// The layout of a single element (vertex or index).
232
element_layout: ElementLayout,
233
}
234
235
/// The type of element that a slab can store.
236
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
237
enum ElementClass {
238
/// Data for a vertex.
239
Vertex,
240
/// A vertex index.
241
Index,
242
}
243
244
/// The results of [`GeneralSlab::grow_if_necessary`].
245
enum SlabGrowthResult {
246
/// The mesh data already fits in the slab; the slab doesn't need to grow.
247
NoGrowthNeeded,
248
/// The slab needed to grow.
249
///
250
/// The [`SlabToReallocate`] contains the old capacity of the slab.
251
NeededGrowth(SlabToReallocate),
252
/// The slab wanted to grow but couldn't because it hit its maximum size.
253
CantGrow,
254
}
255
256
/// Information about the size of individual elements (vertices or indices)
257
/// within a slab.
258
///
259
/// Slab objects are allocated in units of *slots*. Usually, each element takes
260
/// up one slot, and so elements and slots are equivalent. Occasionally,
261
/// however, a slot may consist of 2 or even 4 elements. This occurs when the
262
/// size of an element isn't divisible by [`COPY_BUFFER_ALIGNMENT`]. When we
263
/// resize buffers, we perform GPU-to-GPU copies to shuffle the existing
264
/// elements into their new positions, and such copies must be on
265
/// [`COPY_BUFFER_ALIGNMENT`] boundaries. Slots solve this problem by
266
/// guaranteeing that the size of an allocation quantum is divisible by both the
267
/// size of an element and [`COPY_BUFFER_ALIGNMENT`], so we can relocate it
268
/// freely.
269
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
270
struct ElementLayout {
271
/// Either a vertex or an index.
272
class: ElementClass,
273
274
/// The size in bytes of a single element (vertex or index).
275
size: u64,
276
277
/// The number of elements that make up a single slot.
278
///
279
/// Usually, this is 1, but it can be different if [`ElementLayout::size`]
280
/// isn't divisible by 4. See the comment in [`ElementLayout`] for more
281
/// details.
282
elements_per_slot: u32,
283
}
284
285
/// The location of an allocation and the slab it's contained in.
286
struct MeshAllocation {
287
/// The ID of the slab.
288
slab_id: SlabId,
289
/// Holds the actual allocation.
290
slab_allocation: SlabAllocation,
291
}
292
293
/// An allocation within a slab.
294
#[derive(Clone)]
295
struct SlabAllocation {
296
/// The actual [`Allocator`] handle, needed to free the allocation.
297
allocation: Allocation,
298
/// The number of slots that this allocation takes up.
299
slot_count: u32,
300
}
301
302
/// Holds information about all slabs scheduled to be allocated or reallocated.
303
#[derive(Default, Deref, DerefMut)]
304
struct SlabsToReallocate(HashMap<SlabId, SlabToReallocate>);
305
306
/// Holds information about a slab that's scheduled to be allocated or
307
/// reallocated.
308
#[derive(Default)]
309
struct SlabToReallocate {
310
/// The capacity of the slab before we decided to grow it.
311
old_slot_capacity: u32,
312
}
313
314
impl Display for SlabId {
315
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
316
self.0.fmt(f)
317
}
318
}
319
320
impl Plugin for MeshAllocatorPlugin {
321
fn build(&self, app: &mut App) {
322
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
323
return;
324
};
325
326
render_app
327
.init_resource::<MeshAllocatorSettings>()
328
.add_systems(
329
Render,
330
allocate_and_free_meshes
331
.in_set(RenderSystems::PrepareAssets)
332
.before(prepare_assets::<RenderMesh>),
333
);
334
}
335
336
fn finish(&self, app: &mut App) {
337
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
338
return;
339
};
340
341
// The `RenderAdapter` isn't available until now, so we can't do this in
342
// [`Plugin::build`].
343
render_app.init_resource::<MeshAllocator>();
344
}
345
}
346
347
impl FromWorld for MeshAllocator {
348
fn from_world(world: &mut World) -> Self {
349
// Note whether we're on WebGL 2. In this case, we must give every
350
// vertex array its own slab.
351
let render_adapter = world.resource::<RenderAdapter>();
352
let general_vertex_slabs_supported = render_adapter
353
.get_downlevel_capabilities()
354
.flags
355
.contains(DownlevelFlags::BASE_VERTEX);
356
357
Self {
358
slabs: HashMap::default(),
359
slab_layouts: HashMap::default(),
360
mesh_id_to_vertex_slab: HashMap::default(),
361
mesh_id_to_index_slab: HashMap::default(),
362
next_slab_id: default(),
363
general_vertex_slabs_supported,
364
extra_buffer_usages: BufferUsages::empty(),
365
}
366
}
367
}
368
369
/// A system that processes newly-extracted or newly-removed meshes and writes
370
/// their data into buffers or frees their data as appropriate.
371
pub fn allocate_and_free_meshes(
372
mut mesh_allocator: ResMut<MeshAllocator>,
373
mesh_allocator_settings: Res<MeshAllocatorSettings>,
374
extracted_meshes: Res<ExtractedAssets<RenderMesh>>,
375
mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,
376
render_device: Res<RenderDevice>,
377
render_queue: Res<RenderQueue>,
378
) {
379
// Process removed or modified meshes.
380
mesh_allocator.free_meshes(&extracted_meshes);
381
382
// Process newly-added or modified meshes.
383
mesh_allocator.allocate_meshes(
384
&mesh_allocator_settings,
385
&extracted_meshes,
386
&mut mesh_vertex_buffer_layouts,
387
&render_device,
388
&render_queue,
389
);
390
}
391
392
impl MeshAllocator {
393
/// Returns the buffer and range within that buffer of the vertex data for
394
/// the mesh with the given ID.
395
///
396
/// If the mesh wasn't allocated, returns None.
397
pub fn mesh_vertex_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {
398
self.mesh_slice_in_slab(mesh_id, *self.mesh_id_to_vertex_slab.get(mesh_id)?)
399
}
400
401
/// Returns the buffer and range within that buffer of the index data for
402
/// the mesh with the given ID.
403
///
404
/// If the mesh has no index data or wasn't allocated, returns None.
405
pub fn mesh_index_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {
406
self.mesh_slice_in_slab(mesh_id, *self.mesh_id_to_index_slab.get(mesh_id)?)
407
}
408
409
/// Returns the IDs of the vertex buffer and index buffer respectively for
410
/// the mesh with the given ID.
411
///
412
/// If the mesh wasn't allocated, or has no index data in the case of the
413
/// index buffer, the corresponding element in the returned tuple will be
414
/// None.
415
pub fn mesh_slabs(&self, mesh_id: &AssetId<Mesh>) -> (Option<SlabId>, Option<SlabId>) {
416
(
417
self.mesh_id_to_vertex_slab.get(mesh_id).cloned(),
418
self.mesh_id_to_index_slab.get(mesh_id).cloned(),
419
)
420
}
421
422
/// Get the number of allocated slabs
423
pub fn slab_count(&self) -> usize {
424
self.slabs.len()
425
}
426
427
/// Get the total size of all allocated slabs
428
pub fn slabs_size(&self) -> u64 {
429
self.slabs.iter().map(|slab| slab.1.buffer_size()).sum()
430
}
431
432
pub fn allocations(&self) -> usize {
433
self.mesh_id_to_index_slab.len()
434
}
435
436
/// Given a slab and a mesh with data located with it, returns the buffer
437
/// and range of that mesh data within the slab.
438
fn mesh_slice_in_slab(
439
&self,
440
mesh_id: &AssetId<Mesh>,
441
slab_id: SlabId,
442
) -> Option<MeshBufferSlice<'_>> {
443
match self.slabs.get(&slab_id)? {
444
Slab::General(general_slab) => {
445
let slab_allocation = general_slab.resident_allocations.get(mesh_id)?;
446
Some(MeshBufferSlice {
447
buffer: general_slab.buffer.as_ref()?,
448
range: (slab_allocation.allocation.offset
449
* general_slab.element_layout.elements_per_slot)
450
..((slab_allocation.allocation.offset + slab_allocation.slot_count)
451
* general_slab.element_layout.elements_per_slot),
452
})
453
}
454
455
Slab::LargeObject(large_object_slab) => {
456
let buffer = large_object_slab.buffer.as_ref()?;
457
Some(MeshBufferSlice {
458
buffer,
459
range: 0..((buffer.size() / large_object_slab.element_layout.size) as u32),
460
})
461
}
462
}
463
}
464
465
/// Processes newly-loaded meshes, allocating room in the slabs for their
466
/// mesh data and performing upload operations as appropriate.
467
fn allocate_meshes(
468
&mut self,
469
mesh_allocator_settings: &MeshAllocatorSettings,
470
extracted_meshes: &ExtractedAssets<RenderMesh>,
471
mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
472
render_device: &RenderDevice,
473
render_queue: &RenderQueue,
474
) {
475
let mut slabs_to_grow = SlabsToReallocate::default();
476
477
// Allocate.
478
for (mesh_id, mesh) in &extracted_meshes.extracted {
479
let vertex_buffer_size = mesh.get_vertex_buffer_size() as u64;
480
if vertex_buffer_size == 0 {
481
continue;
482
}
483
// Allocate vertex data. Note that we can only pack mesh vertex data
484
// together if the platform supports it.
485
let vertex_element_layout = ElementLayout::vertex(mesh_vertex_buffer_layouts, mesh);
486
if self.general_vertex_slabs_supported {
487
self.allocate(
488
mesh_id,
489
vertex_buffer_size,
490
vertex_element_layout,
491
&mut slabs_to_grow,
492
mesh_allocator_settings,
493
);
494
} else {
495
self.allocate_large(mesh_id, vertex_element_layout);
496
}
497
498
// Allocate index data.
499
if let (Some(index_buffer_data), Some(index_element_layout)) =
500
(mesh.get_index_buffer_bytes(), ElementLayout::index(mesh))
501
{
502
self.allocate(
503
mesh_id,
504
index_buffer_data.len() as u64,
505
index_element_layout,
506
&mut slabs_to_grow,
507
mesh_allocator_settings,
508
);
509
}
510
}
511
512
// Perform growth.
513
for (slab_id, slab_to_grow) in slabs_to_grow.0 {
514
self.reallocate_slab(render_device, render_queue, slab_id, slab_to_grow);
515
}
516
517
// Copy new mesh data in.
518
for (mesh_id, mesh) in &extracted_meshes.extracted {
519
self.copy_mesh_vertex_data(mesh_id, mesh, render_device, render_queue);
520
self.copy_mesh_index_data(mesh_id, mesh, render_device, render_queue);
521
}
522
}
523
524
/// Copies vertex array data from a mesh into the appropriate spot in the
525
/// slab.
526
fn copy_mesh_vertex_data(
527
&mut self,
528
mesh_id: &AssetId<Mesh>,
529
mesh: &Mesh,
530
render_device: &RenderDevice,
531
render_queue: &RenderQueue,
532
) {
533
let Some(&slab_id) = self.mesh_id_to_vertex_slab.get(mesh_id) else {
534
return;
535
};
536
537
// Call the generic function.
538
self.copy_element_data(
539
mesh_id,
540
mesh.get_vertex_buffer_size(),
541
|slice| mesh.write_packed_vertex_buffer_data(slice),
542
BufferUsages::VERTEX,
543
slab_id,
544
render_device,
545
render_queue,
546
);
547
}
548
549
/// Copies index array data from a mesh into the appropriate spot in the
550
/// slab.
551
fn copy_mesh_index_data(
552
&mut self,
553
mesh_id: &AssetId<Mesh>,
554
mesh: &Mesh,
555
render_device: &RenderDevice,
556
render_queue: &RenderQueue,
557
) {
558
let Some(&slab_id) = self.mesh_id_to_index_slab.get(mesh_id) else {
559
return;
560
};
561
let Some(index_data) = mesh.get_index_buffer_bytes() else {
562
return;
563
};
564
565
// Call the generic function.
566
self.copy_element_data(
567
mesh_id,
568
index_data.len(),
569
|slice| slice.copy_from_slice(index_data),
570
BufferUsages::INDEX,
571
slab_id,
572
render_device,
573
render_queue,
574
);
575
}
576
577
/// A generic function that copies either vertex or index data into a slab.
578
fn copy_element_data(
579
&mut self,
580
mesh_id: &AssetId<Mesh>,
581
len: usize,
582
fill_data: impl Fn(&mut [u8]),
583
buffer_usages: BufferUsages,
584
slab_id: SlabId,
585
render_device: &RenderDevice,
586
render_queue: &RenderQueue,
587
) {
588
let Some(slab) = self.slabs.get_mut(&slab_id) else {
589
return;
590
};
591
592
match *slab {
593
Slab::General(ref mut general_slab) => {
594
let (Some(buffer), Some(allocated_range)) = (
595
&general_slab.buffer,
596
general_slab.pending_allocations.remove(mesh_id),
597
) else {
598
return;
599
};
600
601
let slot_size = general_slab.element_layout.slot_size();
602
603
// round up size to a multiple of the slot size to satisfy wgpu alignment requirements
604
if let Some(size) = BufferSize::new((len as u64).next_multiple_of(slot_size)) {
605
// Write the data in.
606
if let Some(mut buffer) = render_queue.write_buffer_with(
607
buffer,
608
allocated_range.allocation.offset as u64 * slot_size,
609
size,
610
) {
611
let slice = &mut buffer.as_mut()[..len];
612
fill_data(slice);
613
}
614
}
615
616
// Mark the allocation as resident.
617
general_slab
618
.resident_allocations
619
.insert(*mesh_id, allocated_range);
620
}
621
622
Slab::LargeObject(ref mut large_object_slab) => {
623
debug_assert!(large_object_slab.buffer.is_none());
624
625
// Create the buffer and its data in one go.
626
let buffer = render_device.create_buffer(&BufferDescriptor {
627
label: Some(&format!(
628
"large mesh slab {} ({}buffer)",
629
slab_id,
630
buffer_usages_to_str(buffer_usages)
631
)),
632
size: len as u64,
633
usage: buffer_usages | BufferUsages::COPY_DST | self.extra_buffer_usages,
634
mapped_at_creation: true,
635
});
636
{
637
let slice = &mut buffer.slice(..).get_mapped_range_mut()[..len];
638
fill_data(slice);
639
}
640
buffer.unmap();
641
large_object_slab.buffer = Some(buffer);
642
}
643
}
644
}
645
646
/// Frees allocations for meshes that were removed or modified this frame.
647
fn free_meshes(&mut self, extracted_meshes: &ExtractedAssets<RenderMesh>) {
648
let mut empty_slabs = <HashSet<_>>::default();
649
650
// TODO: Consider explicitly reusing allocations for changed meshes of the same size
651
let meshes_to_free = extracted_meshes
652
.removed
653
.iter()
654
.chain(extracted_meshes.modified.iter());
655
656
for mesh_id in meshes_to_free {
657
if let Some(slab_id) = self.mesh_id_to_vertex_slab.remove(mesh_id) {
658
self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs);
659
}
660
if let Some(slab_id) = self.mesh_id_to_index_slab.remove(mesh_id) {
661
self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs);
662
}
663
}
664
665
for empty_slab in empty_slabs {
666
self.slab_layouts.values_mut().for_each(|slab_ids| {
667
let idx = slab_ids.iter().position(|&slab_id| slab_id == empty_slab);
668
if let Some(idx) = idx {
669
slab_ids.remove(idx);
670
}
671
});
672
self.slabs.remove(&empty_slab);
673
}
674
}
675
676
/// Given a slab and the ID of a mesh containing data in it, marks the
677
/// allocation as free.
678
///
679
/// If this results in the slab becoming empty, this function adds the slab
680
/// to the `empty_slabs` set.
681
fn free_allocation_in_slab(
682
&mut self,
683
mesh_id: &AssetId<Mesh>,
684
slab_id: SlabId,
685
empty_slabs: &mut HashSet<SlabId>,
686
) {
687
let Some(slab) = self.slabs.get_mut(&slab_id) else {
688
return;
689
};
690
691
match *slab {
692
Slab::General(ref mut general_slab) => {
693
let Some(slab_allocation) = general_slab
694
.resident_allocations
695
.remove(mesh_id)
696
.or_else(|| general_slab.pending_allocations.remove(mesh_id))
697
else {
698
return;
699
};
700
701
general_slab.allocator.free(slab_allocation.allocation);
702
703
if general_slab.is_empty() {
704
empty_slabs.insert(slab_id);
705
}
706
}
707
Slab::LargeObject(_) => {
708
empty_slabs.insert(slab_id);
709
}
710
}
711
}
712
713
/// Allocates space for mesh data with the given byte size and layout in the
714
/// appropriate slab, creating that slab if necessary.
715
fn allocate(
716
&mut self,
717
mesh_id: &AssetId<Mesh>,
718
data_byte_len: u64,
719
layout: ElementLayout,
720
slabs_to_grow: &mut SlabsToReallocate,
721
settings: &MeshAllocatorSettings,
722
) {
723
let data_element_count = data_byte_len.div_ceil(layout.size) as u32;
724
let data_slot_count = data_element_count.div_ceil(layout.elements_per_slot);
725
726
// If the mesh data is too large for a slab, give it a slab of its own.
727
if data_slot_count as u64 * layout.slot_size()
728
>= settings.large_threshold.min(settings.max_slab_size)
729
{
730
self.allocate_large(mesh_id, layout);
731
} else {
732
self.allocate_general(mesh_id, data_slot_count, layout, slabs_to_grow, settings);
733
}
734
}
735
736
/// Allocates space for mesh data with the given slot size and layout in the
737
/// appropriate general slab.
738
fn allocate_general(
739
&mut self,
740
mesh_id: &AssetId<Mesh>,
741
data_slot_count: u32,
742
layout: ElementLayout,
743
slabs_to_grow: &mut SlabsToReallocate,
744
settings: &MeshAllocatorSettings,
745
) {
746
let candidate_slabs = self.slab_layouts.entry(layout).or_default();
747
748
// Loop through the slabs that accept elements of the appropriate type
749
// and try to allocate the mesh inside them. We go with the first one
750
// that succeeds.
751
let mut mesh_allocation = None;
752
for &slab_id in &*candidate_slabs {
753
let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {
754
unreachable!("Slab not found")
755
};
756
757
let Some(allocation) = slab.allocator.allocate(data_slot_count) else {
758
continue;
759
};
760
761
// Try to fit the object in the slab, growing if necessary.
762
match slab.grow_if_necessary(allocation.offset + data_slot_count, settings) {
763
SlabGrowthResult::NoGrowthNeeded => {}
764
SlabGrowthResult::NeededGrowth(slab_to_reallocate) => {
765
// If we already grew the slab this frame, don't replace the
766
// `SlabToReallocate` entry. We want to keep the entry
767
// corresponding to the size that the slab had at the start
768
// of the frame, so that we can copy only the used portion
769
// of the initial buffer to the new one.
770
if let Entry::Vacant(vacant_entry) = slabs_to_grow.entry(slab_id) {
771
vacant_entry.insert(slab_to_reallocate);
772
}
773
}
774
SlabGrowthResult::CantGrow => continue,
775
}
776
777
mesh_allocation = Some(MeshAllocation {
778
slab_id,
779
slab_allocation: SlabAllocation {
780
allocation,
781
slot_count: data_slot_count,
782
},
783
});
784
break;
785
}
786
787
// If we still have no allocation, make a new slab.
788
if mesh_allocation.is_none() {
789
let new_slab_id = self.next_slab_id;
790
self.next_slab_id.0 = NonMaxU32::new(self.next_slab_id.0.get() + 1).unwrap_or_default();
791
792
let new_slab = GeneralSlab::new(
793
new_slab_id,
794
&mut mesh_allocation,
795
settings,
796
layout,
797
data_slot_count,
798
);
799
800
self.slabs.insert(new_slab_id, Slab::General(new_slab));
801
candidate_slabs.push(new_slab_id);
802
slabs_to_grow.insert(new_slab_id, SlabToReallocate::default());
803
}
804
805
let mesh_allocation = mesh_allocation.expect("Should have been able to allocate");
806
807
// Mark the allocation as pending. Don't copy it in just yet; further
808
// meshes loaded this frame may result in its final allocation location
809
// changing.
810
if let Some(Slab::General(general_slab)) = self.slabs.get_mut(&mesh_allocation.slab_id) {
811
general_slab
812
.pending_allocations
813
.insert(*mesh_id, mesh_allocation.slab_allocation);
814
};
815
816
self.record_allocation(mesh_id, mesh_allocation.slab_id, layout.class);
817
}
818
819
/// Allocates an object into its own dedicated slab.
820
fn allocate_large(&mut self, mesh_id: &AssetId<Mesh>, layout: ElementLayout) {
821
let new_slab_id = self.next_slab_id;
822
self.next_slab_id.0 = NonMaxU32::new(self.next_slab_id.0.get() + 1).unwrap_or_default();
823
824
self.record_allocation(mesh_id, new_slab_id, layout.class);
825
826
self.slabs.insert(
827
new_slab_id,
828
Slab::LargeObject(LargeObjectSlab {
829
buffer: None,
830
element_layout: layout,
831
}),
832
);
833
}
834
835
/// Reallocates a slab that needs to be resized, or allocates a new slab.
836
///
837
/// This performs the actual growth operation that
838
/// [`GeneralSlab::grow_if_necessary`] scheduled. We do the growth in two
839
/// phases so that, if a slab grows multiple times in the same frame, only
840
/// one new buffer is reallocated, rather than reallocating the buffer
841
/// multiple times.
842
fn reallocate_slab(
843
&mut self,
844
render_device: &RenderDevice,
845
render_queue: &RenderQueue,
846
slab_id: SlabId,
847
slab_to_grow: SlabToReallocate,
848
) {
849
let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {
850
error!("Couldn't find slab {} to grow", slab_id);
851
return;
852
};
853
854
let old_buffer = slab.buffer.take();
855
856
let mut buffer_usages = BufferUsages::COPY_SRC | BufferUsages::COPY_DST;
857
match slab.element_layout.class {
858
ElementClass::Vertex => buffer_usages |= BufferUsages::VERTEX,
859
ElementClass::Index => buffer_usages |= BufferUsages::INDEX,
860
};
861
862
// Create the buffer.
863
let new_buffer = render_device.create_buffer(&BufferDescriptor {
864
label: Some(&format!(
865
"general mesh slab {} ({}buffer)",
866
slab_id,
867
buffer_usages_to_str(buffer_usages)
868
)),
869
size: slab.current_slot_capacity as u64 * slab.element_layout.slot_size(),
870
usage: buffer_usages | self.extra_buffer_usages,
871
mapped_at_creation: false,
872
});
873
874
slab.buffer = Some(new_buffer.clone());
875
876
let Some(old_buffer) = old_buffer else { return };
877
878
// In order to do buffer copies, we need a command encoder.
879
let mut encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
880
label: Some("slab resize encoder"),
881
});
882
883
// Copy the data from the old buffer into the new one.
884
encoder.copy_buffer_to_buffer(
885
&old_buffer,
886
0,
887
&new_buffer,
888
0,
889
slab_to_grow.old_slot_capacity as u64 * slab.element_layout.slot_size(),
890
);
891
892
let command_buffer = encoder.finish();
893
render_queue.submit([command_buffer]);
894
}
895
896
/// Records the location of the given newly-allocated mesh data in the
897
/// [`Self::mesh_id_to_vertex_slab`] or [`Self::mesh_id_to_index_slab`]
898
/// tables as appropriate.
899
fn record_allocation(
900
&mut self,
901
mesh_id: &AssetId<Mesh>,
902
slab_id: SlabId,
903
element_class: ElementClass,
904
) {
905
match element_class {
906
ElementClass::Vertex => {
907
self.mesh_id_to_vertex_slab.insert(*mesh_id, slab_id);
908
}
909
ElementClass::Index => {
910
self.mesh_id_to_index_slab.insert(*mesh_id, slab_id);
911
}
912
}
913
}
914
}
915
916
impl GeneralSlab {
917
/// Creates a new growable slab big enough to hold a single element of
918
/// `data_slot_count` size with the given `layout`.
919
fn new(
920
new_slab_id: SlabId,
921
mesh_allocation: &mut Option<MeshAllocation>,
922
settings: &MeshAllocatorSettings,
923
layout: ElementLayout,
924
data_slot_count: u32,
925
) -> GeneralSlab {
926
let initial_slab_slot_capacity = (settings.min_slab_size.div_ceil(layout.slot_size())
927
as u32)
928
.max(offset_allocator::ext::min_allocator_size(data_slot_count));
929
let max_slab_slot_capacity = (settings.max_slab_size.div_ceil(layout.slot_size()) as u32)
930
.max(offset_allocator::ext::min_allocator_size(data_slot_count));
931
932
let mut new_slab = GeneralSlab {
933
allocator: Allocator::new(max_slab_slot_capacity),
934
buffer: None,
935
resident_allocations: HashMap::default(),
936
pending_allocations: HashMap::default(),
937
element_layout: layout,
938
current_slot_capacity: initial_slab_slot_capacity,
939
};
940
941
// This should never fail.
942
if let Some(allocation) = new_slab.allocator.allocate(data_slot_count) {
943
*mesh_allocation = Some(MeshAllocation {
944
slab_id: new_slab_id,
945
slab_allocation: SlabAllocation {
946
slot_count: data_slot_count,
947
allocation,
948
},
949
});
950
}
951
952
new_slab
953
}
954
955
/// Checks to see if the size of this slab is at least `new_size_in_slots`
956
/// and grows the slab if it isn't.
957
///
958
/// The returned [`SlabGrowthResult`] describes whether the slab needed to
959
/// grow and whether, if so, it was successful in doing so.
960
fn grow_if_necessary(
961
&mut self,
962
new_size_in_slots: u32,
963
settings: &MeshAllocatorSettings,
964
) -> SlabGrowthResult {
965
// Is the slab big enough already?
966
let initial_slot_capacity = self.current_slot_capacity;
967
if self.current_slot_capacity >= new_size_in_slots {
968
return SlabGrowthResult::NoGrowthNeeded;
969
}
970
971
// Try to grow in increments of `MeshAllocatorSettings::growth_factor`
972
// until we're big enough.
973
while self.current_slot_capacity < new_size_in_slots {
974
let new_slab_slot_capacity =
975
((self.current_slot_capacity as f64 * settings.growth_factor).ceil() as u32)
976
.min((settings.max_slab_size / self.element_layout.slot_size()) as u32);
977
if new_slab_slot_capacity == self.current_slot_capacity {
978
// The slab is full.
979
return SlabGrowthResult::CantGrow;
980
}
981
982
self.current_slot_capacity = new_slab_slot_capacity;
983
}
984
985
// Tell our caller what we did.
986
SlabGrowthResult::NeededGrowth(SlabToReallocate {
987
old_slot_capacity: initial_slot_capacity,
988
})
989
}
990
}
991
992
impl ElementLayout {
993
/// Creates an [`ElementLayout`] for mesh data of the given class (vertex or
994
/// index) with the given byte size.
995
fn new(class: ElementClass, size: u64) -> ElementLayout {
996
const {
997
assert!(4 == COPY_BUFFER_ALIGNMENT);
998
}
999
// this is equivalent to `4 / gcd(4,size)` but lets us not implement gcd.
1000
// ping @atlv if above assert ever fails (likely never)
1001
let elements_per_slot = [1, 4, 2, 4][size as usize & 3];
1002
ElementLayout {
1003
class,
1004
size,
1005
// Make sure that slot boundaries begin and end on
1006
// `COPY_BUFFER_ALIGNMENT`-byte (4-byte) boundaries.
1007
elements_per_slot,
1008
}
1009
}
1010
1011
fn slot_size(&self) -> u64 {
1012
self.size * self.elements_per_slot as u64
1013
}
1014
1015
/// Creates the appropriate [`ElementLayout`] for the given mesh's vertex
1016
/// data.
1017
fn vertex(
1018
mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
1019
mesh: &Mesh,
1020
) -> ElementLayout {
1021
let mesh_vertex_buffer_layout =
1022
mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
1023
ElementLayout::new(
1024
ElementClass::Vertex,
1025
mesh_vertex_buffer_layout.0.layout().array_stride,
1026
)
1027
}
1028
1029
/// Creates the appropriate [`ElementLayout`] for the given mesh's index
1030
/// data.
1031
fn index(mesh: &Mesh) -> Option<ElementLayout> {
1032
let size = match mesh.indices()? {
1033
Indices::U16(_) => 2,
1034
Indices::U32(_) => 4,
1035
};
1036
Some(ElementLayout::new(ElementClass::Index, size))
1037
}
1038
}
1039
1040
impl GeneralSlab {
1041
/// Returns true if this slab is empty.
1042
fn is_empty(&self) -> bool {
1043
self.resident_allocations.is_empty() && self.pending_allocations.is_empty()
1044
}
1045
}
1046
1047
/// Returns a string describing the given buffer usages.
1048
fn buffer_usages_to_str(buffer_usages: BufferUsages) -> &'static str {
1049
if buffer_usages.contains(BufferUsages::VERTEX) {
1050
"vertex "
1051
} else if buffer_usages.contains(BufferUsages::INDEX) {
1052
"index "
1053
} else {
1054
""
1055
}
1056
}
1057
1058