Path: blob/main/crates/bevy_render/src/mesh/allocator.rs
9331 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_log::error;20use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};21use bevy_utils::default;22use offset_allocator::{Allocation, Allocator};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}174175impl Slab {176pub fn buffer_size(&self) -> u64 {177match self {178Self::General(gs) => gs.buffer.as_ref().map(|buffer| buffer.size()).unwrap_or(0),179Self::LargeObject(lo) => lo.buffer.as_ref().map(|buffer| buffer.size()).unwrap_or(0),180}181}182}183184/// A resizable slab that can contain multiple objects.185///186/// This is the normal type of slab used for objects that are below the187/// [`MeshAllocatorSettings::large_threshold`]. Slabs are divided into *slots*,188/// which are described in detail in the [`ElementLayout`] documentation.189struct GeneralSlab {190/// The [`Allocator`] that manages the objects in this slab.191allocator: Allocator,192193/// The GPU buffer that backs this slab.194///195/// This may be `None` if the buffer hasn't been created yet. We delay196/// creation of buffers until allocating all the meshes for a single frame,197/// so that we don't needlessly create and resize buffers when many meshes198/// load all at once.199buffer: Option<Buffer>,200201/// Allocations that are on the GPU.202///203/// The range is in slots.204resident_allocations: HashMap<AssetId<Mesh>, SlabAllocation>,205206/// Allocations that are waiting to be uploaded to the GPU.207///208/// The range is in slots.209pending_allocations: HashMap<AssetId<Mesh>, SlabAllocation>,210211/// The layout of a single element (vertex or index).212element_layout: ElementLayout,213214/// The size of this slab in slots.215current_slot_capacity: u32,216}217218/// A slab that contains a single object.219///220/// Typically, this is for objects that exceed the221/// [`MeshAllocatorSettings::large_threshold`]. This is also for objects that222/// would ordinarily receive their own slab but can't because of platform223/// limitations, most notably vertex arrays on WebGL 2.224struct LargeObjectSlab {225/// The GPU buffer that backs this slab.226///227/// This may be `None` if the buffer hasn't been created yet.228buffer: Option<Buffer>,229230/// The layout of a single element (vertex or index).231element_layout: ElementLayout,232}233234/// The type of element that a slab can store.235#[derive(Clone, Copy, PartialEq, Eq, Hash)]236enum ElementClass {237/// Data for a vertex.238Vertex,239/// A vertex index.240Index,241}242243/// The results of [`GeneralSlab::grow_if_necessary`].244enum SlabGrowthResult {245/// The mesh data already fits in the slab; the slab doesn't need to grow.246NoGrowthNeeded,247/// The slab needed to grow.248///249/// The [`SlabToReallocate`] contains the old capacity of the slab.250NeededGrowth(SlabToReallocate),251/// The slab wanted to grow but couldn't because it hit its maximum size.252CantGrow,253}254255/// Information about the size of individual elements (vertices or indices)256/// within a slab.257///258/// Slab objects are allocated in units of *slots*. Usually, each element takes259/// up one slot, and so elements and slots are equivalent. Occasionally,260/// however, a slot may consist of 2 or even 4 elements. This occurs when the261/// size of an element isn't divisible by [`COPY_BUFFER_ALIGNMENT`]. When we262/// resize buffers, we perform GPU-to-GPU copies to shuffle the existing263/// elements into their new positions, and such copies must be on264/// [`COPY_BUFFER_ALIGNMENT`] boundaries. Slots solve this problem by265/// guaranteeing that the size of an allocation quantum is divisible by both the266/// size of an element and [`COPY_BUFFER_ALIGNMENT`], so we can relocate it267/// freely.268#[derive(Clone, Copy, PartialEq, Eq, Hash)]269struct ElementLayout {270/// Either a vertex or an index.271class: ElementClass,272273/// The size in bytes of a single element (vertex or index).274size: u64,275276/// The number of elements that make up a single slot.277///278/// Usually, this is 1, but it can be different if [`ElementLayout::size`]279/// isn't divisible by 4. See the comment in [`ElementLayout`] for more280/// details.281elements_per_slot: u32,282}283284/// The location of an allocation and the slab it's contained in.285struct MeshAllocation {286/// The ID of the slab.287slab_id: SlabId,288/// Holds the actual allocation.289slab_allocation: SlabAllocation,290}291292/// An allocation within a slab.293#[derive(Clone)]294struct SlabAllocation {295/// The actual [`Allocator`] handle, needed to free the allocation.296allocation: Allocation,297/// The number of slots that this allocation takes up.298slot_count: u32,299}300301/// Holds information about all slabs scheduled to be allocated or reallocated.302#[derive(Default, Deref, DerefMut)]303struct SlabsToReallocate(HashMap<SlabId, SlabToReallocate>);304305/// Holds information about a slab that's scheduled to be allocated or306/// reallocated.307#[derive(Default)]308struct SlabToReallocate {309/// The capacity of the slab before we decided to grow it.310old_slot_capacity: u32,311}312313impl Display for SlabId {314fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {315self.0.fmt(f)316}317}318319impl Plugin for MeshAllocatorPlugin {320fn build(&self, app: &mut App) {321let Some(render_app) = app.get_sub_app_mut(RenderApp) else {322return;323};324325render_app326.init_resource::<MeshAllocatorSettings>()327.add_systems(328Render,329allocate_and_free_meshes330.in_set(RenderSystems::PrepareAssets)331.before(prepare_assets::<RenderMesh>),332);333}334335fn finish(&self, app: &mut App) {336let Some(render_app) = app.get_sub_app_mut(RenderApp) else {337return;338};339340// The `RenderAdapter` isn't available until now, so we can't do this in341// [`Plugin::build`].342render_app.init_resource::<MeshAllocator>();343}344}345346impl FromWorld for MeshAllocator {347fn from_world(world: &mut World) -> Self {348// Note whether we're on WebGL 2. In this case, we must give every349// vertex array its own slab.350let render_adapter = world.resource::<RenderAdapter>();351let general_vertex_slabs_supported = render_adapter352.get_downlevel_capabilities()353.flags354.contains(DownlevelFlags::BASE_VERTEX);355356Self {357slabs: HashMap::default(),358slab_layouts: HashMap::default(),359mesh_id_to_vertex_slab: HashMap::default(),360mesh_id_to_index_slab: HashMap::default(),361next_slab_id: default(),362general_vertex_slabs_supported,363extra_buffer_usages: BufferUsages::empty(),364}365}366}367368/// A system that processes newly-extracted or newly-removed meshes and writes369/// their data into buffers or frees their data as appropriate.370pub fn allocate_and_free_meshes(371mut mesh_allocator: ResMut<MeshAllocator>,372mesh_allocator_settings: Res<MeshAllocatorSettings>,373extracted_meshes: Res<ExtractedAssets<RenderMesh>>,374mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,375render_device: Res<RenderDevice>,376render_queue: Res<RenderQueue>,377) {378// Process removed or modified meshes.379mesh_allocator.free_meshes(&extracted_meshes);380381// Process newly-added or modified meshes.382mesh_allocator.allocate_meshes(383&mesh_allocator_settings,384&extracted_meshes,385&mut mesh_vertex_buffer_layouts,386&render_device,387&render_queue,388);389}390391impl MeshAllocator {392/// Returns the buffer and range within that buffer of the vertex data for393/// the mesh with the given ID.394///395/// If the mesh wasn't allocated, returns None.396pub fn mesh_vertex_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {397self.mesh_slice_in_slab(mesh_id, *self.mesh_id_to_vertex_slab.get(mesh_id)?)398}399400/// Returns the buffer and range within that buffer of the index data for401/// the mesh with the given ID.402///403/// If the mesh has no index data or wasn't allocated, returns None.404pub fn mesh_index_slice(&self, mesh_id: &AssetId<Mesh>) -> Option<MeshBufferSlice<'_>> {405self.mesh_slice_in_slab(mesh_id, *self.mesh_id_to_index_slab.get(mesh_id)?)406}407408/// Returns the IDs of the vertex buffer and index buffer respectively for409/// the mesh with the given ID.410///411/// If the mesh wasn't allocated, or has no index data in the case of the412/// index buffer, the corresponding element in the returned tuple will be413/// None.414pub fn mesh_slabs(&self, mesh_id: &AssetId<Mesh>) -> (Option<SlabId>, Option<SlabId>) {415(416self.mesh_id_to_vertex_slab.get(mesh_id).cloned(),417self.mesh_id_to_index_slab.get(mesh_id).cloned(),418)419}420421/// Get the number of allocated slabs422pub fn slab_count(&self) -> usize {423self.slabs.len()424}425426/// Get the total size of all allocated slabs427pub fn slabs_size(&self) -> u64 {428self.slabs.iter().map(|slab| slab.1.buffer_size()).sum()429}430431pub fn allocations(&self) -> usize {432self.mesh_id_to_index_slab.len()433}434435/// Given a slab and a mesh with data located with it, returns the buffer436/// and range of that mesh data within the slab.437fn mesh_slice_in_slab(438&self,439mesh_id: &AssetId<Mesh>,440slab_id: SlabId,441) -> Option<MeshBufferSlice<'_>> {442match self.slabs.get(&slab_id)? {443Slab::General(general_slab) => {444let slab_allocation = general_slab.resident_allocations.get(mesh_id)?;445Some(MeshBufferSlice {446buffer: general_slab.buffer.as_ref()?,447range: (slab_allocation.allocation.offset448* general_slab.element_layout.elements_per_slot)449..((slab_allocation.allocation.offset + slab_allocation.slot_count)450* general_slab.element_layout.elements_per_slot),451})452}453454Slab::LargeObject(large_object_slab) => {455let buffer = large_object_slab.buffer.as_ref()?;456Some(MeshBufferSlice {457buffer,458range: 0..((buffer.size() / large_object_slab.element_layout.size) as u32),459})460}461}462}463464/// Processes newly-loaded meshes, allocating room in the slabs for their465/// mesh data and performing upload operations as appropriate.466fn allocate_meshes(467&mut self,468mesh_allocator_settings: &MeshAllocatorSettings,469extracted_meshes: &ExtractedAssets<RenderMesh>,470mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,471render_device: &RenderDevice,472render_queue: &RenderQueue,473) {474let mut slabs_to_grow = SlabsToReallocate::default();475476// Allocate.477for (mesh_id, mesh) in &extracted_meshes.extracted {478let vertex_buffer_size = mesh.get_vertex_buffer_size() as u64;479if vertex_buffer_size == 0 {480continue;481}482// Allocate vertex data. Note that we can only pack mesh vertex data483// together if the platform supports it.484let vertex_element_layout = ElementLayout::vertex(mesh_vertex_buffer_layouts, mesh);485if self.general_vertex_slabs_supported {486self.allocate(487mesh_id,488vertex_buffer_size,489vertex_element_layout,490&mut slabs_to_grow,491mesh_allocator_settings,492);493} else {494self.allocate_large(mesh_id, vertex_element_layout);495}496497// Allocate index data.498if let (Some(index_buffer_data), Some(index_element_layout)) =499(mesh.get_index_buffer_bytes(), ElementLayout::index(mesh))500{501self.allocate(502mesh_id,503index_buffer_data.len() as u64,504index_element_layout,505&mut slabs_to_grow,506mesh_allocator_settings,507);508}509}510511// Perform growth.512for (slab_id, slab_to_grow) in slabs_to_grow.0 {513self.reallocate_slab(render_device, render_queue, slab_id, slab_to_grow);514}515516// Copy new mesh data in.517for (mesh_id, mesh) in &extracted_meshes.extracted {518self.copy_mesh_vertex_data(mesh_id, mesh, render_device, render_queue);519self.copy_mesh_index_data(mesh_id, mesh, render_device, render_queue);520}521}522523/// Copies vertex array data from a mesh into the appropriate spot in the524/// slab.525fn copy_mesh_vertex_data(526&mut self,527mesh_id: &AssetId<Mesh>,528mesh: &Mesh,529render_device: &RenderDevice,530render_queue: &RenderQueue,531) {532let Some(&slab_id) = self.mesh_id_to_vertex_slab.get(mesh_id) else {533return;534};535536// Call the generic function.537self.copy_element_data(538mesh_id,539mesh.get_vertex_buffer_size(),540|slice| mesh.write_packed_vertex_buffer_data(slice),541BufferUsages::VERTEX,542slab_id,543render_device,544render_queue,545);546}547548/// Copies index array data from a mesh into the appropriate spot in the549/// slab.550fn copy_mesh_index_data(551&mut self,552mesh_id: &AssetId<Mesh>,553mesh: &Mesh,554render_device: &RenderDevice,555render_queue: &RenderQueue,556) {557let Some(&slab_id) = self.mesh_id_to_index_slab.get(mesh_id) else {558return;559};560let Some(index_data) = mesh.get_index_buffer_bytes() else {561return;562};563564// Call the generic function.565self.copy_element_data(566mesh_id,567index_data.len(),568|slice| slice.copy_from_slice(index_data),569BufferUsages::INDEX,570slab_id,571render_device,572render_queue,573);574}575576/// A generic function that copies either vertex or index data into a slab.577fn copy_element_data(578&mut self,579mesh_id: &AssetId<Mesh>,580len: usize,581fill_data: impl Fn(&mut [u8]),582buffer_usages: BufferUsages,583slab_id: SlabId,584render_device: &RenderDevice,585render_queue: &RenderQueue,586) {587let Some(slab) = self.slabs.get_mut(&slab_id) else {588return;589};590591match *slab {592Slab::General(ref mut general_slab) => {593let (Some(buffer), Some(allocated_range)) = (594&general_slab.buffer,595general_slab.pending_allocations.remove(mesh_id),596) else {597return;598};599600let slot_size = general_slab.element_layout.slot_size();601602// round up size to a multiple of the slot size to satisfy wgpu alignment requirements603if let Some(size) = BufferSize::new((len as u64).next_multiple_of(slot_size)) {604// Write the data in.605if let Some(mut buffer) = render_queue.write_buffer_with(606buffer,607allocated_range.allocation.offset as u64 * slot_size,608size,609) {610let slice = &mut buffer.as_mut()[..len];611fill_data(slice);612}613}614615// Mark the allocation as resident.616general_slab617.resident_allocations618.insert(*mesh_id, allocated_range);619}620621Slab::LargeObject(ref mut large_object_slab) => {622debug_assert!(large_object_slab.buffer.is_none());623624// Create the buffer and its data in one go.625let buffer = render_device.create_buffer(&BufferDescriptor {626label: Some(&format!(627"large mesh slab {} ({}buffer)",628slab_id,629buffer_usages_to_str(buffer_usages)630)),631size: len as u64,632usage: buffer_usages | BufferUsages::COPY_DST | self.extra_buffer_usages,633mapped_at_creation: true,634});635{636let slice = &mut buffer.slice(..).get_mapped_range_mut()[..len];637fill_data(slice);638}639buffer.unmap();640large_object_slab.buffer = Some(buffer);641}642}643}644645/// Frees allocations for meshes that were removed or modified this frame.646fn free_meshes(&mut self, extracted_meshes: &ExtractedAssets<RenderMesh>) {647let mut empty_slabs = <HashSet<_>>::default();648649// TODO: Consider explicitly reusing allocations for changed meshes of the same size650let meshes_to_free = extracted_meshes651.removed652.iter()653.chain(extracted_meshes.modified.iter());654655for mesh_id in meshes_to_free {656if let Some(slab_id) = self.mesh_id_to_vertex_slab.remove(mesh_id) {657self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs);658}659if let Some(slab_id) = self.mesh_id_to_index_slab.remove(mesh_id) {660self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs);661}662}663664for empty_slab in empty_slabs {665self.slab_layouts.values_mut().for_each(|slab_ids| {666let idx = slab_ids.iter().position(|&slab_id| slab_id == empty_slab);667if let Some(idx) = idx {668slab_ids.remove(idx);669}670});671self.slabs.remove(&empty_slab);672}673}674675/// Given a slab and the ID of a mesh containing data in it, marks the676/// allocation as free.677///678/// If this results in the slab becoming empty, this function adds the slab679/// to the `empty_slabs` set.680fn free_allocation_in_slab(681&mut self,682mesh_id: &AssetId<Mesh>,683slab_id: SlabId,684empty_slabs: &mut HashSet<SlabId>,685) {686let Some(slab) = self.slabs.get_mut(&slab_id) else {687return;688};689690match *slab {691Slab::General(ref mut general_slab) => {692let Some(slab_allocation) = general_slab693.resident_allocations694.remove(mesh_id)695.or_else(|| general_slab.pending_allocations.remove(mesh_id))696else {697return;698};699700general_slab.allocator.free(slab_allocation.allocation);701702if general_slab.is_empty() {703empty_slabs.insert(slab_id);704}705}706Slab::LargeObject(_) => {707empty_slabs.insert(slab_id);708}709}710}711712/// Allocates space for mesh data with the given byte size and layout in the713/// appropriate slab, creating that slab if necessary.714fn allocate(715&mut self,716mesh_id: &AssetId<Mesh>,717data_byte_len: u64,718layout: ElementLayout,719slabs_to_grow: &mut SlabsToReallocate,720settings: &MeshAllocatorSettings,721) {722let data_element_count = data_byte_len.div_ceil(layout.size) as u32;723let data_slot_count = data_element_count.div_ceil(layout.elements_per_slot);724725// If the mesh data is too large for a slab, give it a slab of its own.726if data_slot_count as u64 * layout.slot_size()727>= settings.large_threshold.min(settings.max_slab_size)728{729self.allocate_large(mesh_id, layout);730} else {731self.allocate_general(mesh_id, data_slot_count, layout, slabs_to_grow, settings);732}733}734735/// Allocates space for mesh data with the given slot size and layout in the736/// appropriate general slab.737fn allocate_general(738&mut self,739mesh_id: &AssetId<Mesh>,740data_slot_count: u32,741layout: ElementLayout,742slabs_to_grow: &mut SlabsToReallocate,743settings: &MeshAllocatorSettings,744) {745let candidate_slabs = self.slab_layouts.entry(layout).or_default();746747// Loop through the slabs that accept elements of the appropriate type748// and try to allocate the mesh inside them. We go with the first one749// that succeeds.750let mut mesh_allocation = None;751for &slab_id in &*candidate_slabs {752let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {753unreachable!("Slab not found")754};755756let Some(allocation) = slab.allocator.allocate(data_slot_count) else {757continue;758};759760// Try to fit the object in the slab, growing if necessary.761match slab.grow_if_necessary(allocation.offset + data_slot_count, settings) {762SlabGrowthResult::NoGrowthNeeded => {}763SlabGrowthResult::NeededGrowth(slab_to_reallocate) => {764// If we already grew the slab this frame, don't replace the765// `SlabToReallocate` entry. We want to keep the entry766// corresponding to the size that the slab had at the start767// of the frame, so that we can copy only the used portion768// of the initial buffer to the new one.769if let Entry::Vacant(vacant_entry) = slabs_to_grow.entry(slab_id) {770vacant_entry.insert(slab_to_reallocate);771}772}773SlabGrowthResult::CantGrow => continue,774}775776mesh_allocation = Some(MeshAllocation {777slab_id,778slab_allocation: SlabAllocation {779allocation,780slot_count: data_slot_count,781},782});783break;784}785786// If we still have no allocation, make a new slab.787if mesh_allocation.is_none() {788let new_slab_id = self.next_slab_id;789self.next_slab_id.0 = NonMaxU32::new(self.next_slab_id.0.get() + 1).unwrap_or_default();790791let new_slab = GeneralSlab::new(792new_slab_id,793&mut mesh_allocation,794settings,795layout,796data_slot_count,797);798799self.slabs.insert(new_slab_id, Slab::General(new_slab));800candidate_slabs.push(new_slab_id);801slabs_to_grow.insert(new_slab_id, SlabToReallocate::default());802}803804let mesh_allocation = mesh_allocation.expect("Should have been able to allocate");805806// Mark the allocation as pending. Don't copy it in just yet; further807// meshes loaded this frame may result in its final allocation location808// changing.809if let Some(Slab::General(general_slab)) = self.slabs.get_mut(&mesh_allocation.slab_id) {810general_slab811.pending_allocations812.insert(*mesh_id, mesh_allocation.slab_allocation);813};814815self.record_allocation(mesh_id, mesh_allocation.slab_id, layout.class);816}817818/// Allocates an object into its own dedicated slab.819fn allocate_large(&mut self, mesh_id: &AssetId<Mesh>, layout: ElementLayout) {820let new_slab_id = self.next_slab_id;821self.next_slab_id.0 = NonMaxU32::new(self.next_slab_id.0.get() + 1).unwrap_or_default();822823self.record_allocation(mesh_id, new_slab_id, layout.class);824825self.slabs.insert(826new_slab_id,827Slab::LargeObject(LargeObjectSlab {828buffer: None,829element_layout: layout,830}),831);832}833834/// Reallocates a slab that needs to be resized, or allocates a new slab.835///836/// This performs the actual growth operation that837/// [`GeneralSlab::grow_if_necessary`] scheduled. We do the growth in two838/// phases so that, if a slab grows multiple times in the same frame, only839/// one new buffer is reallocated, rather than reallocating the buffer840/// multiple times.841fn reallocate_slab(842&mut self,843render_device: &RenderDevice,844render_queue: &RenderQueue,845slab_id: SlabId,846slab_to_grow: SlabToReallocate,847) {848let Some(Slab::General(slab)) = self.slabs.get_mut(&slab_id) else {849error!("Couldn't find slab {} to grow", slab_id);850return;851};852853let old_buffer = slab.buffer.take();854855let mut buffer_usages = BufferUsages::COPY_SRC | BufferUsages::COPY_DST;856match slab.element_layout.class {857ElementClass::Vertex => buffer_usages |= BufferUsages::VERTEX,858ElementClass::Index => buffer_usages |= BufferUsages::INDEX,859};860861// Create the buffer.862let new_buffer = render_device.create_buffer(&BufferDescriptor {863label: Some(&format!(864"general mesh slab {} ({}buffer)",865slab_id,866buffer_usages_to_str(buffer_usages)867)),868size: slab.current_slot_capacity as u64 * slab.element_layout.slot_size(),869usage: buffer_usages | self.extra_buffer_usages,870mapped_at_creation: false,871});872873slab.buffer = Some(new_buffer.clone());874875let Some(old_buffer) = old_buffer else { return };876877// In order to do buffer copies, we need a command encoder.878let mut encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {879label: Some("slab resize encoder"),880});881882// Copy the data from the old buffer into the new one.883encoder.copy_buffer_to_buffer(884&old_buffer,8850,886&new_buffer,8870,888slab_to_grow.old_slot_capacity as u64 * slab.element_layout.slot_size(),889);890891let command_buffer = encoder.finish();892render_queue.submit([command_buffer]);893}894895/// Records the location of the given newly-allocated mesh data in the896/// [`Self::mesh_id_to_vertex_slab`] or [`Self::mesh_id_to_index_slab`]897/// tables as appropriate.898fn record_allocation(899&mut self,900mesh_id: &AssetId<Mesh>,901slab_id: SlabId,902element_class: ElementClass,903) {904match element_class {905ElementClass::Vertex => {906self.mesh_id_to_vertex_slab.insert(*mesh_id, slab_id);907}908ElementClass::Index => {909self.mesh_id_to_index_slab.insert(*mesh_id, slab_id);910}911}912}913}914915impl GeneralSlab {916/// Creates a new growable slab big enough to hold a single element of917/// `data_slot_count` size with the given `layout`.918fn new(919new_slab_id: SlabId,920mesh_allocation: &mut Option<MeshAllocation>,921settings: &MeshAllocatorSettings,922layout: ElementLayout,923data_slot_count: u32,924) -> GeneralSlab {925let initial_slab_slot_capacity = (settings.min_slab_size.div_ceil(layout.slot_size())926as u32)927.max(offset_allocator::ext::min_allocator_size(data_slot_count));928let max_slab_slot_capacity = (settings.max_slab_size.div_ceil(layout.slot_size()) as u32)929.max(offset_allocator::ext::min_allocator_size(data_slot_count));930931let mut new_slab = GeneralSlab {932allocator: Allocator::new(max_slab_slot_capacity),933buffer: None,934resident_allocations: HashMap::default(),935pending_allocations: HashMap::default(),936element_layout: layout,937current_slot_capacity: initial_slab_slot_capacity,938};939940// This should never fail.941if let Some(allocation) = new_slab.allocator.allocate(data_slot_count) {942*mesh_allocation = Some(MeshAllocation {943slab_id: new_slab_id,944slab_allocation: SlabAllocation {945slot_count: data_slot_count,946allocation,947},948});949}950951new_slab952}953954/// Checks to see if the size of this slab is at least `new_size_in_slots`955/// and grows the slab if it isn't.956///957/// The returned [`SlabGrowthResult`] describes whether the slab needed to958/// grow and whether, if so, it was successful in doing so.959fn grow_if_necessary(960&mut self,961new_size_in_slots: u32,962settings: &MeshAllocatorSettings,963) -> SlabGrowthResult {964// Is the slab big enough already?965let initial_slot_capacity = self.current_slot_capacity;966if self.current_slot_capacity >= new_size_in_slots {967return SlabGrowthResult::NoGrowthNeeded;968}969970// Try to grow in increments of `MeshAllocatorSettings::growth_factor`971// until we're big enough.972while self.current_slot_capacity < new_size_in_slots {973let new_slab_slot_capacity =974((self.current_slot_capacity as f64 * settings.growth_factor).ceil() as u32)975.min((settings.max_slab_size / self.element_layout.slot_size()) as u32);976if new_slab_slot_capacity == self.current_slot_capacity {977// The slab is full.978return SlabGrowthResult::CantGrow;979}980981self.current_slot_capacity = new_slab_slot_capacity;982}983984// Tell our caller what we did.985SlabGrowthResult::NeededGrowth(SlabToReallocate {986old_slot_capacity: initial_slot_capacity,987})988}989}990991impl ElementLayout {992/// Creates an [`ElementLayout`] for mesh data of the given class (vertex or993/// index) with the given byte size.994fn new(class: ElementClass, size: u64) -> ElementLayout {995const {996assert!(4 == COPY_BUFFER_ALIGNMENT);997}998// this is equivalent to `4 / gcd(4,size)` but lets us not implement gcd.999// ping @atlv if above assert ever fails (likely never)1000let elements_per_slot = [1, 4, 2, 4][size as usize & 3];1001ElementLayout {1002class,1003size,1004// Make sure that slot boundaries begin and end on1005// `COPY_BUFFER_ALIGNMENT`-byte (4-byte) boundaries.1006elements_per_slot,1007}1008}10091010fn slot_size(&self) -> u64 {1011self.size * self.elements_per_slot as u641012}10131014/// Creates the appropriate [`ElementLayout`] for the given mesh's vertex1015/// data.1016fn vertex(1017mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,1018mesh: &Mesh,1019) -> ElementLayout {1020let mesh_vertex_buffer_layout =1021mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);1022ElementLayout::new(1023ElementClass::Vertex,1024mesh_vertex_buffer_layout.0.layout().array_stride,1025)1026}10271028/// Creates the appropriate [`ElementLayout`] for the given mesh's index1029/// data.1030fn index(mesh: &Mesh) -> Option<ElementLayout> {1031let size = match mesh.indices()? {1032Indices::U16(_) => 2,1033Indices::U32(_) => 4,1034};1035Some(ElementLayout::new(ElementClass::Index, size))1036}1037}10381039impl GeneralSlab {1040/// Returns true if this slab is empty.1041fn is_empty(&self) -> bool {1042self.resident_allocations.is_empty() && self.pending_allocations.is_empty()1043}1044}10451046/// Returns a string describing the given buffer usages.1047fn buffer_usages_to_str(buffer_usages: BufferUsages) -> &'static str {1048if buffer_usages.contains(BufferUsages::VERTEX) {1049"vertex "1050} else if buffer_usages.contains(BufferUsages::INDEX) {1051"index "1052} else {1053""1054}1055}105610571058