From 312adb75636c95a8b573104794ac452e2ef3666e Mon Sep 17 00:00:00 2001 From: Brian Merchant Date: Wed, 20 Jul 2022 17:24:34 +0000 Subject: [PATCH] Documenting `UniformBuffer`, `DynamicUniformBuffer`, `StorageBuffer` and `DynamicStorageBuffer`. (#5223) # Objective Documents the `UniformBuffer`, `DynamicUniformBuffer`, `StorageBuffer` and `DynamicStorageBuffer` render resources. ## Solution I looked through Discord discussion on these structures, and found [a comment](https://discord.com/channels/691052431525675048/953222550568173580/956596218857918464) to be particularly helpful, in the general discussion around encase. Other resources I have used are documented here: https://discord.com/channels/691052431525675048/968333504838524958/991195474029715520 Co-authored-by: Brian Merchant --- .../src/render_resource/buffer_vec.rs | 22 ++++---- .../src/render_resource/storage_buffer.rs | 47 +++++++++++++++++ .../src/render_resource/uniform_buffer.rs | 50 +++++++++++++++++++ 3 files changed, 107 insertions(+), 12 deletions(-) diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 80b88c7ac8197a..7ae137001ede45 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -10,25 +10,23 @@ use wgpu::BufferUsages; /// for use by the GPU. /// /// "Properly formatted" means that item data already meets the alignment and padding -/// requirements for how it will be used on the GPU. +/// requirements for how it will be used on the GPU. The item type must implement [`Pod`] +/// for its data representation to be directly copyable. /// /// Index, vertex, and instance-rate vertex buffers have no alignment nor padding requirements and -/// so this helper type is a good choice for them. Uniform buffers must adhere to std140 -/// alignment/padding requirements, and storage buffers to std430. There are helper types for such -/// buffers: -/// - Uniform buffers -/// - Plain: [`UniformBuffer`](crate::render_resource::UniformBuffer) -/// - Dynamic offsets: [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// - Storage buffers -/// - Plain: [`StorageBuffer`](crate::render_resource::StorageBuffer) -/// - Dynamic offsets: [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) -/// -/// The item type must implement [`Pod`] for its data representation to be directly copyable. +/// so this helper type is a good choice for them. /// /// The contained data is stored in system RAM. Calling [`reserve`](crate::render_resource::BufferVec::reserve) /// allocates VRAM from the [`RenderDevice`](crate::renderer::RenderDevice). /// [`write_buffer`](crate::render_resource::BufferVec::write_buffer) queues copying of the data /// from system RAM to VRAM. +/// +/// Other options for storing GPU-accessible data are: +/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) +/// * [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`BufferVec`](crate::render_resource::BufferVec) +/// * [`Texture`](crate::render_resource::Texture) pub struct BufferVec { values: Vec, buffer: Option, diff --git a/crates/bevy_render/src/render_resource/storage_buffer.rs b/crates/bevy_render/src/render_resource/storage_buffer.rs index 96c24a2c090eb3..4579cd2d06a435 100644 --- a/crates/bevy_render/src/render_resource/storage_buffer.rs +++ b/crates/bevy_render/src/render_resource/storage_buffer.rs @@ -1,3 +1,5 @@ +#![allow(clippy::doc_markdown)] + use super::Buffer; use crate::renderer::{RenderDevice, RenderQueue}; use encase::{ @@ -6,6 +8,25 @@ use encase::{ }; use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsages}; +/// Stores data to be transferred to the GPU and made accessible to shaders as a storage buffer. +/// +/// Storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts of data. +/// Note however that WebGL2 does not support storage buffers, so consider alternative options in this case. +/// +/// Storage buffers can store runtime-sized arrays, but only if they are the last field in a structure. +/// +/// The contained data is stored in system RAM. [`write_buffer`](crate::render_resource::StorageBuffer::write_buffer) queues +/// copying of the data from system RAM to VRAM. Storage buffers must conform to [std430 alignment/padding requirements], which +/// is automatically enforced by this structure. +/// +/// Other options for storing GPU-accessible data are: +/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) +/// * [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`BufferVec`](crate::render_resource::BufferVec) +/// * [`Texture`](crate::render_resource::Texture) +/// +/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage pub struct StorageBuffer { value: T, scratch: StorageBufferWrapper>, @@ -60,6 +81,11 @@ impl StorageBuffer { &mut self.value } + /// Queues writing of data from system RAM to VRAM using the [`RenderDevice`](crate::renderer::RenderDevice) + /// and the provided [`RenderQueue`](crate::renderer::RenderQueue). + /// + /// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously + /// allocated does not have enough capacity, a new GPU-side buffer is created. pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { self.scratch.write(&self.value).unwrap(); @@ -78,6 +104,27 @@ impl StorageBuffer { } } +/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic storage buffer. +/// +/// Dynamic storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts +/// of data. Note however that WebGL2 does not support storage buffers, so consider alternative options in this case. Dynamic +/// storage buffers support multiple separate bindings at dynamic byte offsets and so have a +/// [`push`](crate::render_resource::DynamicStorageBuffer::push) method. +/// +/// The contained data is stored in system RAM. [`write_buffer`](crate::render_resource::DynamicStorageBuffer::write_buffer) +/// queues copying of the data from system RAM to VRAM. The data within a storage buffer binding must conform to +/// [std430 alignment/padding requirements]. `DynamicStorageBuffer` takes care of serialising the inner type to conform to +/// these requirements. Each item [`push`](crate::render_resource::DynamicStorageBuffer::push)ed into this structure +/// will additionally be aligned to meet dynamic offset alignment requirements. +/// +/// Other options for storing GPU-accessible data are: +/// * [`StorageBuffer`](crate::render_resource::StorageBuffer) +/// * [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`BufferVec`](crate::render_resource::BufferVec) +/// * [`Texture`](crate::render_resource::Texture) +/// +/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage pub struct DynamicStorageBuffer { values: Vec, scratch: DynamicStorageBufferWrapper>, diff --git a/crates/bevy_render/src/render_resource/uniform_buffer.rs b/crates/bevy_render/src/render_resource/uniform_buffer.rs index 6f9ad642dcc489..c56294a2225a9f 100644 --- a/crates/bevy_render/src/render_resource/uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/uniform_buffer.rs @@ -8,6 +8,25 @@ use encase::{ }; use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsages}; +/// Stores data to be transferred to the GPU and made accessible to shaders as a uniform buffer. +/// +/// Uniform buffers are available to shaders on a read-only basis. Uniform buffers are commonly used to make available to shaders +/// parameters that are constant during shader execution, and are best used for data that is relatively small in size as they are +/// only guaranteed to support up to 16kB per binding. +/// +/// The contained data is stored in system RAM. [`write_buffer`](crate::render_resource::UniformBuffer::write_buffer) queues +/// copying of the data from system RAM to VRAM. Data in uniform buffers must follow [std140 alignment/padding requirements], +/// which is automatically enforced by this structure. Per the WGPU spec, uniform buffers cannot store runtime-sized array +/// (vectors), or structures with fields that are vectors. +/// +/// Other options for storing GPU-accessible data are: +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`StorageBuffer`](crate::render_resource::StorageBuffer) +/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) +/// * [`BufferVec`](crate::render_resource::BufferVec) +/// * [`Texture`](crate::render_resource::Texture) +/// +/// [std140 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-uniform pub struct UniformBuffer { value: T, scratch: UniformBufferWrapper>, @@ -47,6 +66,7 @@ impl UniformBuffer { )) } + /// Set the data the buffer stores. pub fn set(&mut self, value: T) { self.value = value; } @@ -59,6 +79,11 @@ impl UniformBuffer { &mut self.value } + /// Queues writing of data from system RAM to VRAM using the [`RenderDevice`](crate::renderer::RenderDevice) + /// and the provided [`RenderQueue`](crate::renderer::RenderQueue), if a GPU-side backing buffer already exists. + /// + /// If a GPU-side buffer does not already exist for this data, such a buffer is initialized with currently + /// available data. pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { self.scratch.write(&self.value).unwrap(); @@ -75,6 +100,25 @@ impl UniformBuffer { } } +/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic uniform buffer. +/// +/// Dynamic uniform buffers are available to shaders on a read-only basis. Dynamic uniform buffers are commonly used to make +/// available to shaders runtime-sized arrays of parameters that are otherwise constant during shader execution, and are best +/// suited to data that is relatively small in size as they are only guaranteed to support up to 16kB per binding. +/// +/// The contained data is stored in system RAM. [`write_buffer`](crate::render_resource::DynamicUniformBuffer::write_buffer) queues +/// copying of the data from system RAM to VRAM. Data in uniform buffers must follow [std140 alignment/padding requirements], +/// which is automatically enforced by this structure. Per the WGPU spec, uniform buffers cannot store runtime-sized array +/// (vectors), or structures with fields that are vectors. +/// +/// Other options for storing GPU-accessible data are: +/// * [`StorageBuffer`](crate::render_resource::StorageBuffer) +/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) +/// * [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`Texture`](crate::render_resource::Texture) +/// +/// [std140 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-uniform pub struct DynamicUniformBuffer { values: Vec, scratch: DynamicUniformBufferWrapper>, @@ -118,6 +162,7 @@ impl DynamicUniformBuffer { self.values.is_empty() } + /// Push data into the `DynamicUniformBuffer`'s internal vector (residing on system RAM). #[inline] pub fn push(&mut self, value: T) -> u32 { let offset = self.scratch.write(&value).unwrap() as u32; @@ -125,6 +170,11 @@ impl DynamicUniformBuffer { offset } + /// Queues writing of data from system RAM to VRAM using the [`RenderDevice`](crate::renderer::RenderDevice) + /// and the provided [`RenderQueue`](crate::renderer::RenderQueue). + /// + /// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously + /// allocated does not have enough capacity, a new GPU-side buffer is created. #[inline] pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { let size = self.scratch.as_ref().len();