From 40e9985ad5e41a585c57f30dd86b657f35497e69 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Fri, 24 Mar 2023 21:49:51 -0400 Subject: [PATCH 01/24] Add GpuList and BatchedUniformBuffer --- crates/bevy_render/src/gpu_component_list.rs | 55 ++++++++ crates/bevy_render/src/lib.rs | 1 + .../render_resource/batched_uniform_buffer.rs | 125 +++++++++++++++++ .../src/render_resource/buffer_vec.rs | 2 + .../src/render_resource/gpu_list.rs | 127 ++++++++++++++++++ crates/bevy_render/src/render_resource/mod.rs | 3 + .../src/render_resource/storage_buffer.rs | 4 + .../src/render_resource/uniform_buffer.rs | 18 ++- 8 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_render/src/gpu_component_list.rs create mode 100644 crates/bevy_render/src/render_resource/batched_uniform_buffer.rs create mode 100644 crates/bevy_render/src/render_resource/gpu_list.rs diff --git a/crates/bevy_render/src/gpu_component_list.rs b/crates/bevy_render/src/gpu_component_list.rs new file mode 100644 index 0000000000000..efcb49fe8bf6b --- /dev/null +++ b/crates/bevy_render/src/gpu_component_list.rs @@ -0,0 +1,55 @@ +use crate::{ + render_resource::{GpuList, GpuListable}, + renderer::{RenderDevice, RenderQueue}, + Render, RenderApp, RenderSet, +}; +use bevy_app::{App, Plugin}; +use bevy_ecs::{ + prelude::{Component, Entity}, + schedule::IntoSystemConfigs, + system::{Commands, Query, Res, ResMut}, +}; +use std::marker::PhantomData; + +/// This plugin prepares the components of the corresponding type for the GPU +/// by storing them in a [`GpuList`]. +pub struct GpuComponentListPlugin(PhantomData); + +impl Plugin for GpuComponentListPlugin { + fn build(&self, app: &mut App) { + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .insert_resource(GpuList::::new( + render_app.world.resource::(), + )) + .add_systems( + Render, + prepare_gpu_component_lists::.in_set(RenderSet::Prepare), + ); + } + } +} + +impl Default for GpuComponentListPlugin { + fn default() -> Self { + Self(PhantomData::) + } +} + +fn prepare_gpu_component_lists( + mut commands: Commands, + render_device: Res, + render_queue: Res, + mut gpu_list: ResMut>, + components: Query<(Entity, &C)>, +) { + gpu_list.clear(); + + let entities = components + .iter() + .map(|(entity, component)| (entity, gpu_list.push(component.clone()))) + .collect::>(); + commands.insert_or_spawn_batch(entities); + + gpu_list.write_buffer(&render_device, &render_queue); +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7101ba435f314..4fb3be0459a54 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -9,6 +9,7 @@ pub mod extract_component; mod extract_param; pub mod extract_resource; pub mod globals; +pub mod gpu_component_list; pub mod mesh; pub mod pipelined_rendering; pub mod primitives; diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs new file mode 100644 index 0000000000000..034da33d087d0 --- /dev/null +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -0,0 +1,125 @@ +use super::{GpuListIndex, GpuListable}; +use crate::{ + render_resource::DynamicUniformBuffer, + renderer::{RenderDevice, RenderQueue}, +}; +use encase::{ + private::{ArrayMetadata, BufferMut, Metadata, RuntimeSizedArray, WriteInto, Writer}, + ShaderType, +}; +use std::{marker::PhantomData, num::NonZeroU64}; +use wgpu::{BindingResource, Limits}; + +// 1MB else we will make really large arrays on macOS which reports very large +// `max_uniform_buffer_binding_size`. On macOS this ends up being the minimum +// size of the uniform buffer as well as the size of each chunk of data at a +// dynamic offset. +const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; + +/// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size) +/// are grouped into a batch as an `array` in WGSL. +pub struct BatchedUniformBuffer { + uniforms: DynamicUniformBuffer>>, + temp: MaxCapacityArray>, + current_offset: u32, + dynamic_offset_alignment: u32, +} + +impl BatchedUniformBuffer { + pub fn batch_size(limits: &Limits) -> usize { + (limits + .max_uniform_buffer_binding_size + .min(MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE) as u64 + / T::min_size().get()) as usize + } + + pub fn new(limits: &Limits) -> Self { + let capacity = Self::batch_size(limits); + let alignment = limits.min_uniform_buffer_offset_alignment; + + Self { + uniforms: DynamicUniformBuffer::new_with_alignment(alignment as u64), + temp: MaxCapacityArray(Vec::with_capacity(capacity), capacity), + current_offset: 0, + dynamic_offset_alignment: alignment, + } + } + + #[inline] + pub fn size(&self) -> NonZeroU64 { + self.temp.size() + } + + pub fn clear(&mut self) { + self.uniforms.clear(); + self.current_offset = 0; + self.temp.0.clear(); + } + + pub fn push(&mut self, component: T) -> GpuListIndex { + let result = GpuListIndex { + index: self.temp.0.len() as u32, + dynamic_offset: Some(self.current_offset), + element_type: PhantomData, + }; + self.temp.0.push(component); + if self.temp.0.len() == self.temp.1 { + self.flush(); + } + result + } + + pub fn flush(&mut self) { + self.uniforms.push(self.temp.clone()); + + self.current_offset += + round_up(self.temp.size().get(), self.dynamic_offset_alignment as u64) as u32; + + self.temp.0.clear(); + } + + pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { + if !self.temp.0.is_empty() { + self.flush(); + } + self.uniforms.write_buffer(device, queue); + } + + #[inline] + pub fn binding(&self) -> Option { + self.uniforms.binding() + } +} + +// ---------------------------------------------------------------------------- + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +struct MaxCapacityArray(T, usize); + +impl ShaderType for MaxCapacityArray +where + T: ShaderType, +{ + type ExtraMetadata = ArrayMetadata; + + const METADATA: Metadata = T::METADATA; + + fn size(&self) -> ::core::num::NonZeroU64 { + Self::METADATA.stride().mul(self.1.max(1) as u64).0 + } +} + +impl WriteInto for MaxCapacityArray +where + T: WriteInto + RuntimeSizedArray, +{ + fn write_into(&self, writer: &mut Writer) { + debug_assert!(self.0.len() <= self.1); + self.0.write_into(writer) + } +} + +#[inline] +fn round_up(v: u64, a: u64) -> u64 { + ((v + a - 1) / a) * a +} diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 4a3c744e071c9..2794f9a8f13ae 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -21,9 +21,11 @@ use wgpu::BufferUsages; /// from system RAM to VRAM. /// /// 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) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) pub struct BufferVec { diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs new file mode 100644 index 0000000000000..a22a341982d58 --- /dev/null +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -0,0 +1,127 @@ +use super::StorageBuffer; +use crate::{ + render_resource::batched_uniform_buffer::BatchedUniformBuffer, + renderer::{RenderDevice, RenderQueue}, +}; +use bevy_ecs::{prelude::Component, system::Resource}; +use encase::{private::WriteInto, ShaderSize, ShaderType}; +use std::{marker::PhantomData, mem}; +use wgpu::{BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, ShaderStages}; + +/// Trait for types able to go in a [`GpuList`]. +pub trait GpuListable: ShaderType + ShaderSize + WriteInto + Clone {} +impl GpuListable for T {} + +/// Stores a list of elements to be transferred to the GPU and made accessible to shaders as a read-only array. +/// +/// On platforms that support storage buffers, this is equivalent to [`StorageBuffer>`]. +/// Otherwise, this falls back to batched uniforms. +/// +/// 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) +/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`BufferVec`](crate::render_resource::BufferVec) +/// * [`Texture`](crate::render_resource::Texture) +#[derive(Resource)] +pub enum GpuList { + Uniform(BatchedUniformBuffer), + Storage((StorageBuffer>, Vec)), +} + +impl GpuList { + pub fn new(device: &RenderDevice) -> Self { + let limits = device.limits(); + if limits.max_storage_buffers_per_shader_stage < 3 { + GpuList::Uniform(BatchedUniformBuffer::new(&limits)) + } else { + GpuList::Storage((StorageBuffer::default(), Vec::new())) + } + } + + pub fn clear(&mut self) { + match self { + GpuList::Uniform(buffer) => buffer.clear(), + GpuList::Storage((_, buffer)) => buffer.clear(), + } + } + + pub fn push(&mut self, value: T) -> GpuListIndex { + match self { + GpuList::Uniform(buffer) => buffer.push(value), + GpuList::Storage((_, buffer)) => { + let index = buffer.len() as u32; + buffer.push(value); + GpuListIndex { + index, + dynamic_offset: None, + element_type: PhantomData, + } + } + } + } + + pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { + match self { + GpuList::Uniform(buffer) => buffer.write_buffer(device, queue), + GpuList::Storage((buffer, vec)) => { + buffer.set(mem::take(vec)); + buffer.write_buffer(device, queue); + } + } + } + + pub fn binding_layout( + binding: u32, + visibility: ShaderStages, + device: &RenderDevice, + ) -> BindGroupLayoutEntry { + BindGroupLayoutEntry { + binding, + visibility, + ty: if device.limits().max_storage_buffers_per_shader_stage < 3 { + BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(T::min_size()), + } + } else { + BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: Some(T::min_size()), + } + }, + count: None, + } + } + + pub fn binding(&self) -> Option { + match self { + GpuList::Uniform(buffer) => buffer.binding(), + GpuList::Storage((buffer, _)) => buffer.binding(), + } + } + + pub fn batch_size(device: &RenderDevice) -> Option { + let limits = device.limits(); + if limits.max_storage_buffers_per_shader_stage < 3 { + Some(BatchedUniformBuffer::::batch_size(&limits) as u32) + } else { + None + } + } +} + +/// An index into a [`GpuList`] for a given element. +#[derive(Component)] +pub struct GpuListIndex { + /// The index to use in a shader on the array. + pub index: u32, + /// The dynamic offset to use when binding the list from Rust. + /// Only used on platforms that don't support storage buffers. + pub dynamic_offset: Option, + pub element_type: PhantomData, +} diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index 91440cf55c276..70c5428795efe 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -1,7 +1,9 @@ +mod batched_uniform_buffer; mod bind_group; mod bind_group_layout; mod buffer; mod buffer_vec; +mod gpu_list; mod pipeline; mod pipeline_cache; mod pipeline_specializer; @@ -15,6 +17,7 @@ pub use bind_group::*; pub use bind_group_layout::*; pub use buffer::*; pub use buffer_vec::*; +pub use gpu_list::*; pub use pipeline::*; pub use pipeline_cache::*; pub use pipeline_specializer::*; diff --git a/crates/bevy_render/src/render_resource/storage_buffer.rs b/crates/bevy_render/src/render_resource/storage_buffer.rs index 19bdb00909ad5..1f6693f0b9a0d 100644 --- a/crates/bevy_render/src/render_resource/storage_buffer.rs +++ b/crates/bevy_render/src/render_resource/storage_buffer.rs @@ -20,9 +20,11 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// is automatically enforced by this structure. /// /// 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) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// @@ -153,8 +155,10 @@ impl StorageBuffer { /// /// 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) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// diff --git a/crates/bevy_render/src/render_resource/uniform_buffer.rs b/crates/bevy_render/src/render_resource/uniform_buffer.rs index e59b21ec555e2..cf64627e2be52 100644 --- a/crates/bevy_render/src/render_resource/uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/uniform_buffer.rs @@ -20,9 +20,11 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// (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) +/// * [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// @@ -149,6 +151,8 @@ impl UniformBuffer { /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// /// [std140 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-uniform @@ -177,6 +181,18 @@ impl Default for DynamicUniformBuffer { } impl DynamicUniformBuffer { + pub fn new_with_alignment(alignment: u64) -> Self { + Self { + values: Vec::new(), + scratch: DynamicUniformBufferWrapper::new_with_alignment(Vec::new(), alignment), + buffer: None, + capacity: 0, + label: None, + changed: false, + buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM, + } + } + #[inline] pub fn buffer(&self) -> Option<&Buffer> { self.buffer.as_ref() From c11d4f2f1068483fe468e5105e29c74d9b4b0182 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Fri, 24 Mar 2023 22:20:51 -0400 Subject: [PATCH 02/24] Clippy lint --- .../bevy_render/src/render_resource/batched_uniform_buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 034da33d087d0..8fb587802df51 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -115,7 +115,7 @@ where { fn write_into(&self, writer: &mut Writer) { debug_assert!(self.0.len() <= self.1); - self.0.write_into(writer) + self.0.write_into(writer); } } From 8688472dbc4816cad94e55473bf1f14cc86ffd39 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Fri, 24 Mar 2023 21:49:51 -0400 Subject: [PATCH 03/24] Add GpuList and BatchedUniformBuffer Clippy lint --- crates/bevy_render/src/gpu_component_list.rs | 55 ++++++++ crates/bevy_render/src/lib.rs | 1 + .../render_resource/batched_uniform_buffer.rs | 125 +++++++++++++++++ .../src/render_resource/buffer_vec.rs | 2 + .../src/render_resource/gpu_list.rs | 127 ++++++++++++++++++ crates/bevy_render/src/render_resource/mod.rs | 3 + .../src/render_resource/storage_buffer.rs | 4 + .../src/render_resource/uniform_buffer.rs | 18 ++- 8 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_render/src/gpu_component_list.rs create mode 100644 crates/bevy_render/src/render_resource/batched_uniform_buffer.rs create mode 100644 crates/bevy_render/src/render_resource/gpu_list.rs diff --git a/crates/bevy_render/src/gpu_component_list.rs b/crates/bevy_render/src/gpu_component_list.rs new file mode 100644 index 0000000000000..efcb49fe8bf6b --- /dev/null +++ b/crates/bevy_render/src/gpu_component_list.rs @@ -0,0 +1,55 @@ +use crate::{ + render_resource::{GpuList, GpuListable}, + renderer::{RenderDevice, RenderQueue}, + Render, RenderApp, RenderSet, +}; +use bevy_app::{App, Plugin}; +use bevy_ecs::{ + prelude::{Component, Entity}, + schedule::IntoSystemConfigs, + system::{Commands, Query, Res, ResMut}, +}; +use std::marker::PhantomData; + +/// This plugin prepares the components of the corresponding type for the GPU +/// by storing them in a [`GpuList`]. +pub struct GpuComponentListPlugin(PhantomData); + +impl Plugin for GpuComponentListPlugin { + fn build(&self, app: &mut App) { + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { + render_app + .insert_resource(GpuList::::new( + render_app.world.resource::(), + )) + .add_systems( + Render, + prepare_gpu_component_lists::.in_set(RenderSet::Prepare), + ); + } + } +} + +impl Default for GpuComponentListPlugin { + fn default() -> Self { + Self(PhantomData::) + } +} + +fn prepare_gpu_component_lists( + mut commands: Commands, + render_device: Res, + render_queue: Res, + mut gpu_list: ResMut>, + components: Query<(Entity, &C)>, +) { + gpu_list.clear(); + + let entities = components + .iter() + .map(|(entity, component)| (entity, gpu_list.push(component.clone()))) + .collect::>(); + commands.insert_or_spawn_batch(entities); + + gpu_list.write_buffer(&render_device, &render_queue); +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7101ba435f314..4fb3be0459a54 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -9,6 +9,7 @@ pub mod extract_component; mod extract_param; pub mod extract_resource; pub mod globals; +pub mod gpu_component_list; pub mod mesh; pub mod pipelined_rendering; pub mod primitives; diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs new file mode 100644 index 0000000000000..8fb587802df51 --- /dev/null +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -0,0 +1,125 @@ +use super::{GpuListIndex, GpuListable}; +use crate::{ + render_resource::DynamicUniformBuffer, + renderer::{RenderDevice, RenderQueue}, +}; +use encase::{ + private::{ArrayMetadata, BufferMut, Metadata, RuntimeSizedArray, WriteInto, Writer}, + ShaderType, +}; +use std::{marker::PhantomData, num::NonZeroU64}; +use wgpu::{BindingResource, Limits}; + +// 1MB else we will make really large arrays on macOS which reports very large +// `max_uniform_buffer_binding_size`. On macOS this ends up being the minimum +// size of the uniform buffer as well as the size of each chunk of data at a +// dynamic offset. +const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; + +/// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size) +/// are grouped into a batch as an `array` in WGSL. +pub struct BatchedUniformBuffer { + uniforms: DynamicUniformBuffer>>, + temp: MaxCapacityArray>, + current_offset: u32, + dynamic_offset_alignment: u32, +} + +impl BatchedUniformBuffer { + pub fn batch_size(limits: &Limits) -> usize { + (limits + .max_uniform_buffer_binding_size + .min(MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE) as u64 + / T::min_size().get()) as usize + } + + pub fn new(limits: &Limits) -> Self { + let capacity = Self::batch_size(limits); + let alignment = limits.min_uniform_buffer_offset_alignment; + + Self { + uniforms: DynamicUniformBuffer::new_with_alignment(alignment as u64), + temp: MaxCapacityArray(Vec::with_capacity(capacity), capacity), + current_offset: 0, + dynamic_offset_alignment: alignment, + } + } + + #[inline] + pub fn size(&self) -> NonZeroU64 { + self.temp.size() + } + + pub fn clear(&mut self) { + self.uniforms.clear(); + self.current_offset = 0; + self.temp.0.clear(); + } + + pub fn push(&mut self, component: T) -> GpuListIndex { + let result = GpuListIndex { + index: self.temp.0.len() as u32, + dynamic_offset: Some(self.current_offset), + element_type: PhantomData, + }; + self.temp.0.push(component); + if self.temp.0.len() == self.temp.1 { + self.flush(); + } + result + } + + pub fn flush(&mut self) { + self.uniforms.push(self.temp.clone()); + + self.current_offset += + round_up(self.temp.size().get(), self.dynamic_offset_alignment as u64) as u32; + + self.temp.0.clear(); + } + + pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { + if !self.temp.0.is_empty() { + self.flush(); + } + self.uniforms.write_buffer(device, queue); + } + + #[inline] + pub fn binding(&self) -> Option { + self.uniforms.binding() + } +} + +// ---------------------------------------------------------------------------- + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +struct MaxCapacityArray(T, usize); + +impl ShaderType for MaxCapacityArray +where + T: ShaderType, +{ + type ExtraMetadata = ArrayMetadata; + + const METADATA: Metadata = T::METADATA; + + fn size(&self) -> ::core::num::NonZeroU64 { + Self::METADATA.stride().mul(self.1.max(1) as u64).0 + } +} + +impl WriteInto for MaxCapacityArray +where + T: WriteInto + RuntimeSizedArray, +{ + fn write_into(&self, writer: &mut Writer) { + debug_assert!(self.0.len() <= self.1); + self.0.write_into(writer); + } +} + +#[inline] +fn round_up(v: u64, a: u64) -> u64 { + ((v + a - 1) / a) * a +} diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 4a3c744e071c9..2794f9a8f13ae 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -21,9 +21,11 @@ use wgpu::BufferUsages; /// from system RAM to VRAM. /// /// 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) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) pub struct BufferVec { diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs new file mode 100644 index 0000000000000..a22a341982d58 --- /dev/null +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -0,0 +1,127 @@ +use super::StorageBuffer; +use crate::{ + render_resource::batched_uniform_buffer::BatchedUniformBuffer, + renderer::{RenderDevice, RenderQueue}, +}; +use bevy_ecs::{prelude::Component, system::Resource}; +use encase::{private::WriteInto, ShaderSize, ShaderType}; +use std::{marker::PhantomData, mem}; +use wgpu::{BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, ShaderStages}; + +/// Trait for types able to go in a [`GpuList`]. +pub trait GpuListable: ShaderType + ShaderSize + WriteInto + Clone {} +impl GpuListable for T {} + +/// Stores a list of elements to be transferred to the GPU and made accessible to shaders as a read-only array. +/// +/// On platforms that support storage buffers, this is equivalent to [`StorageBuffer>`]. +/// Otherwise, this falls back to batched uniforms. +/// +/// 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) +/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`BufferVec`](crate::render_resource::BufferVec) +/// * [`Texture`](crate::render_resource::Texture) +#[derive(Resource)] +pub enum GpuList { + Uniform(BatchedUniformBuffer), + Storage((StorageBuffer>, Vec)), +} + +impl GpuList { + pub fn new(device: &RenderDevice) -> Self { + let limits = device.limits(); + if limits.max_storage_buffers_per_shader_stage < 3 { + GpuList::Uniform(BatchedUniformBuffer::new(&limits)) + } else { + GpuList::Storage((StorageBuffer::default(), Vec::new())) + } + } + + pub fn clear(&mut self) { + match self { + GpuList::Uniform(buffer) => buffer.clear(), + GpuList::Storage((_, buffer)) => buffer.clear(), + } + } + + pub fn push(&mut self, value: T) -> GpuListIndex { + match self { + GpuList::Uniform(buffer) => buffer.push(value), + GpuList::Storage((_, buffer)) => { + let index = buffer.len() as u32; + buffer.push(value); + GpuListIndex { + index, + dynamic_offset: None, + element_type: PhantomData, + } + } + } + } + + pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { + match self { + GpuList::Uniform(buffer) => buffer.write_buffer(device, queue), + GpuList::Storage((buffer, vec)) => { + buffer.set(mem::take(vec)); + buffer.write_buffer(device, queue); + } + } + } + + pub fn binding_layout( + binding: u32, + visibility: ShaderStages, + device: &RenderDevice, + ) -> BindGroupLayoutEntry { + BindGroupLayoutEntry { + binding, + visibility, + ty: if device.limits().max_storage_buffers_per_shader_stage < 3 { + BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(T::min_size()), + } + } else { + BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: Some(T::min_size()), + } + }, + count: None, + } + } + + pub fn binding(&self) -> Option { + match self { + GpuList::Uniform(buffer) => buffer.binding(), + GpuList::Storage((buffer, _)) => buffer.binding(), + } + } + + pub fn batch_size(device: &RenderDevice) -> Option { + let limits = device.limits(); + if limits.max_storage_buffers_per_shader_stage < 3 { + Some(BatchedUniformBuffer::::batch_size(&limits) as u32) + } else { + None + } + } +} + +/// An index into a [`GpuList`] for a given element. +#[derive(Component)] +pub struct GpuListIndex { + /// The index to use in a shader on the array. + pub index: u32, + /// The dynamic offset to use when binding the list from Rust. + /// Only used on platforms that don't support storage buffers. + pub dynamic_offset: Option, + pub element_type: PhantomData, +} diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index 91440cf55c276..70c5428795efe 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -1,7 +1,9 @@ +mod batched_uniform_buffer; mod bind_group; mod bind_group_layout; mod buffer; mod buffer_vec; +mod gpu_list; mod pipeline; mod pipeline_cache; mod pipeline_specializer; @@ -15,6 +17,7 @@ pub use bind_group::*; pub use bind_group_layout::*; pub use buffer::*; pub use buffer_vec::*; +pub use gpu_list::*; pub use pipeline::*; pub use pipeline_cache::*; pub use pipeline_specializer::*; diff --git a/crates/bevy_render/src/render_resource/storage_buffer.rs b/crates/bevy_render/src/render_resource/storage_buffer.rs index 19bdb00909ad5..1f6693f0b9a0d 100644 --- a/crates/bevy_render/src/render_resource/storage_buffer.rs +++ b/crates/bevy_render/src/render_resource/storage_buffer.rs @@ -20,9 +20,11 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// is automatically enforced by this structure. /// /// 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) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// @@ -153,8 +155,10 @@ impl StorageBuffer { /// /// 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) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// diff --git a/crates/bevy_render/src/render_resource/uniform_buffer.rs b/crates/bevy_render/src/render_resource/uniform_buffer.rs index e59b21ec555e2..cf64627e2be52 100644 --- a/crates/bevy_render/src/render_resource/uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/uniform_buffer.rs @@ -20,9 +20,11 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// (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) +/// * [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// @@ -149,6 +151,8 @@ impl UniformBuffer { /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// /// [std140 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-uniform @@ -177,6 +181,18 @@ impl Default for DynamicUniformBuffer { } impl DynamicUniformBuffer { + pub fn new_with_alignment(alignment: u64) -> Self { + Self { + values: Vec::new(), + scratch: DynamicUniformBufferWrapper::new_with_alignment(Vec::new(), alignment), + buffer: None, + capacity: 0, + label: None, + changed: false, + buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM, + } + } + #[inline] pub fn buffer(&self) -> Option<&Buffer> { self.buffer.as_ref() From dc39eb52cb9f03774b5470922386044324f3e696 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:21:24 -0400 Subject: [PATCH 04/24] Update crates/bevy_render/src/render_resource/batched_uniform_buffer.rs Co-authored-by: Robert Swain --- .../bevy_render/src/render_resource/batched_uniform_buffer.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 8fb587802df51..6a7c753186ea6 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -18,6 +18,10 @@ const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; /// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size) /// are grouped into a batch as an `array` in WGSL. +/// +/// This reduces the number of rebindings required due to having to pass dynamic +/// offsets to bind group commands, and if indices into the array can be passed +/// in via other means, it enables batching of draw commands. pub struct BatchedUniformBuffer { uniforms: DynamicUniformBuffer>>, temp: MaxCapacityArray>, From f88ccc4117aa498e22ffc0f05f911eb164384320 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:21:44 -0400 Subject: [PATCH 05/24] Update crates/bevy_render/src/render_resource/gpu_list.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/gpu_list.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index a22a341982d58..0db07197f2746 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -15,7 +15,8 @@ impl GpuListable for T {} /// Stores a list of elements to be transferred to the GPU and made accessible to shaders as a read-only array. /// /// On platforms that support storage buffers, this is equivalent to [`StorageBuffer>`]. -/// Otherwise, this falls back to batched uniforms. +/// Otherwise, this falls back to a dynamic offset uniform buffer with the largest +/// array of T that fits within a uniform buffer binding. /// /// Other options for storing GPU-accessible data are: /// * [`StorageBuffer`](crate::render_resource::StorageBuffer) From cdbbad254681ed0df3a0117b0c1ac78f1ae8d6fe Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:22:00 -0400 Subject: [PATCH 06/24] Update crates/bevy_render/src/render_resource/gpu_list.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/gpu_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 0db07197f2746..2225513650172 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -35,7 +35,7 @@ pub enum GpuList { impl GpuList { pub fn new(device: &RenderDevice) -> Self { let limits = device.limits(); - if limits.max_storage_buffers_per_shader_stage < 3 { + if limits.max_storage_buffers_per_shader_stage < 1 { GpuList::Uniform(BatchedUniformBuffer::new(&limits)) } else { GpuList::Storage((StorageBuffer::default(), Vec::new())) From 94a58ecd22a3504173404bfa4e4666dd4b3e9161 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:23:39 -0400 Subject: [PATCH 07/24] Update crates/bevy_render/src/render_resource/gpu_list.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/gpu_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 2225513650172..8ffafdee61d6f 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -82,7 +82,7 @@ impl GpuList { BindGroupLayoutEntry { binding, visibility, - ty: if device.limits().max_storage_buffers_per_shader_stage < 3 { + ty: if device.limits().max_storage_buffers_per_shader_stage < 1 { BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: true, From c5357de0d52ff347f36ac9a18d2441ab9847f2fd Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:24:52 -0400 Subject: [PATCH 08/24] Update crates/bevy_render/src/render_resource/gpu_list.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/gpu_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 8ffafdee61d6f..a356c8378868f 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -119,7 +119,7 @@ impl GpuList { /// An index into a [`GpuList`] for a given element. #[derive(Component)] pub struct GpuListIndex { - /// The index to use in a shader on the array. + /// The index to use in a shader into the array. pub index: u32, /// The dynamic offset to use when binding the list from Rust. /// Only used on platforms that don't support storage buffers. From 70254d432ff6da84d63d0ca230862414b9d0f517 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:25:06 -0400 Subject: [PATCH 09/24] Update crates/bevy_render/src/render_resource/gpu_list.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/gpu_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index a356c8378868f..6b03d49e766b5 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -108,7 +108,7 @@ impl GpuList { pub fn batch_size(device: &RenderDevice) -> Option { let limits = device.limits(); - if limits.max_storage_buffers_per_shader_stage < 3 { + if limits.max_storage_buffers_per_shader_stage < 1 { Some(BatchedUniformBuffer::::batch_size(&limits) as u32) } else { None From 7f7101d93bae6d82698a187714665ed9e6dae28b Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:26:06 -0400 Subject: [PATCH 10/24] Update crates/bevy_render/src/render_resource/gpu_list.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/gpu_list.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 6b03d49e766b5..00934c22dc04a 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -23,7 +23,6 @@ impl GpuListable for T {} /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) #[derive(Resource)] From 66e48d7733b5d54c1fde502315e7ccd34de8c47c Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:26:16 -0400 Subject: [PATCH 11/24] Update crates/bevy_render/src/render_resource/storage_buffer.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/storage_buffer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/storage_buffer.rs b/crates/bevy_render/src/render_resource/storage_buffer.rs index 1f6693f0b9a0d..6813499425310 100644 --- a/crates/bevy_render/src/render_resource/storage_buffer.rs +++ b/crates/bevy_render/src/render_resource/storage_buffer.rs @@ -20,7 +20,6 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// is automatically enforced by this structure. /// /// 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) From 00fb9b563bd3126295473c1b3e7d09e45e122f08 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:26:47 -0400 Subject: [PATCH 12/24] Update crates/bevy_render/src/render_resource/storage_buffer.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/storage_buffer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/storage_buffer.rs b/crates/bevy_render/src/render_resource/storage_buffer.rs index 6813499425310..6e526b9947358 100644 --- a/crates/bevy_render/src/render_resource/storage_buffer.rs +++ b/crates/bevy_render/src/render_resource/storage_buffer.rs @@ -154,7 +154,6 @@ impl StorageBuffer { /// /// 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) /// * [`GpuList`](crate::render_resource::GpuList) From 3144b42218abf62881835b89f93af6d73dacefdf Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:26:53 -0400 Subject: [PATCH 13/24] Update crates/bevy_render/src/render_resource/uniform_buffer.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/render_resource/uniform_buffer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/uniform_buffer.rs b/crates/bevy_render/src/render_resource/uniform_buffer.rs index cf64627e2be52..841e31d9f3d86 100644 --- a/crates/bevy_render/src/render_resource/uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/uniform_buffer.rs @@ -22,7 +22,6 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// 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) /// * [`GpuList`](crate::render_resource::GpuList) /// * [`BufferVec`](crate::render_resource::BufferVec) From 658568a0d2a8338a1dead9ff024b678aa899e7eb Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 1 May 2023 18:34:36 +0200 Subject: [PATCH 14/24] Update crates/bevy_render/src/render_resource/gpu_list.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: François --- crates/bevy_render/src/render_resource/gpu_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 00934c22dc04a..a45d708d5f073 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -34,7 +34,7 @@ pub enum GpuList { impl GpuList { pub fn new(device: &RenderDevice) -> Self { let limits = device.limits(); - if limits.max_storage_buffers_per_shader_stage < 1 { + if limits.max_storage_buffers_per_shader_stage == 0 { GpuList::Uniform(BatchedUniformBuffer::new(&limits)) } else { GpuList::Storage((StorageBuffer::default(), Vec::new())) From eb930679852330a1e269657b5ebb2284294990bc Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 1 May 2023 18:34:47 +0200 Subject: [PATCH 15/24] Update crates/bevy_render/src/render_resource/gpu_list.rs --- crates/bevy_render/src/render_resource/gpu_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index a45d708d5f073..63661fc235798 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -120,7 +120,7 @@ impl GpuList { pub struct GpuListIndex { /// The index to use in a shader into the array. pub index: u32, - /// The dynamic offset to use when binding the list from Rust. + /// The dynamic offset to use when setting the bind group in a pass. /// Only used on platforms that don't support storage buffers. pub dynamic_offset: Option, pub element_type: PhantomData, From 68f7a8e3b1017f3f5857d4dee8e7dbc76d21b997 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 1 May 2023 23:05:08 +0200 Subject: [PATCH 16/24] Fixes to buffer sizes MaxCapacityArray is runtime-sized but MaxCapacityArray::min_size() returns a much smaller size, I believe T::min_size(), as it cannot know the size of the buffer as that depends on the platform / graphics API / device limits. For some reason this was not a problem on native, but on WebGL2 it mattered. This is anyway correct. --- .../src/render_resource/batched_uniform_buffer.rs | 7 ++++++- crates/bevy_render/src/render_resource/gpu_list.rs | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 6a7c753186ea6..b5da3685362ba 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -91,7 +91,12 @@ impl BatchedUniformBuffer { #[inline] pub fn binding(&self) -> Option { - self.uniforms.binding() + let mut binding = self.uniforms.binding(); + if let Some(BindingResource::Buffer(binding)) = &mut binding { + // MaxCapacityArray is runtime-sized so can't use T::min_size() + binding.size = Some(self.size()); + } + binding } } diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 63661fc235798..7b54e15ae697a 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -85,7 +85,9 @@ impl GpuList { BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: true, - min_binding_size: Some(T::min_size()), + // BatchedUniformBuffer uses a MaxCapacityArray that is runtime-sized, so we use + // None here and let wgpu figure out the size. + min_binding_size: None, } } else { BindingType::Buffer { From 05c723f25be97af42a5a2bdc0a8d26be7fa54eff Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 1 May 2023 23:13:37 +0200 Subject: [PATCH 17/24] Fix after merge from main --- crates/bevy_render/src/render_resource/uniform_buffer.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_render/src/render_resource/uniform_buffer.rs b/crates/bevy_render/src/render_resource/uniform_buffer.rs index 7ac0db2f80eae..cfe5842815f51 100644 --- a/crates/bevy_render/src/render_resource/uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/uniform_buffer.rs @@ -182,13 +182,12 @@ impl Default for DynamicUniformBuffer { impl DynamicUniformBuffer { pub fn new_with_alignment(alignment: u64) -> Self { Self { - values: Vec::new(), scratch: DynamicUniformBufferWrapper::new_with_alignment(Vec::new(), alignment), buffer: None, - capacity: 0, label: None, changed: false, buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM, + _marker: PhantomData, } } From 37fdbd4d2f367f7a03f5a5a00ba13f93a870e963 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 1 May 2023 23:37:42 +0200 Subject: [PATCH 18/24] Add credit to Teoxoy for MaxCapacityArray Co-authored-by: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com> --- .../src/render_resource/batched_uniform_buffer.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index b5da3685362ba..401a6a4761b4e 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -100,7 +100,15 @@ impl BatchedUniformBuffer { } } +#[inline] +fn round_up(v: u64, a: u64) -> u64 { + ((v + a - 1) / a) * a +} + // ---------------------------------------------------------------------------- +// MaxCapacityArray was implemented by Teodor Tanasoaia for encase. It was +// copied here as it was not yet included in an encase release and it is +// unclear if it is the correct long-term solution for encase. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] struct MaxCapacityArray(T, usize); @@ -127,8 +135,3 @@ where self.0.write_into(writer); } } - -#[inline] -fn round_up(v: u64, a: u64) -> u64 { - ((v + a - 1) / a) * a -} From f5ca55d4af4bedc1133d17780b19f95483b30a15 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 2 May 2023 00:32:03 +0200 Subject: [PATCH 19/24] Clarify logic around max_storage_buffers_per_shader_stage Use == 0 instead of < 1. --- crates/bevy_render/src/render_resource/gpu_list.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_list.rs index 7b54e15ae697a..8f8a606f06809 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_list.rs @@ -81,7 +81,7 @@ impl GpuList { BindGroupLayoutEntry { binding, visibility, - ty: if device.limits().max_storage_buffers_per_shader_stage < 1 { + ty: if device.limits().max_storage_buffers_per_shader_stage == 0 { BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: true, @@ -109,7 +109,7 @@ impl GpuList { pub fn batch_size(device: &RenderDevice) -> Option { let limits = device.limits(); - if limits.max_storage_buffers_per_shader_stage < 1 { + if limits.max_storage_buffers_per_shader_stage == 0 { Some(BatchedUniformBuffer::::batch_size(&limits) as u32) } else { None From 9f4f027a83b4ce5e3497b15823e55ce6e72a5542 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:15:18 -0400 Subject: [PATCH 20/24] Lower MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE on WebGL2 --- .../src/render_resource/batched_uniform_buffer.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 401a6a4761b4e..3905b2f929ead 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -14,8 +14,17 @@ use wgpu::{BindingResource, Limits}; // `max_uniform_buffer_binding_size`. On macOS this ends up being the minimum // size of the uniform buffer as well as the size of each chunk of data at a // dynamic offset. +#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; +// WebGL2 quirk: using uniform buffers larger than 4KB will cause extremely +// long shader compilation times, so the limit needs to be lower on WebGL2. +// This is due to older shader compilers/GPUs that don't support dynamically +// indexing uniform buffers, and instead emulate it with large switch statements +// over buffer indices that take a long time to compile. +#[cfg(all(feature = "webgl", target_arch = "wasm32"))] +const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 12; + /// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size) /// are grouped into a batch as an `array` in WGSL. /// From 973b8bbe3ae7a954e207e2f5876544326669180b Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:22:29 -0400 Subject: [PATCH 21/24] Rename GpuList -> GpuArrayBuffer --- ..._list.rs => gpu_component_array_buffer.rs} | 24 +++++------ crates/bevy_render/src/lib.rs | 2 +- .../render_resource/batched_uniform_buffer.rs | 10 ++--- .../src/render_resource/buffer_vec.rs | 2 +- .../{gpu_list.rs => gpu_array_buffer.rs} | 40 +++++++++---------- crates/bevy_render/src/render_resource/mod.rs | 4 +- .../src/render_resource/storage_buffer.rs | 4 +- .../src/render_resource/uniform_buffer.rs | 4 +- 8 files changed, 45 insertions(+), 45 deletions(-) rename crates/bevy_render/src/{gpu_component_list.rs => gpu_component_array_buffer.rs} (54%) rename crates/bevy_render/src/render_resource/{gpu_list.rs => gpu_array_buffer.rs} (74%) diff --git a/crates/bevy_render/src/gpu_component_list.rs b/crates/bevy_render/src/gpu_component_array_buffer.rs similarity index 54% rename from crates/bevy_render/src/gpu_component_list.rs rename to crates/bevy_render/src/gpu_component_array_buffer.rs index efcb49fe8bf6b..6076049c7fd2c 100644 --- a/crates/bevy_render/src/gpu_component_list.rs +++ b/crates/bevy_render/src/gpu_component_array_buffer.rs @@ -1,5 +1,5 @@ use crate::{ - render_resource::{GpuList, GpuListable}, + render_resource::{GpuArrayBuffer, GpuArrayBufferable}, renderer::{RenderDevice, RenderQueue}, Render, RenderApp, RenderSet, }; @@ -12,44 +12,44 @@ use bevy_ecs::{ use std::marker::PhantomData; /// This plugin prepares the components of the corresponding type for the GPU -/// by storing them in a [`GpuList`]. -pub struct GpuComponentListPlugin(PhantomData); +/// by storing them in a [`GpuArrayBuffer`]. +pub struct GpuComponentArrayBufferPlugin(PhantomData); -impl Plugin for GpuComponentListPlugin { +impl Plugin for GpuComponentArrayBufferPlugin { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app - .insert_resource(GpuList::::new( + .insert_resource(GpuArrayBuffer::::new( render_app.world.resource::(), )) .add_systems( Render, - prepare_gpu_component_lists::.in_set(RenderSet::Prepare), + prepare_gpu_component_array_buffers::.in_set(RenderSet::Prepare), ); } } } -impl Default for GpuComponentListPlugin { +impl Default for GpuComponentArrayBufferPlugin { fn default() -> Self { Self(PhantomData::) } } -fn prepare_gpu_component_lists( +fn prepare_gpu_component_array_buffers( mut commands: Commands, render_device: Res, render_queue: Res, - mut gpu_list: ResMut>, + mut gpu_array_buffer: ResMut>, components: Query<(Entity, &C)>, ) { - gpu_list.clear(); + gpu_array_buffer.clear(); let entities = components .iter() - .map(|(entity, component)| (entity, gpu_list.push(component.clone()))) + .map(|(entity, component)| (entity, gpu_array_buffer.push(component.clone()))) .collect::>(); commands.insert_or_spawn_batch(entities); - gpu_list.write_buffer(&render_device, &render_queue); + gpu_array_buffer.write_buffer(&render_device, &render_queue); } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 4d53475bc51c0..06b77ac9dba18 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -11,7 +11,7 @@ pub mod extract_component; mod extract_param; pub mod extract_resource; pub mod globals; -pub mod gpu_component_list; +pub mod gpu_component_array_buffer; pub mod mesh; pub mod pipelined_rendering; pub mod primitives; diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 3905b2f929ead..637dc8cb490bc 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -1,4 +1,4 @@ -use super::{GpuListIndex, GpuListable}; +use super::{GpuArrayBufferIndex, GpuArrayBufferable}; use crate::{ render_resource::DynamicUniformBuffer, renderer::{RenderDevice, RenderQueue}, @@ -31,14 +31,14 @@ const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 12; /// This reduces the number of rebindings required due to having to pass dynamic /// offsets to bind group commands, and if indices into the array can be passed /// in via other means, it enables batching of draw commands. -pub struct BatchedUniformBuffer { +pub struct BatchedUniformBuffer { uniforms: DynamicUniformBuffer>>, temp: MaxCapacityArray>, current_offset: u32, dynamic_offset_alignment: u32, } -impl BatchedUniformBuffer { +impl BatchedUniformBuffer { pub fn batch_size(limits: &Limits) -> usize { (limits .max_uniform_buffer_binding_size @@ -69,8 +69,8 @@ impl BatchedUniformBuffer { self.temp.0.clear(); } - pub fn push(&mut self, component: T) -> GpuListIndex { - let result = GpuListIndex { + pub fn push(&mut self, component: T) -> GpuArrayBufferIndex { + let result = GpuArrayBufferIndex { index: self.temp.0.len() as u32, dynamic_offset: Some(self.current_offset), element_type: PhantomData, diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 1be83fa01691f..002e8f8bd3309 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -25,7 +25,7 @@ use wgpu::BufferUsages; /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) pub struct BufferVec { diff --git a/crates/bevy_render/src/render_resource/gpu_list.rs b/crates/bevy_render/src/render_resource/gpu_array_buffer.rs similarity index 74% rename from crates/bevy_render/src/render_resource/gpu_list.rs rename to crates/bevy_render/src/render_resource/gpu_array_buffer.rs index 8f8a606f06809..0954daf2592ac 100644 --- a/crates/bevy_render/src/render_resource/gpu_list.rs +++ b/crates/bevy_render/src/render_resource/gpu_array_buffer.rs @@ -8,15 +8,15 @@ use encase::{private::WriteInto, ShaderSize, ShaderType}; use std::{marker::PhantomData, mem}; use wgpu::{BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, ShaderStages}; -/// Trait for types able to go in a [`GpuList`]. -pub trait GpuListable: ShaderType + ShaderSize + WriteInto + Clone {} -impl GpuListable for T {} +/// Trait for types able to go in a [`GpuArrayBuffer`]. +pub trait GpuArrayBufferable: ShaderType + ShaderSize + WriteInto + Clone {} +impl GpuArrayBufferable for T {} /// Stores a list of elements to be transferred to the GPU and made accessible to shaders as a read-only array. /// /// On platforms that support storage buffers, this is equivalent to [`StorageBuffer>`]. /// Otherwise, this falls back to a dynamic offset uniform buffer with the largest -/// array of T that fits within a uniform buffer binding. +/// array of T that fits within a uniform buffer binding (within reasonable limits). /// /// Other options for storing GPU-accessible data are: /// * [`StorageBuffer`](crate::render_resource::StorageBuffer) @@ -26,35 +26,35 @@ impl GpuListable for T {} /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) #[derive(Resource)] -pub enum GpuList { +pub enum GpuArrayBuffer { Uniform(BatchedUniformBuffer), Storage((StorageBuffer>, Vec)), } -impl GpuList { +impl GpuArrayBuffer { pub fn new(device: &RenderDevice) -> Self { let limits = device.limits(); if limits.max_storage_buffers_per_shader_stage == 0 { - GpuList::Uniform(BatchedUniformBuffer::new(&limits)) + GpuArrayBuffer::Uniform(BatchedUniformBuffer::new(&limits)) } else { - GpuList::Storage((StorageBuffer::default(), Vec::new())) + GpuArrayBuffer::Storage((StorageBuffer::default(), Vec::new())) } } pub fn clear(&mut self) { match self { - GpuList::Uniform(buffer) => buffer.clear(), - GpuList::Storage((_, buffer)) => buffer.clear(), + GpuArrayBuffer::Uniform(buffer) => buffer.clear(), + GpuArrayBuffer::Storage((_, buffer)) => buffer.clear(), } } - pub fn push(&mut self, value: T) -> GpuListIndex { + pub fn push(&mut self, value: T) -> GpuArrayBufferIndex { match self { - GpuList::Uniform(buffer) => buffer.push(value), - GpuList::Storage((_, buffer)) => { + GpuArrayBuffer::Uniform(buffer) => buffer.push(value), + GpuArrayBuffer::Storage((_, buffer)) => { let index = buffer.len() as u32; buffer.push(value); - GpuListIndex { + GpuArrayBufferIndex { index, dynamic_offset: None, element_type: PhantomData, @@ -65,8 +65,8 @@ impl GpuList { pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { match self { - GpuList::Uniform(buffer) => buffer.write_buffer(device, queue), - GpuList::Storage((buffer, vec)) => { + GpuArrayBuffer::Uniform(buffer) => buffer.write_buffer(device, queue), + GpuArrayBuffer::Storage((buffer, vec)) => { buffer.set(mem::take(vec)); buffer.write_buffer(device, queue); } @@ -102,8 +102,8 @@ impl GpuList { pub fn binding(&self) -> Option { match self { - GpuList::Uniform(buffer) => buffer.binding(), - GpuList::Storage((buffer, _)) => buffer.binding(), + GpuArrayBuffer::Uniform(buffer) => buffer.binding(), + GpuArrayBuffer::Storage((buffer, _)) => buffer.binding(), } } @@ -117,9 +117,9 @@ impl GpuList { } } -/// An index into a [`GpuList`] for a given element. +/// An index into a [`GpuArrayBuffer`] for a given element. #[derive(Component)] -pub struct GpuListIndex { +pub struct GpuArrayBufferIndex { /// The index to use in a shader into the array. pub index: u32, /// The dynamic offset to use when setting the bind group in a pass. diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index 70c5428795efe..f16f5f1269929 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -3,7 +3,7 @@ mod bind_group; mod bind_group_layout; mod buffer; mod buffer_vec; -mod gpu_list; +mod gpu_array_buffer; mod pipeline; mod pipeline_cache; mod pipeline_specializer; @@ -17,7 +17,7 @@ pub use bind_group::*; pub use bind_group_layout::*; pub use buffer::*; pub use buffer_vec::*; -pub use gpu_list::*; +pub use gpu_array_buffer::*; pub use pipeline::*; pub use pipeline_cache::*; pub use pipeline_specializer::*; diff --git a/crates/bevy_render/src/render_resource/storage_buffer.rs b/crates/bevy_render/src/render_resource/storage_buffer.rs index e0c046bf5735d..2c73b322d79b5 100644 --- a/crates/bevy_render/src/render_resource/storage_buffer.rs +++ b/crates/bevy_render/src/render_resource/storage_buffer.rs @@ -25,7 +25,7 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// @@ -155,7 +155,7 @@ impl StorageBuffer { /// * [`StorageBuffer`](crate::render_resource::StorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// diff --git a/crates/bevy_render/src/render_resource/uniform_buffer.rs b/crates/bevy_render/src/render_resource/uniform_buffer.rs index cfe5842815f51..4c1ad61b2aeb8 100644 --- a/crates/bevy_render/src/render_resource/uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/uniform_buffer.rs @@ -25,7 +25,7 @@ use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsa /// * [`StorageBuffer`](crate::render_resource::StorageBuffer) /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// @@ -152,7 +152,7 @@ impl UniformBuffer { /// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) /// * [`UniformBuffer`](crate::render_resource::UniformBuffer) /// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) -/// * [`GpuList`](crate::render_resource::GpuList) +/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer) /// * [`BufferVec`](crate::render_resource::BufferVec) /// * [`Texture`](crate::render_resource::Texture) /// From d65cab47a115fe252da7d702106f1975f5d9f6fa Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 26 Jun 2023 15:12:46 -0400 Subject: [PATCH 22/24] Update crates/bevy_render/src/render_resource/gpu_array_buffer.rs Co-authored-by: IceSentry --- crates/bevy_render/src/render_resource/gpu_array_buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/gpu_array_buffer.rs b/crates/bevy_render/src/render_resource/gpu_array_buffer.rs index 0954daf2592ac..45eaba4f73246 100644 --- a/crates/bevy_render/src/render_resource/gpu_array_buffer.rs +++ b/crates/bevy_render/src/render_resource/gpu_array_buffer.rs @@ -12,7 +12,7 @@ use wgpu::{BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType pub trait GpuArrayBufferable: ShaderType + ShaderSize + WriteInto + Clone {} impl GpuArrayBufferable for T {} -/// Stores a list of elements to be transferred to the GPU and made accessible to shaders as a read-only array. +/// Stores an array of elements to be transferred to the GPU and made accessible to shaders as a read-only array. /// /// On platforms that support storage buffers, this is equivalent to [`StorageBuffer>`]. /// Otherwise, this falls back to a dynamic offset uniform buffer with the largest From 499e3a291ddc296e184c96a6c237a6bcdee881eb Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Fri, 21 Jul 2023 18:16:16 +0200 Subject: [PATCH 23/24] Add internal documentation of BatchedUniformBuffer members --- .../src/render_resource/batched_uniform_buffer.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 637dc8cb490bc..4e4e0141f5922 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -32,7 +32,12 @@ const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 12; /// offsets to bind group commands, and if indices into the array can be passed /// in via other means, it enables batching of draw commands. pub struct BatchedUniformBuffer { + // Batches of fixed-size arrays of T are written to this buffer so that + // each batch in a fixed-size array can be bound at a dynamic offset. uniforms: DynamicUniformBuffer>>, + // A batch of T are gathered into this `MaxCapacityArray` until it is full, + // then it is written into the `DynamicUniformBuffer`, cleared, and new T + // are gathered here, and so on for each batch. temp: MaxCapacityArray>, current_offset: u32, dynamic_offset_alignment: u32, From df66b85e5acd94c5729c9141311782cf44d80be8 Mon Sep 17 00:00:00 2001 From: Vincent <9408210+konsolas@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:24:36 +0200 Subject: [PATCH 24/24] BatchedUniformBuffer: Optimize rounding code Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> --- .../src/render_resource/batched_uniform_buffer.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs index 4e4e0141f5922..a9fba2ac7fb42 100644 --- a/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs +++ b/crates/bevy_render/src/render_resource/batched_uniform_buffer.rs @@ -91,7 +91,7 @@ impl BatchedUniformBuffer { self.uniforms.push(self.temp.clone()); self.current_offset += - round_up(self.temp.size().get(), self.dynamic_offset_alignment as u64) as u32; + align_to_next(self.temp.size().get(), self.dynamic_offset_alignment as u64) as u32; self.temp.0.clear(); } @@ -115,8 +115,9 @@ impl BatchedUniformBuffer { } #[inline] -fn round_up(v: u64, a: u64) -> u64 { - ((v + a - 1) / a) * a +fn align_to_next(value: u64, alignment: u64) -> u64 { + debug_assert!(alignment & (alignment - 1) == 0); + ((value - 1) | (alignment - 1)) + 1 } // ----------------------------------------------------------------------------