Path: blob/main/crates/bevy_render/src/render_resource/storage_buffer.rs
9331 views
use core::marker::PhantomData;12use super::Buffer;3use crate::{4render_resource::make_buffer_label,5renderer::{RenderDevice, RenderQueue},6};7use encase::{8internal::WriteInto, DynamicStorageBuffer as DynamicStorageBufferWrapper, ShaderType,9StorageBuffer as StorageBufferWrapper,10};11use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferSize, BufferUsages};1213use super::IntoBinding;1415/// Stores data to be transferred to the GPU and made accessible to shaders as a storage buffer.16///17/// Storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts of data.18/// Note however that WebGL2 does not support storage buffers, so consider alternative options in this case.19///20/// Storage buffers can store runtime-sized arrays, but only if they are the last field in a structure.21///22/// The contained data is stored in system RAM. [`write_buffer`](StorageBuffer::write_buffer) queues23/// copying of the data from system RAM to VRAM. Storage buffers must conform to [std430 alignment/padding requirements], which24/// is automatically enforced by this structure.25///26/// Other options for storing GPU-accessible data are:27/// * [`BufferVec`](crate::render_resource::BufferVec)28/// * [`DynamicStorageBuffer`]29/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)30/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)31/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)32/// * [`Texture`](crate::render_resource::Texture)33/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)34///35/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage36pub struct StorageBuffer<T: ShaderType> {37value: T,38scratch: StorageBufferWrapper<Vec<u8>>,39buffer: Option<Buffer>,40label: Option<String>,41changed: bool,42buffer_usage: BufferUsages,43last_written_size: Option<BufferSize>,44}4546impl<T: ShaderType> From<T> for StorageBuffer<T> {47fn from(value: T) -> Self {48Self {49value,50scratch: StorageBufferWrapper::new(Vec::new()),51buffer: None,52label: None,53changed: false,54buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,55last_written_size: None,56}57}58}5960impl<T: ShaderType + Default> Default for StorageBuffer<T> {61fn default() -> Self {62Self {63value: T::default(),64scratch: StorageBufferWrapper::new(Vec::new()),65buffer: None,66label: None,67changed: false,68buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,69last_written_size: None,70}71}72}7374impl<T: ShaderType + WriteInto> StorageBuffer<T> {75#[inline]76pub fn buffer(&self) -> Option<&Buffer> {77self.buffer.as_ref()78}7980#[inline]81pub fn binding(&self) -> Option<BindingResource<'_>> {82Some(BindingResource::Buffer(BufferBinding {83buffer: self.buffer()?,84offset: 0,85size: self.last_written_size,86}))87}8889pub fn set(&mut self, value: T) {90self.value = value;91}9293pub fn get(&self) -> &T {94&self.value95}9697pub fn get_mut(&mut self) -> &mut T {98&mut self.value99}100101pub fn set_label(&mut self, label: Option<&str>) {102let label = label.map(str::to_string);103104if label != self.label {105self.changed = true;106}107108self.label = label;109}110111pub fn get_label(&self) -> Option<&str> {112self.label.as_deref()113}114115/// Add more [`BufferUsages`] to the buffer.116///117/// This method only allows addition of flags to the default usage flags.118///119/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::STORAGE`.120pub fn add_usages(&mut self, usage: BufferUsages) {121self.buffer_usage |= usage;122self.changed = true;123}124125/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]126/// and the provided [`RenderQueue`].127///128/// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously129/// allocated does not have enough capacity, a new GPU-side buffer is created.130pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {131self.scratch.write(&self.value).unwrap();132133let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);134let size = self.scratch.as_ref().len() as u64;135136if capacity < size || self.changed {137self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {138label: make_buffer_label::<Self>(&self.label),139usage: self.buffer_usage,140contents: self.scratch.as_ref(),141}));142self.changed = false;143} else if let Some(buffer) = &self.buffer {144queue.write_buffer(buffer, 0, self.scratch.as_ref());145}146147self.last_written_size = BufferSize::new(size);148}149}150151impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a StorageBuffer<T> {152#[inline]153fn into_binding(self) -> BindingResource<'a> {154self.binding().expect("Failed to get buffer")155}156}157158/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic storage buffer.159///160/// This is just a [`StorageBuffer`], but also allows you to set dynamic offsets.161///162/// Dynamic storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts163/// of data. Note however that WebGL2 does not support storage buffers, so consider alternative options in this case. Dynamic164/// storage buffers support multiple separate bindings at dynamic byte offsets and so have a165/// [`push`](DynamicStorageBuffer::push) method.166///167/// The contained data is stored in system RAM. [`write_buffer`](DynamicStorageBuffer::write_buffer)168/// queues copying of the data from system RAM to VRAM. The data within a storage buffer binding must conform to169/// [std430 alignment/padding requirements]. `DynamicStorageBuffer` takes care of serializing the inner type to conform to170/// these requirements. Each item [`push`](DynamicStorageBuffer::push)ed into this structure171/// will additionally be aligned to meet dynamic offset alignment requirements.172///173/// Other options for storing GPU-accessible data are:174/// * [`BufferVec`](crate::render_resource::BufferVec)175/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)176/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)177/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)178/// * [`StorageBuffer`]179/// * [`Texture`](crate::render_resource::Texture)180/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)181///182/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage183pub struct DynamicStorageBuffer<T: ShaderType> {184scratch: DynamicStorageBufferWrapper<Vec<u8>>,185buffer: Option<Buffer>,186label: Option<String>,187changed: bool,188buffer_usage: BufferUsages,189last_written_size: Option<BufferSize>,190_marker: PhantomData<fn() -> T>,191}192193impl<T: ShaderType> Default for DynamicStorageBuffer<T> {194fn default() -> Self {195Self {196scratch: DynamicStorageBufferWrapper::new(Vec::new()),197buffer: None,198label: None,199changed: false,200buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,201last_written_size: None,202_marker: PhantomData,203}204}205}206207impl<T: ShaderType + WriteInto> DynamicStorageBuffer<T> {208#[inline]209pub fn buffer(&self) -> Option<&Buffer> {210self.buffer.as_ref()211}212213#[inline]214pub fn binding(&self) -> Option<BindingResource<'_>> {215Some(BindingResource::Buffer(BufferBinding {216buffer: self.buffer()?,217offset: 0,218size: self.last_written_size,219}))220}221222#[inline]223pub fn is_empty(&self) -> bool {224self.scratch.as_ref().is_empty()225}226227#[inline]228pub fn push(&mut self, value: T) -> u32 {229self.scratch.write(&value).unwrap() as u32230}231232pub fn set_label(&mut self, label: Option<&str>) {233let label = label.map(str::to_string);234235if label != self.label {236self.changed = true;237}238239self.label = label;240}241242pub fn get_label(&self) -> Option<&str> {243self.label.as_deref()244}245246/// Add more [`BufferUsages`] to the buffer.247///248/// This method only allows addition of flags to the default usage flags.249///250/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::STORAGE`.251pub fn add_usages(&mut self, usage: BufferUsages) {252self.buffer_usage |= usage;253self.changed = true;254}255256#[inline]257pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {258let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);259let size = self.scratch.as_ref().len() as u64;260261if capacity < size || (self.changed && size > 0) {262self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {263label: make_buffer_label::<Self>(&self.label),264usage: self.buffer_usage,265contents: self.scratch.as_ref(),266}));267self.changed = false;268} else if let Some(buffer) = &self.buffer {269queue.write_buffer(buffer, 0, self.scratch.as_ref());270}271272self.last_written_size = BufferSize::new(size);273}274275#[inline]276pub fn clear(&mut self) {277self.scratch.as_mut().clear();278self.scratch.set_offset(0);279}280}281282impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicStorageBuffer<T> {283#[inline]284fn into_binding(self) -> BindingResource<'a> {285self.binding().expect("Failed to get buffer")286}287}288289290