Path: blob/main/crates/bevy_pbr/src/meshlet/persistent_buffer.rs
6600 views
use bevy_render::{1render_resource::{2BindingResource, Buffer, BufferAddress, BufferDescriptor, BufferUsages,3CommandEncoderDescriptor, COPY_BUFFER_ALIGNMENT,4},5renderer::{RenderDevice, RenderQueue},6};7use core::{num::NonZero, ops::Range};8use range_alloc::RangeAllocator;910/// Wrapper for a GPU buffer holding a large amount of data that persists across frames.11pub struct PersistentGpuBuffer<T: PersistentGpuBufferable> {12/// Debug label for the buffer.13label: &'static str,14/// Handle to the GPU buffer.15buffer: Buffer,16/// Tracks free slices of the buffer.17allocation_planner: RangeAllocator<BufferAddress>,18/// Queue of pending writes, and associated metadata.19write_queue: Vec<(T, T::Metadata, Range<BufferAddress>)>,20}2122impl<T: PersistentGpuBufferable> PersistentGpuBuffer<T> {23/// Create a new persistent buffer.24pub fn new(label: &'static str, render_device: &RenderDevice) -> Self {25Self {26label,27buffer: render_device.create_buffer(&BufferDescriptor {28label: Some(label),29size: 0,30usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,31mapped_at_creation: false,32}),33allocation_planner: RangeAllocator::new(0..0),34write_queue: Vec::new(),35}36}3738/// Queue an item of type T to be added to the buffer, returning the byte range within the buffer that it will be located at.39pub fn queue_write(&mut self, data: T, metadata: T::Metadata) -> Range<BufferAddress> {40let data_size = data.size_in_bytes() as u64;41debug_assert!(data_size.is_multiple_of(COPY_BUFFER_ALIGNMENT));42if let Ok(buffer_slice) = self.allocation_planner.allocate_range(data_size) {43self.write_queue44.push((data, metadata, buffer_slice.clone()));45return buffer_slice;46}4748let buffer_size = self.allocation_planner.initial_range();49let double_buffer_size = (buffer_size.end - buffer_size.start) * 2;50let new_size = double_buffer_size.max(data_size);51self.allocation_planner.grow_to(buffer_size.end + new_size);5253let buffer_slice = self.allocation_planner.allocate_range(data_size).unwrap();54self.write_queue55.push((data, metadata, buffer_slice.clone()));56buffer_slice57}5859/// Upload all pending data to the GPU buffer.60pub fn perform_writes(&mut self, render_queue: &RenderQueue, render_device: &RenderDevice) {61if self.allocation_planner.initial_range().end > self.buffer.size() {62self.expand_buffer(render_device, render_queue);63}6465let queue_count = self.write_queue.len();6667for (data, metadata, buffer_slice) in self.write_queue.drain(..) {68let buffer_slice_size =69NonZero::<u64>::new(buffer_slice.end - buffer_slice.start).unwrap();70let mut buffer_view = render_queue71.write_buffer_with(&self.buffer, buffer_slice.start, buffer_slice_size)72.unwrap();73data.write_bytes_le(metadata, &mut buffer_view, buffer_slice.start);74}7576let queue_saturation = queue_count as f32 / self.write_queue.capacity() as f32;77if queue_saturation < 0.3 {78self.write_queue = Vec::new();79}80}8182/// Mark a section of the GPU buffer as no longer needed.83pub fn mark_slice_unused(&mut self, buffer_slice: Range<BufferAddress>) {84self.allocation_planner.free_range(buffer_slice);85}8687pub fn binding(&self) -> BindingResource<'_> {88self.buffer.as_entire_binding()89}9091/// Expand the buffer by creating a new buffer and copying old data over.92fn expand_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {93let size = self.allocation_planner.initial_range();94let new_buffer = render_device.create_buffer(&BufferDescriptor {95label: Some(self.label),96size: size.end - size.start,97usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,98mapped_at_creation: false,99});100101let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {102label: Some("persistent_gpu_buffer_expand"),103});104command_encoder.copy_buffer_to_buffer(&self.buffer, 0, &new_buffer, 0, self.buffer.size());105render_queue.submit([command_encoder.finish()]);106107self.buffer = new_buffer;108}109}110111/// A trait representing data that can be written to a [`PersistentGpuBuffer`].112pub trait PersistentGpuBufferable {113/// Additional metadata associated with each item, made available during `write_bytes_le`.114type Metadata;115116/// The size in bytes of `self`. This will determine the size of the buffer passed into117/// `write_bytes_le`.118///119/// All data written must be in a multiple of `wgpu::COPY_BUFFER_ALIGNMENT` bytes. Failure to do so will120/// result in a panic when using [`PersistentGpuBuffer`].121fn size_in_bytes(&self) -> usize;122123/// Convert `self` + `metadata` into bytes (little-endian), and write to the provided buffer slice.124/// Any bytes not written to in the slice will be zeroed out when uploaded to the GPU.125fn write_bytes_le(126&self,127metadata: Self::Metadata,128buffer_slice: &mut [u8],129buffer_offset: BufferAddress,130);131}132133134