Path: blob/main/crates/bevy_render/src/render_resource/storage_buffer.rs
6596 views
use core::marker::PhantomData;12use super::Buffer;3use crate::renderer::{RenderDevice, RenderQueue};4use encase::{5internal::WriteInto, DynamicStorageBuffer as DynamicStorageBufferWrapper, ShaderType,6StorageBuffer as StorageBufferWrapper,7};8use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferSize, BufferUsages};910use super::IntoBinding;1112/// Stores data to be transferred to the GPU and made accessible to shaders as a storage buffer.13///14/// Storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts of data.15/// Note however that WebGL2 does not support storage buffers, so consider alternative options in this case.16///17/// Storage buffers can store runtime-sized arrays, but only if they are the last field in a structure.18///19/// The contained data is stored in system RAM. [`write_buffer`](StorageBuffer::write_buffer) queues20/// copying of the data from system RAM to VRAM. Storage buffers must conform to [std430 alignment/padding requirements], which21/// is automatically enforced by this structure.22///23/// Other options for storing GPU-accessible data are:24/// * [`BufferVec`](crate::render_resource::BufferVec)25/// * [`DynamicStorageBuffer`]26/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)27/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)28/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)29/// * [`Texture`](crate::render_resource::Texture)30/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)31///32/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage33pub struct StorageBuffer<T: ShaderType> {34value: T,35scratch: StorageBufferWrapper<Vec<u8>>,36buffer: Option<Buffer>,37label: Option<String>,38changed: bool,39buffer_usage: BufferUsages,40last_written_size: Option<BufferSize>,41}4243impl<T: ShaderType> From<T> for StorageBuffer<T> {44fn from(value: T) -> Self {45Self {46value,47scratch: StorageBufferWrapper::new(Vec::new()),48buffer: None,49label: None,50changed: false,51buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,52last_written_size: None,53}54}55}5657impl<T: ShaderType + Default> Default for StorageBuffer<T> {58fn default() -> Self {59Self {60value: T::default(),61scratch: StorageBufferWrapper::new(Vec::new()),62buffer: None,63label: None,64changed: false,65buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,66last_written_size: None,67}68}69}7071impl<T: ShaderType + WriteInto> StorageBuffer<T> {72#[inline]73pub fn buffer(&self) -> Option<&Buffer> {74self.buffer.as_ref()75}7677#[inline]78pub fn binding(&self) -> Option<BindingResource<'_>> {79Some(BindingResource::Buffer(BufferBinding {80buffer: self.buffer()?,81offset: 0,82size: self.last_written_size,83}))84}8586pub fn set(&mut self, value: T) {87self.value = value;88}8990pub fn get(&self) -> &T {91&self.value92}9394pub fn get_mut(&mut self) -> &mut T {95&mut self.value96}9798pub fn set_label(&mut self, label: Option<&str>) {99let label = label.map(str::to_string);100101if label != self.label {102self.changed = true;103}104105self.label = label;106}107108pub fn get_label(&self) -> Option<&str> {109self.label.as_deref()110}111112/// Add more [`BufferUsages`] to the buffer.113///114/// This method only allows addition of flags to the default usage flags.115///116/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::STORAGE`.117pub fn add_usages(&mut self, usage: BufferUsages) {118self.buffer_usage |= usage;119self.changed = true;120}121122/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]123/// and the provided [`RenderQueue`].124///125/// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously126/// allocated does not have enough capacity, a new GPU-side buffer is created.127pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {128self.scratch.write(&self.value).unwrap();129130let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);131let size = self.scratch.as_ref().len() as u64;132133if capacity < size || self.changed {134self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {135label: self.label.as_deref(),136usage: self.buffer_usage,137contents: self.scratch.as_ref(),138}));139self.changed = false;140} else if let Some(buffer) = &self.buffer {141queue.write_buffer(buffer, 0, self.scratch.as_ref());142}143144self.last_written_size = BufferSize::new(size);145}146}147148impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a StorageBuffer<T> {149#[inline]150fn into_binding(self) -> BindingResource<'a> {151self.binding().expect("Failed to get buffer")152}153}154155/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic storage buffer.156///157/// This is just a [`StorageBuffer`], but also allows you to set dynamic offsets.158///159/// Dynamic storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts160/// of data. Note however that WebGL2 does not support storage buffers, so consider alternative options in this case. Dynamic161/// storage buffers support multiple separate bindings at dynamic byte offsets and so have a162/// [`push`](DynamicStorageBuffer::push) method.163///164/// The contained data is stored in system RAM. [`write_buffer`](DynamicStorageBuffer::write_buffer)165/// queues copying of the data from system RAM to VRAM. The data within a storage buffer binding must conform to166/// [std430 alignment/padding requirements]. `DynamicStorageBuffer` takes care of serializing the inner type to conform to167/// these requirements. Each item [`push`](DynamicStorageBuffer::push)ed into this structure168/// will additionally be aligned to meet dynamic offset alignment requirements.169///170/// Other options for storing GPU-accessible data are:171/// * [`BufferVec`](crate::render_resource::BufferVec)172/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)173/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)174/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)175/// * [`StorageBuffer`]176/// * [`Texture`](crate::render_resource::Texture)177/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)178///179/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage180pub struct DynamicStorageBuffer<T: ShaderType> {181scratch: DynamicStorageBufferWrapper<Vec<u8>>,182buffer: Option<Buffer>,183label: Option<String>,184changed: bool,185buffer_usage: BufferUsages,186last_written_size: Option<BufferSize>,187_marker: PhantomData<fn() -> T>,188}189190impl<T: ShaderType> Default for DynamicStorageBuffer<T> {191fn default() -> Self {192Self {193scratch: DynamicStorageBufferWrapper::new(Vec::new()),194buffer: None,195label: None,196changed: false,197buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,198last_written_size: None,199_marker: PhantomData,200}201}202}203204impl<T: ShaderType + WriteInto> DynamicStorageBuffer<T> {205#[inline]206pub fn buffer(&self) -> Option<&Buffer> {207self.buffer.as_ref()208}209210#[inline]211pub fn binding(&self) -> Option<BindingResource<'_>> {212Some(BindingResource::Buffer(BufferBinding {213buffer: self.buffer()?,214offset: 0,215size: self.last_written_size,216}))217}218219#[inline]220pub fn is_empty(&self) -> bool {221self.scratch.as_ref().is_empty()222}223224#[inline]225pub fn push(&mut self, value: T) -> u32 {226self.scratch.write(&value).unwrap() as u32227}228229pub fn set_label(&mut self, label: Option<&str>) {230let label = label.map(str::to_string);231232if label != self.label {233self.changed = true;234}235236self.label = label;237}238239pub fn get_label(&self) -> Option<&str> {240self.label.as_deref()241}242243/// Add more [`BufferUsages`] to the buffer.244///245/// This method only allows addition of flags to the default usage flags.246///247/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::STORAGE`.248pub fn add_usages(&mut self, usage: BufferUsages) {249self.buffer_usage |= usage;250self.changed = true;251}252253#[inline]254pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {255let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);256let size = self.scratch.as_ref().len() as u64;257258if capacity < size || (self.changed && size > 0) {259self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {260label: self.label.as_deref(),261usage: self.buffer_usage,262contents: self.scratch.as_ref(),263}));264self.changed = false;265} else if let Some(buffer) = &self.buffer {266queue.write_buffer(buffer, 0, self.scratch.as_ref());267}268269self.last_written_size = BufferSize::new(size);270}271272#[inline]273pub fn clear(&mut self) {274self.scratch.as_mut().clear();275self.scratch.set_offset(0);276}277}278279impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicStorageBuffer<T> {280#[inline]281fn into_binding(self) -> BindingResource<'a> {282self.binding().expect("Failed to get buffer")283}284}285286287