Path: blob/main/crates/bevy_render/src/mesh/allocator.rs
6596 views
//! Manages mesh vertex and index buffers.12use alloc::vec::Vec;3use bevy_mesh::Indices;4use core::{5fmt::{self, Display, Formatter},6ops::Range,7};8use nonmax::NonMaxU32;910use bevy_app::{App, Plugin};11use bevy_asset::AssetId;12use bevy_derive::{Deref, DerefMut};13use bevy_ecs::{14resource::Resource,15schedule::IntoScheduleConfigs as _,16system::{Res, ResMut},17world::{FromWorld, World},18};19use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};20use bevy_utils::default;21use offset_allocator::{Allocation, Allocator};22use tracing::error;23use wgpu::{24BufferDescriptor, BufferSize, BufferUsages, CommandEncoderDescriptor, DownlevelFlags,25COPY_BUFFER_ALIGNMENT,26};2728use crate::{29mesh::{Mesh, MeshVertexBufferLayouts, RenderMesh},30render_asset::{prepare_assets, ExtractedAssets},31render_resource::Buffer,32renderer::{RenderAdapter, RenderDevice, RenderQueue},33Render, RenderApp, RenderSystems,34};3536/// A plugin that manages GPU memory for mesh data.37pub struct MeshAllocatorPlugin;3839/// Manages the assignment of mesh data to GPU buffers.40///41/// The Bevy renderer tries to pack vertex and index data for multiple meshes42/// together so that multiple meshes can be drawn back-to-back without any43/// rebinding. This resource manages these buffers.44///45/// Within each slab, or hardware buffer, the underlying allocation algorithm is46/// [`offset_allocator`], a Rust port of Sebastian Aaltonen's hard-real-time C++47/// `OffsetAllocator`. Slabs start small and then grow as their contents fill48/// up, up to a maximum size limit. To reduce fragmentation, vertex and index49/// buffers that are too large bypass this system and receive their own buffers.50///51/// The [`MeshAllocatorSettings`] allows you to tune the behavior of the52/// allocator for better performance with your application. Most applications53/// won't need to change the settings from their default values.54#[derive(Resource)]55pub struct MeshAllocator {56/// Holds all buffers and allocators.57slabs: HashMap<SlabId, Slab>,5859/// Maps a layout to the slabs that hold elements of that layout.60///61/// This is used when allocating, so that we can find the appropriate slab62/// to place an object in.63slab_layouts: HashMap<ElementLayout, Vec<SlabId>>,6465/// Maps mesh asset IDs to the ID of the slabs that hold their vertex data.66mesh_id_to_vertex_slab: HashMap<AssetId<Mesh>, SlabId>,6768/// Maps mesh asset IDs to the ID of the slabs that hold their index data.69mesh_id_to_index_slab: HashMap<AssetId<Mesh>, SlabId>,7071/// The next slab ID to assign.72next_slab_id: SlabId,7374/// Whether we can pack multiple vertex arrays into a single slab on this75/// platform.76///77/// This corresponds to [`DownlevelFlags::BASE_VERTEX`], which is unset on78/// WebGL 2. On this platform, we must give each vertex array its own79/// buffer, because we can't adjust the first vertex when we perform a draw.80general_vertex_slabs_supported: bool,8182/// Additional buffer usages to add to any vertex or index buffers created.83pub extra_buffer_usages: BufferUsages,84}8586/// Tunable parameters that customize the behavior of the allocator.87///88/// Generally, these parameters adjust the tradeoff between memory fragmentation89/// and performance. You can adjust them as desired for your application. Most90/// applications can stick with the default values.91#[derive(Resource)]92pub struct MeshAllocatorSettings {93/// The minimum size of a slab (hardware buffer), in bytes.94///95/// The default value is 1 MiB.96pub min_slab_size: u64,9798/// The maximum size of a slab (hardware buffer), in bytes.99///100/// When a slab reaches this limit, a new slab is created.101///102/// The default value is 512 MiB.103pub max_slab_size: u64,104105/// The maximum size of vertex or index data that can be placed in a general106/// slab, in bytes.107///108/// If a mesh has vertex or index data that exceeds this size limit, that109/// data is placed in its own slab. This reduces fragmentation, but incurs110/// more CPU-side binding overhead when drawing the mesh.111///112/// The default value is 256 MiB.113pub large_threshold: u64,114115/// The factor by which we scale a slab when growing it.116///117/// This value must be greater than 1. Higher values result in more118/// fragmentation but fewer expensive copy operations when growing the119/// buffer.120///121/// The default value is 1.5.122pub growth_factor: f64,123}124125impl Default for MeshAllocatorSettings {126fn default() -> Self {127Self {128// 1 MiB129min_slab_size: 1024 * 1024,130// 512 MiB131max_slab_size: 1024 * 1024 * 512,132// 256 MiB133large_threshold: 1024 * 1024 * 256,134// 1.5× growth135growth_factor: 1.5,136}137}138}139140/// The hardware buffer that mesh data lives in, as well as the range within141/// that buffer.142pub struct MeshBufferSlice<'a> {143/// The buffer that the mesh data resides in.144pub buffer: &'a Buffer,145146/// The range of elements within this buffer that the mesh data resides in,147/// measured in elements.148///149/// This is not a byte range; it's an element range. For vertex data, this150/// is measured in increments of a single vertex. (Thus, if a vertex is 32151/// bytes long, then this range is in units of 32 bytes each.) For index152/// data, this is measured in increments of a single index value (2 or 4153/// bytes). Draw commands generally take their ranges in elements, not154/// bytes, so this is the most convenient unit in this case.155pub range: Range<u32>,156}157158/// The index of a single slab.159#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]160#[repr(transparent)]161pub struct SlabId(pub NonMaxU32);162163/// Data for a single slab.164#[expect(165clippy::large_enum_variant,166reason = "See https://github.com/bevyengine/bevy/issues/19220"167)]168enum Slab {169/// A slab that can contain multiple objects.170General(GeneralSlab),171/// A slab that contains a single object.172LargeObject(LargeObjectSlab),173}174175/// A resizable slab that can contain multiple objects.176///177/// This is the normal type of slab used for objects that are below the178/// [`MeshAllocatorSettings::large_threshold`]. Slabs are divided into *slots*,179/// which are described in detail in the [`ElementLayout`] documentation.180struct GeneralSlab {181/// The [`Allocator`] that manages the objects in this slab.182allocator: Allocator,183184/// The GPU buffer that backs this slab.185///186/// This may be `None` if the buffer hasn't been created yet. We delay187/// creation of buffers until allocating all the meshes for a single frame,188/// so that we don't needlessly create and resize buffers when many meshes189/// load all at once.190buffer: Option<Buffer>,191192/// Allocations that are on the GPU.193///194/// The range is in slots.195resident_allocations: HashMap<AssetId<Mesh>, SlabAllocation>,196197/// Allocations that are waiting to be uploaded to the GPU.198///199/// The range is in slots.200pending_allocations: HashMap<AssetId<Mesh>, SlabAllocation>,201202/// The layout of a single element (vertex or index).203element_layout: ElementLayout,204205/// The size of this slab in slots.206current_slot_capacity: u32,207}208209/// A slab that contains a single object.210///211/// Typically, this is for objects that exceed the212/// [`MeshAllocatorSettings::large_threshold`]. This is also for objects that213/// would ordinarily receive their own slab but can't because of platform214/// limitations, most notably vertex arrays on WebGL 2.215struct LargeObjectSlab {216/// The GPU buffer that backs this slab.217///218/// This may be `None` if the buffer hasn't been created yet.219buffer: Option<Buffer>,220221/// The layout of a single element (vertex or index).222element_layout: ElementLayout,223}224225/// The type of element that a slab can store.226#[derive(Clone, Copy, PartialEq, Eq, Hash)]227enum ElementClass {228/// Data for a vertex.229Vertex,230/// A vertex index.231Index,232}233234/// The results of [`GeneralSlab::grow_if_necessary`].235enum SlabGrowthResult {236/// The mesh data already fits in the slab; the slab doesn't need to grow.237NoGrowthNeeded,238/// The slab needed to grow.239///240/// The [`SlabToReallocate`] contains the old capacity of the slab.241NeededGrowth(SlabToReallocate),242/// The slab wanted to grow but couldn't because it hit its maximum size.243CantGrow,244}245246/// Information about the size of individual elements (vertices or indices)247/// within a slab.248///249/// Slab objects are allocated in units of *slots*. Usually, each element takes250/// up one slot, and so elements and slots are equivalent. Occasionally,251/// however, a slot may consist of 2 or even 4 elements. This occurs when the252/// size of an element isn't divisible by [`COPY_BUFFER_ALIGNMENT`]. When we253/// resize buffers, we perform GPU-to-GPU copies to shuffle the existing254/// elements into their new positions, and such copies must be on255/// [`COPY_BUFFER_ALIGNMENT`] boundaries. Slots solve this problem by256/// guaranteeing that the size of an allocation quantum is divisible by both the257/// size of an element and [`COPY_BUFFER_ALIGNMENT`], so we can relocate it258/// freely.259#[derive(Clone, Copy, PartialEq, Eq, Hash)]260struct ElementLayout {261/// Either a vertex or an index.262class: ElementClass,263264/// The size in bytes of a single element (vertex or index).265size: u64,266267/// The number of elements that make up a single slot.268///269/// Usually, this is 1, but it can be different if [`ElementLayout::size`]270/// isn't divisible by 4. See the comment in [`ElementLayout`] for more271/// details.272elements_per_slot: u32,273}274275/// The location of an allocation and the slab it's contained in.276struct MeshAllocation {277/// The ID of the slab.278slab_id: SlabId,279/// Holds the actual allocation.280slab_allocation: SlabAllocation,281}282283/// An allocation within a slab.284#[derive(Clone)]285struct SlabAllocation {286/// The actual [`Allocator`] handle, needed to free the allocation.287allocation: Allocation,288/// The number of slots that this allocation takes up.289slot_count: u32,290}291292/// Holds information about all slabs scheduled to be allocated or reallocated.293#[derive(Default, Deref, DerefMut)]294struct SlabsToReallocate(HashMap<SlabId, SlabToReallocate>);295296/// Holds information about a slab that's scheduled to be allocated or297/// reallocated.298#[derive(Default)]299struct SlabToReallocate {300/// The capacity of the slab before we decided to grow it.301old_slot_capacity: u32,302}303304impl Display for SlabId {305fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {306self.0.fmt(f)307}308}309310impl Plugin for MeshAllocatorPlugin {311fn build(&self, app: &mut App) {312let Some(render_app) = app.get_sub_app_mut(RenderApp) else {313return;314};315316render_app317.init_resource::<MeshAllocatorSettings>()318.add_systems(319Render,320allocate_and_free_meshes321.in_set(RenderSystems::PrepareAssets)322.before(prepare_assets::<RenderMesh>),323);324}325326fn finish(&self, app: &mut App) {327let Some(render_app) = app.get_sub_app_mut(RenderApp) else {328return;329};330331// The `RenderAdapter` isn't available until now, so we can't do this in332// [`Plugin::build`].333render_app.init_resource::<MeshAllocator>();334}335}336337impl FromWorld for MeshAllocator {338fn from_world(world: &mut World) -> Self {339// Note whether we're on WebGL 2. In this case, we must give every340// vertex array its own slab.341let render_adapter = world.resource::<RenderAdapter>();342let general_vertex_slabs_supported = render_adapter343.get_downlevel_capabilities()344.flags345.contains(DownlevelFlags::BASE_VERTEX);346347Self {348slabs: HashMap::default(),349slab_layouts: HashMap::default(),350mesh_id_to_vertex_slab: HashMap::default(),351mesh_id_to_index_slab: HashMap::default(),352next_slab_id: default(),353general_vertex_slabs_supported,354extra_buffer_usages: BufferUsages::empty(),355}356}357}358359/// A system that processes newly-extracted or newly-removed meshes and writes360/// their data into buffers or frees their data as appropriate.361pub fn allocate_and_free_meshes(362mut mesh_allocator: ResMut<MeshAllocator>,363mesh_allocator_settings: Res<MeshAllocatorSettings>,364extracted_meshes: Res<ExtractedAssets<RenderMesh>>,365mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,366render_device: Res<RenderDevice>,367render_queue: Res<RenderQueue>,368) {369// Process removed or modified meshes.370mesh_allocator.free_meshes(&extracted_meshes);371372// Process newly-added or modified meshes.373mesh_allocator.allocate_meshes(374&mesh_allocator_settings,375&extracted_meshes,376&mut mesh_vertex_buffer_layouts,377&render_device,378&render_queue,379);380}381382impl MeshAllocator {383/// Returns the buffer and range within that buffer of the vertex data for384/// the mesh with the given ID.385///386/// If the mesh wasn't allocated, returns None.387pub fn mesh_vertex_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {388self.mesh_slice_in_slab(mesh_id, *self.mesh_id_to_vertex_slab.get(mesh_id)?)389}390391/// Returns the buffer and range within that buffer of the index data for392/// the mesh with the given ID.393///394/// If the mesh has no index data or wasn't allocated, returns None.395pub fn mesh_index_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {396self.mesh_slice_in_slab(mesh_id, *self.mesh_id_to_index_slab.get(mesh_id)?)397}398399/// Returns the IDs of the vertex buffer and index buffer respectively for400/// the mesh with the given ID.401///402/// If the mesh wasn't allocated, or has no index data in the case of the403/// index buffer, the corresponding element in the returned tuple will be404/// None.405pub fn mesh_slabs(&self, mesh_id: &AssetId<Mesh>) -> (Option<SlabId>, Option<SlabId>) {406(407self.mesh_id_to_vertex_slab.get(mesh_id).cloned(),408self.mesh_id_to_index_slab.get(mesh_id).cloned(),409)410}411412/// Given a slab and a mesh with data located with it, returns the buffer413/// and range of that mesh data within the slab.414fn mesh_slice_in_slab(415&self,416mesh_id: &AssetId<Mesh>,417slab_id: SlabId,418) -> Option<MeshBufferSlice<'_>> {419match self.slabs.get(&slab_id)? {420Slab::General(general_slab) => {421let slab_allocation = general_slab.resident_allocations.get(mesh_id)?;422Some(MeshBufferSlice {423buffer: general_slab.buffer.as_ref()?,424range: (slab_allocation.allocation.offset425* general_slab.element_layout.elements_per_slot)426..((slab_allocation.allocation.offset + slab_allocation.slot_count)427* general_slab.element_layout.elements_per_slot),428})429}430431Slab::LargeObject(large_object_slab) => {432let buffer = large_object_slab.buffer.as_ref()?;433Some(MeshBufferSlice {434buffer,435range: 0..((buffer.size() / large_object_slab.element_layout.size) as u32),436})437}438}439}440441/// Processes newly-loaded meshes, allocating room in the slabs for their442/// mesh data and performing upload operations as appropriate.443fn allocate_meshes(444&mut self,445mesh_allocator_settings: &MeshAllocatorSettings,446extracted_meshes: &ExtractedAssets<RenderMesh>,447mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,448render_device: &RenderDevice,449render_queue: &RenderQueue,450) {451let mut slabs_to_grow = SlabsToReallocate::default();452453// Allocate.454for (mesh_id, mesh) in &extracted_meshes.extracted {455let vertex_buffer_size = mesh.get_vertex_buffer_size() as u64;456if vertex_buffer_size == 0 {457continue;458}459// Allocate vertex data. Note that we can only pack mesh vertex data460// together if the platform supports it.461let vertex_element_layout = ElementLayout::vertex(mesh_vertex_buffer_layouts, mesh);462if self.general_vertex_slabs_supported {463self.allocate(464mesh_id,465vertex_buffer_size,466vertex_element_layout,467&mut slabs_to_grow,468mesh_allocator_settings,469);470} else {471self.allocate_large(mesh_id, vertex_element_layout);472}473474// Allocate index data.475if let (Some(index_buffer_data), Some(index_element_layout)) =476(mesh.get_index_buffer_bytes(), ElementLayout::index(mesh))477{478self.allocate(479mesh_id,480index_buffer_data.len() as u64,481index_element_layout,482&mut slabs_to_grow,483mesh_allocator_settings,484);485}486}487488// Perform growth.489for (slab_id, slab_to_grow) in slabs_to_grow.0 {490self.reallocate_slab(render_device, render_queue, slab_id, slab_to_grow);491}492493// Copy new mesh data in.494for (mesh_id, mesh) in &extracted_meshes.extracted {495self.copy_mesh_vertex_data(mesh_id, mesh, render_device, render_queue);496self.copy_mesh_index_data(mesh_id, mesh, render_device, render_queue);497}498}499500/// Copies vertex array data from a mesh into the appropriate spot in the501/// slab.502fn copy_mesh_vertex_data(503&mut self,504mesh_id: &AssetId<Mesh>,505mesh: &Mesh,506render_device: &RenderDevice,507render_queue: &RenderQueue,508) {509let Some(&slab_id) = self.mesh_id_to_vertex_slab.get(mesh_id) else {510return;511};512513// Call the generic function.514self.copy_element_data(515mesh_id,516mesh.get_vertex_buffer_size(),517|slice| mesh.write_packed_vertex_buffer_data(slice),518BufferUsages::VERTEX,519slab_id,520render_device,521render_queue,522);523}524525/// Copies index array data from a mesh into the appropriate spot in the526/// slab.527fn copy_mesh_index_data(528&mut self,529mesh_id: &AssetId<Mesh>,530mesh: &Mesh,531render_device: &RenderDevice,532render_queue: &RenderQueue,533) {534let Some(&slab_id) = self.mesh_id_to_index_slab.get(mesh_id) else {535return;536};537let Some(index_data) = mesh.get_index_buffer_bytes() else {538return;539};540541// Call the generic function.542self.copy_element_data(543mesh_id,544index_data.len(),545|slice| slice.copy_from_slice(index_data),546BufferUsages::INDEX,547slab_id,548render_device,549render_queue,550);551}552553/// A generic function that copies either vertex or index data into a slab.554fn copy_element_data(555&mut self,556mesh_id: &AssetId<Mesh>,557len: usize,558fill_data: impl Fn(&mut [u8]),559buffer_usages: BufferUsages,560slab_id: SlabId,561render_device: &RenderDevice,562render_queue: &RenderQueue,563) {564let Some(slab) = self.slabs.get_mut(&slab_id) else {565return;566};567568match *slab {569Slab::General(ref mut general_slab) => {570let (Some(buffer), Some(allocated_range)) = (571&general_slab.buffer,572general_slab.pending_allocations.remove(mesh_id),573) else {574return;575};576577let slot_size = general_slab.element_layout.slot_size();578579// round up size to a multiple of the slot size to satisfy wgpu alignment requirements580if let Some(size) = BufferSize::new((len as u64).next_multiple_of(slot_size)) {581// Write the data in.582if let Some(mut buffer) = render_queue.write_buffer_with(583buffer,584allocated_range.allocation.offset as u64 * slot_size,585size,586) {587let slice = &mut buffer.as_mut()[..len];588fill_data(slice);589}590}591592// Mark the allocation as resident.593general_slab594.resident_allocations595.insert(*mesh_id, allocated_range);596}597598Slab::LargeObject(ref mut large_object_slab) => {599debug_assert!(large_object_slab.buffer.is_none());600601// Create the buffer and its data in one go.602let buffer = render_device.create_buffer(&BufferDescriptor {603label: Some(&format!(604"large mesh slab {} ({}buffer)",605slab_id,606buffer_usages_to_str(buffer_usages)607)),608size: len as u64,609usage: buffer_usages | BufferUsages::COPY_DST | self.extra_buffer_usages,610mapped_at_creation: true,611});612{613let slice = &mut buffer.slice(..).get_mapped_range_mut()[..len];614fill_data(slice);615}616buffer.unmap();617large_object_slab.buffer = Some(buffer);618}619}620}621622/// Frees allocations for meshes that were removed or modified this frame.623fn free_meshes(&mut self, extracted_meshes: &ExtractedAssets<RenderMesh>) {624let mut empty_slabs = <HashSet<_>>::default();625626// TODO: Consider explicitly reusing allocations for changed meshes of the same size627let meshes_to_free = extracted_meshes628.removed629.iter()630.chain(extracted_meshes.modified.iter());631632for mesh_id in meshes_to_free {633if let Some(slab_id) = self.mesh_id_to_vertex_slab.remove(mesh_id) {634self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs);635}636if let Some(slab_id) = self.mesh_id_to_index_slab.remove(mesh_id) {637self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs);638}639}640641for empty_slab in empty_slabs {642self.slab_layouts.values_mut().for_each(|slab_ids| {643let idx = slab_ids.iter().position(|&slab_id| slab_id == empty_slab);644if let Some(idx) = idx {645slab_ids.remove(idx);646}647});648self.slabs.remove(&empty_slab);649}650}651652/// Given a slab and the ID of a mesh containing data in it, marks the653/// allocation as free.654///655/// If this results in the slab becoming empty, this function adds the slab656/// to the `empty_slabs` set.657fn free_allocation_in_slab(658&mut self,659mesh_id: &AssetId<Mesh>,660slab_id: SlabId,661empty_slabs: &mut HashSet<SlabId>,662) {663let Some(slab) = self.slabs.get_mut(&slab_id) else {664return;665};666667match *slab {668Slab::General(ref mut general_slab) => {669let Some(slab_allocation) = general_slab670.resident_allocations671.remove(mesh_id)672.or_else(|| general_slab.pending_allocations.remove(mesh_id))673else {674return;675};676677general_slab.allocator.free(slab_allocation.allocation);678679if general_slab.is_empty() {680empty_slabs.insert(slab_id);681}682}683Slab::LargeObject(_) => {684empty_slabs.insert(slab_id);685}686}687}688689/// Allocates space for mesh data with the given byte size and layout in the690/// appropriate slab, creating that slab if necessary.691fn allocate(692&mut self,693mesh_id: &AssetId<Mesh>,694data_byte_len: u64,695layout: ElementLayout,696slabs_to_grow: &mut SlabsToReallocate,697settings: &MeshAllocatorSettings,698) {699let data_element_count = data_byte_len.div_ceil(layout.size) as u32;700let data_slot_count = data_element_count.div_ceil(layout.elements_per_slot);701702// If the mesh data is too large for a slab, give it a slab of its own.703if data_slot_count as u64 * layout.slot_size()704>= settings.large_threshold.min(settings.max_slab_size)705{706self.allocate_large(mesh_id, layout);707} else {708self.allocate_general(mesh_id, data_slot_count, layout, slabs_to_grow, settings);709}710}711712/// Allocates space for mesh data with the given slot size and layout in the713/// appropriate general slab.714fn allocate_general(715&mut self,716mesh_id: &AssetId<Mesh>,717data_slot_count: u32,718layout: ElementLayout,719slabs_to_grow: &mut SlabsToReallocate,720settings: &MeshAllocatorSettings,721) {722let candidate_slabs = self.slab_layouts.entry(layout).or_default();723724// Loop through the slabs that accept elements of the appropriate type725// and try to allocate the mesh inside them. We go with the first one726// that succeeds.727let mut mesh_allocation = None;728for &slab_id in &*candidate_slabs {729let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {730unreachable!("Slab not found")731};732733let Some(allocation) = slab.allocator.allocate(data_slot_count) else {734continue;735};736737// Try to fit the object in the slab, growing if necessary.738match slab.grow_if_necessary(allocation.offset + data_slot_count, settings) {739SlabGrowthResult::NoGrowthNeeded => {}740SlabGrowthResult::NeededGrowth(slab_to_reallocate) => {741// If we already grew the slab this frame, don't replace the742// `SlabToReallocate` entry. We want to keep the entry743// corresponding to the size that the slab had at the start744// of the frame, so that we can copy only the used portion745// of the initial buffer to the new one.746if let Entry::Vacant(vacant_entry) = slabs_to_grow.entry(slab_id) {747vacant_entry.insert(slab_to_reallocate);748}749}750SlabGrowthResult::CantGrow => continue,751}752753mesh_allocation = Some(MeshAllocation {754slab_id,755slab_allocation: SlabAllocation {756allocation,757slot_count: data_slot_count,758},759});760break;761}762763// If we still have no allocation, make a new slab.764if mesh_allocation.is_none() {765let new_slab_id = self.next_slab_id;766self.next_slab_id.0 = NonMaxU32::new(self.next_slab_id.0.get() + 1).unwrap_or_default();767768let new_slab = GeneralSlab::new(769new_slab_id,770&mut mesh_allocation,771settings,772layout,773data_slot_count,774);775776self.slabs.insert(new_slab_id, Slab::General(new_slab));777candidate_slabs.push(new_slab_id);778slabs_to_grow.insert(new_slab_id, SlabToReallocate::default());779}780781let mesh_allocation = mesh_allocation.expect("Should have been able to allocate");782783// Mark the allocation as pending. Don't copy it in just yet; further784// meshes loaded this frame may result in its final allocation location785// changing.786if let Some(Slab::General(general_slab)) = self.slabs.get_mut(&mesh_allocation.slab_id) {787general_slab788.pending_allocations789.insert(*mesh_id, mesh_allocation.slab_allocation);790};791792self.record_allocation(mesh_id, mesh_allocation.slab_id, layout.class);793}794795/// Allocates an object into its own dedicated slab.796fn allocate_large(&mut self, mesh_id: &AssetId<Mesh>, layout: ElementLayout) {797let new_slab_id = self.next_slab_id;798self.next_slab_id.0 = NonMaxU32::new(self.next_slab_id.0.get() + 1).unwrap_or_default();799800self.record_allocation(mesh_id, new_slab_id, layout.class);801802self.slabs.insert(803new_slab_id,804Slab::LargeObject(LargeObjectSlab {805buffer: None,806element_layout: layout,807}),808);809}810811/// Reallocates a slab that needs to be resized, or allocates a new slab.812///813/// This performs the actual growth operation that814/// [`GeneralSlab::grow_if_necessary`] scheduled. We do the growth in two815/// phases so that, if a slab grows multiple times in the same frame, only816/// one new buffer is reallocated, rather than reallocating the buffer817/// multiple times.818fn reallocate_slab(819&mut self,820render_device: &RenderDevice,821render_queue: &RenderQueue,822slab_id: SlabId,823slab_to_grow: SlabToReallocate,824) {825let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {826error!("Couldn't find slab {} to grow", slab_id);827return;828};829830let old_buffer = slab.buffer.take();831832let mut buffer_usages = BufferUsages::COPY_SRC | BufferUsages::COPY_DST;833match slab.element_layout.class {834ElementClass::Vertex => buffer_usages |= BufferUsages::VERTEX,835ElementClass::Index => buffer_usages |= BufferUsages::INDEX,836};837838// Create the buffer.839let new_buffer = render_device.create_buffer(&BufferDescriptor {840label: Some(&format!(841"general mesh slab {} ({}buffer)",842slab_id,843buffer_usages_to_str(buffer_usages)844)),845size: slab.current_slot_capacity as u64 * slab.element_layout.slot_size(),846usage: buffer_usages | self.extra_buffer_usages,847mapped_at_creation: false,848});849850slab.buffer = Some(new_buffer.clone());851852let Some(old_buffer) = old_buffer else { return };853854// In order to do buffer copies, we need a command encoder.855let mut encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {856label: Some("slab resize encoder"),857});858859// Copy the data from the old buffer into the new one.860encoder.copy_buffer_to_buffer(861&old_buffer,8620,863&new_buffer,8640,865slab_to_grow.old_slot_capacity as u64 * slab.element_layout.slot_size(),866);867868let command_buffer = encoder.finish();869render_queue.submit([command_buffer]);870}871872/// Records the location of the given newly-allocated mesh data in the873/// [`Self::mesh_id_to_vertex_slab`] or [`Self::mesh_id_to_index_slab`]874/// tables as appropriate.875fn record_allocation(876&mut self,877mesh_id: &AssetId<Mesh>,878slab_id: SlabId,879element_class: ElementClass,880) {881match element_class {882ElementClass::Vertex => {883self.mesh_id_to_vertex_slab.insert(*mesh_id, slab_id);884}885ElementClass::Index => {886self.mesh_id_to_index_slab.insert(*mesh_id, slab_id);887}888}889}890}891892impl GeneralSlab {893/// Creates a new growable slab big enough to hold a single element of894/// `data_slot_count` size with the given `layout`.895fn new(896new_slab_id: SlabId,897mesh_allocation: &mut Option<MeshAllocation>,898settings: &MeshAllocatorSettings,899layout: ElementLayout,900data_slot_count: u32,901) -> GeneralSlab {902let initial_slab_slot_capacity = (settings.min_slab_size.div_ceil(layout.slot_size())903as u32)904.max(offset_allocator::ext::min_allocator_size(data_slot_count));905let max_slab_slot_capacity = (settings.max_slab_size.div_ceil(layout.slot_size()) as u32)906.max(offset_allocator::ext::min_allocator_size(data_slot_count));907908let mut new_slab = GeneralSlab {909allocator: Allocator::new(max_slab_slot_capacity),910buffer: None,911resident_allocations: HashMap::default(),912pending_allocations: HashMap::default(),913element_layout: layout,914current_slot_capacity: initial_slab_slot_capacity,915};916917// This should never fail.918if let Some(allocation) = new_slab.allocator.allocate(data_slot_count) {919*mesh_allocation = Some(MeshAllocation {920slab_id: new_slab_id,921slab_allocation: SlabAllocation {922slot_count: data_slot_count,923allocation,924},925});926}927928new_slab929}930931/// Checks to see if the size of this slab is at least `new_size_in_slots`932/// and grows the slab if it isn't.933///934/// The returned [`SlabGrowthResult`] describes whether the slab needed to935/// grow and whether, if so, it was successful in doing so.936fn grow_if_necessary(937&mut self,938new_size_in_slots: u32,939settings: &MeshAllocatorSettings,940) -> SlabGrowthResult {941// Is the slab big enough already?942let initial_slot_capacity = self.current_slot_capacity;943if self.current_slot_capacity >= new_size_in_slots {944return SlabGrowthResult::NoGrowthNeeded;945}946947// Try to grow in increments of `MeshAllocatorSettings::growth_factor`948// until we're big enough.949while self.current_slot_capacity < new_size_in_slots {950let new_slab_slot_capacity =951((self.current_slot_capacity as f64 * settings.growth_factor).ceil() as u32)952.min((settings.max_slab_size / self.element_layout.slot_size()) as u32);953if new_slab_slot_capacity == self.current_slot_capacity {954// The slab is full.955return SlabGrowthResult::CantGrow;956}957958self.current_slot_capacity = new_slab_slot_capacity;959}960961// Tell our caller what we did.962SlabGrowthResult::NeededGrowth(SlabToReallocate {963old_slot_capacity: initial_slot_capacity,964})965}966}967968impl ElementLayout {969/// Creates an [`ElementLayout`] for mesh data of the given class (vertex or970/// index) with the given byte size.971fn new(class: ElementClass, size: u64) -> ElementLayout {972const {973assert!(4 == COPY_BUFFER_ALIGNMENT);974}975// this is equivalent to `4 / gcd(4,size)` but lets us not implement gcd.976// ping @atlv if above assert ever fails (likely never)977let elements_per_slot = [1, 4, 2, 4][size as usize & 3];978ElementLayout {979class,980size,981// Make sure that slot boundaries begin and end on982// `COPY_BUFFER_ALIGNMENT`-byte (4-byte) boundaries.983elements_per_slot,984}985}986987fn slot_size(&self) -> u64 {988self.size * self.elements_per_slot as u64989}990991/// Creates the appropriate [`ElementLayout`] for the given mesh's vertex992/// data.993fn vertex(994mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,995mesh: &Mesh,996) -> ElementLayout {997let mesh_vertex_buffer_layout =998mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);999ElementLayout::new(1000ElementClass::Vertex,1001mesh_vertex_buffer_layout.0.layout().array_stride,1002)1003}10041005/// Creates the appropriate [`ElementLayout`] for the given mesh's index1006/// data.1007fn index(mesh: &Mesh) -> Option<ElementLayout> {1008let size = match mesh.indices()? {1009Indices::U16(_) => 2,1010Indices::U32(_) => 4,1011};1012Some(ElementLayout::new(ElementClass::Index, size))1013}1014}10151016impl GeneralSlab {1017/// Returns true if this slab is empty.1018fn is_empty(&self) -> bool {1019self.resident_allocations.is_empty() && self.pending_allocations.is_empty()1020}1021}10221023/// Returns a string describing the given buffer usages.1024fn buffer_usages_to_str(buffer_usages: BufferUsages) -> &'static str {1025if buffer_usages.contains(BufferUsages::VERTEX) {1026"vertex "1027} else if buffer_usages.contains(BufferUsages::INDEX) {1028"index "1029} else {1030""1031}1032}103310341035