Path: blob/main/crates/bevy_render/src/render_resource/buffer_vec.rs
6596 views
use core::{iter, marker::PhantomData};12use crate::{3render_resource::Buffer,4renderer::{RenderDevice, RenderQueue},5};6use bytemuck::{must_cast_slice, NoUninit};7use encase::{8internal::{WriteInto, Writer},9ShaderType,10};11use thiserror::Error;12use wgpu::{BindingResource, BufferAddress, BufferUsages};1314use super::GpuArrayBufferable;1516/// A structure for storing raw bytes that have already been properly formatted17/// for use by the GPU.18///19/// "Properly formatted" means that item data already meets the alignment and padding20/// requirements for how it will be used on the GPU. The item type must implement [`NoUninit`]21/// for its data representation to be directly copyable.22///23/// Index, vertex, and instance-rate vertex buffers have no alignment nor padding requirements and24/// so this helper type is a good choice for them.25///26/// The contained data is stored in system RAM. Calling [`reserve`](RawBufferVec::reserve)27/// allocates VRAM from the [`RenderDevice`].28/// [`write_buffer`](RawBufferVec::write_buffer) queues copying of the data29/// from system RAM to VRAM.30///31/// Other options for storing GPU-accessible data are:32/// * [`BufferVec`]33/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer)34/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)35/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)36/// * [`StorageBuffer`](crate::render_resource::StorageBuffer)37/// * [`Texture`](crate::render_resource::Texture)38/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)39pub struct RawBufferVec<T: NoUninit> {40values: Vec<T>,41buffer: Option<Buffer>,42capacity: usize,43item_size: usize,44buffer_usage: BufferUsages,45label: Option<String>,46changed: bool,47}4849impl<T: NoUninit> RawBufferVec<T> {50/// Creates a new [`RawBufferVec`] with the given [`BufferUsages`].51pub const fn new(buffer_usage: BufferUsages) -> Self {52Self {53values: Vec::new(),54buffer: None,55capacity: 0,56item_size: size_of::<T>(),57buffer_usage,58label: None,59changed: false,60}61}6263/// Returns a handle to the buffer, if the data has been uploaded.64#[inline]65pub fn buffer(&self) -> Option<&Buffer> {66self.buffer.as_ref()67}6869/// Returns the binding for the buffer if the data has been uploaded.70#[inline]71pub fn binding(&self) -> Option<BindingResource<'_>> {72Some(BindingResource::Buffer(73self.buffer()?.as_entire_buffer_binding(),74))75}7677/// Returns the amount of space that the GPU will use before reallocating.78#[inline]79pub fn capacity(&self) -> usize {80self.capacity81}8283/// Returns the number of items that have been pushed to this buffer.84#[inline]85pub fn len(&self) -> usize {86self.values.len()87}8889/// Returns true if the buffer is empty.90#[inline]91pub fn is_empty(&self) -> bool {92self.values.is_empty()93}9495/// Adds a new value and returns its index.96pub fn push(&mut self, value: T) -> usize {97let index = self.values.len();98self.values.push(value);99index100}101102pub fn append(&mut self, other: &mut RawBufferVec<T>) {103self.values.append(&mut other.values);104}105106/// Returns the value at the given index.107pub fn get(&self, index: u32) -> Option<&T> {108self.values.get(index as usize)109}110111/// Sets the value at the given index.112///113/// The index must be less than [`RawBufferVec::len`].114pub fn set(&mut self, index: u32, value: T) {115self.values[index as usize] = value;116}117118/// Preallocates space for `count` elements in the internal CPU-side buffer.119///120/// Unlike [`RawBufferVec::reserve`], this doesn't have any effect on the GPU buffer.121pub fn reserve_internal(&mut self, count: usize) {122self.values.reserve(count);123}124125/// Changes the debugging label of the buffer.126///127/// The next time the buffer is updated (via [`reserve`](Self::reserve)), Bevy will inform128/// the driver of the new label.129pub fn set_label(&mut self, label: Option<&str>) {130let label = label.map(str::to_string);131132if label != self.label {133self.changed = true;134}135136self.label = label;137}138139/// Returns the label140pub fn get_label(&self) -> Option<&str> {141self.label.as_deref()142}143144/// Creates a [`Buffer`] on the [`RenderDevice`] with size145/// at least `size_of::<T>() * capacity`, unless a such a buffer already exists.146///147/// If a [`Buffer`] exists, but is too small, references to it will be discarded,148/// and a new [`Buffer`] will be created. Any previously created [`Buffer`]s149/// that are no longer referenced will be deleted by the [`RenderDevice`]150/// once it is done using them (typically 1-2 frames).151///152/// In addition to any [`BufferUsages`] provided when153/// the `RawBufferVec` was created, the buffer on the [`RenderDevice`]154/// is marked as [`BufferUsages::COPY_DST`](BufferUsages).155pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {156let size = self.item_size * capacity;157if capacity > self.capacity || (self.changed && size > 0) {158self.capacity = capacity;159self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {160label: self.label.as_deref(),161size: size as BufferAddress,162usage: BufferUsages::COPY_DST | self.buffer_usage,163mapped_at_creation: false,164}));165self.changed = false;166}167}168169/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]170/// and the provided [`RenderQueue`].171///172/// Before queuing the write, a [`reserve`](RawBufferVec::reserve) operation173/// is executed.174pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {175if self.values.is_empty() {176return;177}178self.reserve(self.values.len(), device);179if let Some(buffer) = &self.buffer {180let range = 0..self.item_size * self.values.len();181let bytes: &[u8] = must_cast_slice(&self.values);182queue.write_buffer(buffer, 0, &bytes[range]);183}184}185186/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]187/// and the provided [`RenderQueue`].188///189/// If the buffer is not initialized on the GPU or the range is bigger than the capacity it will190/// return an error. You'll need to either reserve a new buffer which will lose data on the GPU191/// or create a new buffer and copy the old data to it.192///193/// This will only write the data contained in the given range. It is useful if you only want194/// to update a part of the buffer.195pub fn write_buffer_range(196&mut self,197render_queue: &RenderQueue,198range: core::ops::Range<usize>,199) -> Result<(), WriteBufferRangeError> {200if self.values.is_empty() {201return Err(WriteBufferRangeError::NoValuesToUpload);202}203if range.end > self.item_size * self.capacity {204return Err(WriteBufferRangeError::RangeBiggerThanBuffer);205}206if let Some(buffer) = &self.buffer {207// Cast only the bytes we need to write208let bytes: &[u8] = must_cast_slice(&self.values[range.start..range.end]);209render_queue.write_buffer(buffer, (range.start * self.item_size) as u64, bytes);210Ok(())211} else {212Err(WriteBufferRangeError::BufferNotInitialized)213}214}215216/// Reduces the length of the buffer.217pub fn truncate(&mut self, len: usize) {218self.values.truncate(len);219}220221/// Removes all elements from the buffer.222pub fn clear(&mut self) {223self.values.clear();224}225226/// Removes and returns the last element in the buffer.227pub fn pop(&mut self) -> Option<T> {228self.values.pop()229}230231pub fn values(&self) -> &Vec<T> {232&self.values233}234235pub fn values_mut(&mut self) -> &mut Vec<T> {236&mut self.values237}238}239240impl<T> RawBufferVec<T>241where242T: NoUninit + Default,243{244pub fn grow_set(&mut self, index: u32, value: T) {245while index as usize + 1 > self.len() {246self.values.push(T::default());247}248self.values[index as usize] = value;249}250}251252impl<T: NoUninit> Extend<T> for RawBufferVec<T> {253#[inline]254fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {255self.values.extend(iter);256}257}258259/// Like [`RawBufferVec`], but doesn't require that the data type `T` be260/// [`NoUninit`].261///262/// This is a high-performance data structure that you should use whenever263/// possible if your data is more complex than is suitable for [`RawBufferVec`].264/// The [`ShaderType`] trait from the `encase` library is used to ensure that265/// the data is correctly aligned for use by the GPU.266///267/// For performance reasons, unlike [`RawBufferVec`], this type doesn't allow268/// CPU access to the data after it's been added via [`BufferVec::push`]. If you269/// need CPU access to the data, consider another type, such as270/// [`StorageBuffer`][super::StorageBuffer].271///272/// Other options for storing GPU-accessible data are:273/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer)274/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)275/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)276/// * [`RawBufferVec`]277/// * [`StorageBuffer`](crate::render_resource::StorageBuffer)278/// * [`Texture`](crate::render_resource::Texture)279/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)280pub struct BufferVec<T>281where282T: ShaderType + WriteInto,283{284data: Vec<u8>,285buffer: Option<Buffer>,286capacity: usize,287buffer_usage: BufferUsages,288label: Option<String>,289label_changed: bool,290phantom: PhantomData<T>,291}292293impl<T> BufferVec<T>294where295T: ShaderType + WriteInto,296{297/// Creates a new [`BufferVec`] with the given [`BufferUsages`].298pub const fn new(buffer_usage: BufferUsages) -> Self {299Self {300data: vec![],301buffer: None,302capacity: 0,303buffer_usage,304label: None,305label_changed: false,306phantom: PhantomData,307}308}309310/// Returns a handle to the buffer, if the data has been uploaded.311#[inline]312pub fn buffer(&self) -> Option<&Buffer> {313self.buffer.as_ref()314}315316/// Returns the binding for the buffer if the data has been uploaded.317#[inline]318pub fn binding(&self) -> Option<BindingResource<'_>> {319Some(BindingResource::Buffer(320self.buffer()?.as_entire_buffer_binding(),321))322}323324/// Returns the amount of space that the GPU will use before reallocating.325#[inline]326pub fn capacity(&self) -> usize {327self.capacity328}329330/// Returns the number of items that have been pushed to this buffer.331#[inline]332pub fn len(&self) -> usize {333self.data.len() / u64::from(T::min_size()) as usize334}335336/// Returns true if the buffer is empty.337#[inline]338pub fn is_empty(&self) -> bool {339self.data.is_empty()340}341342/// Adds a new value and returns its index.343pub fn push(&mut self, value: T) -> usize {344let element_size = u64::from(T::min_size()) as usize;345let offset = self.data.len();346347// TODO: Consider using unsafe code to push uninitialized, to prevent348// the zeroing. It shows up in profiles.349self.data.extend(iter::repeat_n(0, element_size));350351// Take a slice of the new data for `write_into` to use. This is352// important: it hoists the bounds check up here so that the compiler353// can eliminate all the bounds checks that `write_into` will emit.354let mut dest = &mut self.data[offset..(offset + element_size)];355value.write_into(&mut Writer::new(&value, &mut dest, 0).unwrap());356357offset / u64::from(T::min_size()) as usize358}359360/// Changes the debugging label of the buffer.361///362/// The next time the buffer is updated (via [`Self::reserve`]), Bevy will inform363/// the driver of the new label.364pub fn set_label(&mut self, label: Option<&str>) {365let label = label.map(str::to_string);366367if label != self.label {368self.label_changed = true;369}370371self.label = label;372}373374/// Returns the label375pub fn get_label(&self) -> Option<&str> {376self.label.as_deref()377}378379/// Creates a [`Buffer`] on the [`RenderDevice`] with size380/// at least `size_of::<T>() * capacity`, unless such a buffer already exists.381///382/// If a [`Buffer`] exists, but is too small, references to it will be discarded,383/// and a new [`Buffer`] will be created. Any previously created [`Buffer`]s384/// that are no longer referenced will be deleted by the [`RenderDevice`]385/// once it is done using them (typically 1-2 frames).386///387/// In addition to any [`BufferUsages`] provided when388/// the `BufferVec` was created, the buffer on the [`RenderDevice`]389/// is marked as [`BufferUsages::COPY_DST`](BufferUsages).390pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {391if capacity <= self.capacity && !self.label_changed {392return;393}394395self.capacity = capacity;396let size = u64::from(T::min_size()) as usize * capacity;397self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {398label: self.label.as_deref(),399size: size as BufferAddress,400usage: BufferUsages::COPY_DST | self.buffer_usage,401mapped_at_creation: false,402}));403self.label_changed = false;404}405406/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]407/// and the provided [`RenderQueue`].408///409/// Before queuing the write, a [`reserve`](BufferVec::reserve) operation is410/// executed.411pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {412if self.data.is_empty() {413return;414}415416self.reserve(self.data.len() / u64::from(T::min_size()) as usize, device);417418let Some(buffer) = &self.buffer else { return };419queue.write_buffer(buffer, 0, &self.data);420}421422/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]423/// and the provided [`RenderQueue`].424///425/// If the buffer is not initialized on the GPU or the range is bigger than the capacity it will426/// return an error. You'll need to either reserve a new buffer which will lose data on the GPU427/// or create a new buffer and copy the old data to it.428///429/// This will only write the data contained in the given range. It is useful if you only want430/// to update a part of the buffer.431pub fn write_buffer_range(432&mut self,433render_queue: &RenderQueue,434range: core::ops::Range<usize>,435) -> Result<(), WriteBufferRangeError> {436if self.data.is_empty() {437return Err(WriteBufferRangeError::NoValuesToUpload);438}439let item_size = u64::from(T::min_size()) as usize;440if range.end > item_size * self.capacity {441return Err(WriteBufferRangeError::RangeBiggerThanBuffer);442}443if let Some(buffer) = &self.buffer {444let bytes = &self.data[range.start..range.end];445render_queue.write_buffer(buffer, (range.start * item_size) as u64, bytes);446Ok(())447} else {448Err(WriteBufferRangeError::BufferNotInitialized)449}450}451452/// Reduces the length of the buffer.453pub fn truncate(&mut self, len: usize) {454self.data.truncate(u64::from(T::min_size()) as usize * len);455}456457/// Removes all elements from the buffer.458pub fn clear(&mut self) {459self.data.clear();460}461}462463/// Like a [`BufferVec`], but only reserves space on the GPU for elements464/// instead of initializing them CPU-side.465///466/// This type is useful when you're accumulating "output slots" for a GPU467/// compute shader to write into.468///469/// The type `T` need not be [`NoUninit`], unlike [`RawBufferVec`]; it only has to470/// be [`GpuArrayBufferable`].471pub struct UninitBufferVec<T>472where473T: GpuArrayBufferable,474{475buffer: Option<Buffer>,476len: usize,477capacity: usize,478item_size: usize,479buffer_usage: BufferUsages,480label: Option<String>,481label_changed: bool,482phantom: PhantomData<T>,483}484485impl<T> UninitBufferVec<T>486where487T: GpuArrayBufferable,488{489/// Creates a new [`UninitBufferVec`] with the given [`BufferUsages`].490pub const fn new(buffer_usage: BufferUsages) -> Self {491Self {492len: 0,493buffer: None,494capacity: 0,495item_size: size_of::<T>(),496buffer_usage,497label: None,498label_changed: false,499phantom: PhantomData,500}501}502503/// Returns the buffer, if allocated.504#[inline]505pub fn buffer(&self) -> Option<&Buffer> {506self.buffer.as_ref()507}508509/// Returns the binding for the buffer if the data has been uploaded.510#[inline]511pub fn binding(&self) -> Option<BindingResource<'_>> {512Some(BindingResource::Buffer(513self.buffer()?.as_entire_buffer_binding(),514))515}516517/// Reserves space for one more element in the buffer and returns its index.518pub fn add(&mut self) -> usize {519self.add_multiple(1)520}521522/// Reserves space for the given number of elements in the buffer and523/// returns the index of the first one.524pub fn add_multiple(&mut self, count: usize) -> usize {525let index = self.len;526self.len += count;527index528}529530/// Returns true if no elements have been added to this [`UninitBufferVec`].531pub fn is_empty(&self) -> bool {532self.len == 0533}534535/// Removes all elements from the buffer.536pub fn clear(&mut self) {537self.len = 0;538}539540/// Returns the length of the buffer.541pub fn len(&self) -> usize {542self.len543}544545/// Materializes the buffer on the GPU with space for `capacity` elements.546///547/// If the buffer is already big enough, this function doesn't reallocate548/// the buffer.549pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {550if capacity <= self.capacity && !self.label_changed {551return;552}553554self.capacity = capacity;555let size = self.item_size * capacity;556self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {557label: self.label.as_deref(),558size: size as BufferAddress,559usage: BufferUsages::COPY_DST | self.buffer_usage,560mapped_at_creation: false,561}));562563self.label_changed = false;564}565566/// Materializes the buffer on the GPU, with an appropriate size for the567/// elements that have been pushed so far.568pub fn write_buffer(&mut self, device: &RenderDevice) {569if !self.is_empty() {570self.reserve(self.len, device);571}572}573}574575/// Error returned when `write_buffer_range` fails576///577/// See [`RawBufferVec::write_buffer_range`] [`BufferVec::write_buffer_range`]578#[derive(Debug, Eq, PartialEq, Copy, Clone, Error)]579pub enum WriteBufferRangeError {580#[error("the range is bigger than the capacity of the buffer")]581RangeBiggerThanBuffer,582#[error("the gpu buffer is not initialized")]583BufferNotInitialized,584#[error("there are no values to upload")]585NoValuesToUpload,586}587588589