Path: blob/main/crates/bevy_render/src/render_resource/uniform_buffer.rs
6596 views
use core::{marker::PhantomData, num::NonZero};12use crate::{3render_resource::Buffer,4renderer::{RenderDevice, RenderQueue},5};6use encase::{7internal::{AlignmentValue, BufferMut, WriteInto},8DynamicUniformBuffer as DynamicUniformBufferWrapper, ShaderType,9UniformBuffer as UniformBufferWrapper,10};11use wgpu::{12util::BufferInitDescriptor, BindingResource, BufferBinding, BufferDescriptor, BufferUsages,13};1415use super::IntoBinding;1617/// Stores data to be transferred to the GPU and made accessible to shaders as a uniform buffer.18///19/// Uniform buffers are available to shaders on a read-only basis. Uniform buffers are commonly used to make available to shaders20/// parameters that are constant during shader execution, and are best used for data that is relatively small in size as they are21/// only guaranteed to support up to 16kB per binding.22///23/// The contained data is stored in system RAM. [`write_buffer`](UniformBuffer::write_buffer) queues24/// copying of the data from system RAM to VRAM. Data in uniform buffers must follow [std140 alignment/padding requirements],25/// which is automatically enforced by this structure. Per the WGPU spec, uniform buffers cannot store runtime-sized array26/// (vectors), or structures with fields that are vectors.27///28/// Other options for storing GPU-accessible data are:29/// * [`BufferVec`](crate::render_resource::BufferVec)30/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer)31/// * [`DynamicUniformBuffer`]32/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)33/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)34/// * [`StorageBuffer`](crate::render_resource::StorageBuffer)35/// * [`Texture`](crate::render_resource::Texture)36///37/// [std140 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-uniform38pub struct UniformBuffer<T: ShaderType> {39value: T,40scratch: UniformBufferWrapper<Vec<u8>>,41buffer: Option<Buffer>,42label: Option<String>,43changed: bool,44buffer_usage: BufferUsages,45}4647impl<T: ShaderType> From<T> for UniformBuffer<T> {48fn from(value: T) -> Self {49Self {50value,51scratch: UniformBufferWrapper::new(Vec::new()),52buffer: None,53label: None,54changed: false,55buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,56}57}58}5960impl<T: ShaderType + Default> Default for UniformBuffer<T> {61fn default() -> Self {62Self {63value: T::default(),64scratch: UniformBufferWrapper::new(Vec::new()),65buffer: None,66label: None,67changed: false,68buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,69}70}71}7273impl<T: ShaderType + WriteInto> UniformBuffer<T> {74#[inline]75pub fn buffer(&self) -> Option<&Buffer> {76self.buffer.as_ref()77}7879#[inline]80pub fn binding(&self) -> Option<BindingResource<'_>> {81Some(BindingResource::Buffer(82self.buffer()?.as_entire_buffer_binding(),83))84}8586/// Set the data the buffer stores.87pub fn set(&mut self, value: T) {88self.value = value;89}9091pub fn get(&self) -> &T {92&self.value93}9495pub fn get_mut(&mut self) -> &mut T {96&mut self.value97}9899pub fn set_label(&mut self, label: Option<&str>) {100let label = label.map(str::to_string);101102if label != self.label {103self.changed = true;104}105106self.label = label;107}108109pub fn get_label(&self) -> Option<&str> {110self.label.as_deref()111}112113/// Add more [`BufferUsages`] to the buffer.114///115/// This method only allows addition of flags to the default usage flags.116///117/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::UNIFORM`.118pub fn add_usages(&mut self, usage: BufferUsages) {119self.buffer_usage |= usage;120self.changed = true;121}122123/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]124/// and the provided [`RenderQueue`], if a GPU-side backing buffer already exists.125///126/// If a GPU-side buffer does not already exist for this data, such a buffer is initialized with currently127/// available data.128pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {129self.scratch.write(&self.value).unwrap();130131if self.changed || self.buffer.is_none() {132self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {133label: self.label.as_deref(),134usage: self.buffer_usage,135contents: self.scratch.as_ref(),136}));137self.changed = false;138} else if let Some(buffer) = &self.buffer {139queue.write_buffer(buffer, 0, self.scratch.as_ref());140}141}142}143144impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a UniformBuffer<T> {145#[inline]146fn into_binding(self) -> BindingResource<'a> {147self.buffer()148.expect("Failed to get buffer")149.as_entire_buffer_binding()150.into_binding()151}152}153154/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic uniform buffer.155///156/// Dynamic uniform buffers are available to shaders on a read-only basis. Dynamic uniform buffers are commonly used to make157/// available to shaders runtime-sized arrays of parameters that are otherwise constant during shader execution, and are best158/// suited to data that is relatively small in size as they are only guaranteed to support up to 16kB per binding.159///160/// The contained data is stored in system RAM. [`write_buffer`](DynamicUniformBuffer::write_buffer) queues161/// copying of the data from system RAM to VRAM. Data in uniform buffers must follow [std140 alignment/padding requirements],162/// which is automatically enforced by this structure. Per the WGPU spec, uniform buffers cannot store runtime-sized array163/// (vectors), or structures with fields that are vectors.164///165/// Other options for storing GPU-accessible data are:166/// * [`BufferVec`](crate::render_resource::BufferVec)167/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer)168/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)169/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)170/// * [`StorageBuffer`](crate::render_resource::StorageBuffer)171/// * [`Texture`](crate::render_resource::Texture)172/// * [`UniformBuffer`]173///174/// [std140 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-uniform175pub struct DynamicUniformBuffer<T: ShaderType> {176scratch: DynamicUniformBufferWrapper<Vec<u8>>,177buffer: Option<Buffer>,178label: Option<String>,179changed: bool,180buffer_usage: BufferUsages,181_marker: PhantomData<fn() -> T>,182}183184impl<T: ShaderType> Default for DynamicUniformBuffer<T> {185fn default() -> Self {186Self {187scratch: DynamicUniformBufferWrapper::new(Vec::new()),188buffer: None,189label: None,190changed: false,191buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,192_marker: PhantomData,193}194}195}196197impl<T: ShaderType + WriteInto> DynamicUniformBuffer<T> {198pub fn new_with_alignment(alignment: u64) -> Self {199Self {200scratch: DynamicUniformBufferWrapper::new_with_alignment(Vec::new(), alignment),201buffer: None,202label: None,203changed: false,204buffer_usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,205_marker: PhantomData,206}207}208209#[inline]210pub fn buffer(&self) -> Option<&Buffer> {211self.buffer.as_ref()212}213214#[inline]215pub fn binding(&self) -> Option<BindingResource<'_>> {216Some(BindingResource::Buffer(BufferBinding {217buffer: self.buffer()?,218offset: 0,219size: Some(T::min_size()),220}))221}222223#[inline]224pub fn is_empty(&self) -> bool {225self.scratch.as_ref().is_empty()226}227228/// Push data into the `DynamicUniformBuffer`'s internal vector (residing on system RAM).229#[inline]230pub fn push(&mut self, value: &T) -> u32 {231self.scratch.write(value).unwrap() as u32232}233234pub fn set_label(&mut self, label: Option<&str>) {235let label = label.map(str::to_string);236237if label != self.label {238self.changed = true;239}240241self.label = label;242}243244pub fn get_label(&self) -> Option<&str> {245self.label.as_deref()246}247248/// Add more [`BufferUsages`] to the buffer.249///250/// This method only allows addition of flags to the default usage flags.251///252/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::UNIFORM`.253pub fn add_usages(&mut self, usage: BufferUsages) {254self.buffer_usage |= usage;255self.changed = true;256}257258/// Creates a writer that can be used to directly write elements into the target buffer.259///260/// This method uses less memory and performs fewer memory copies using over [`push`] and [`write_buffer`].261///262/// `max_count` *must* be greater than or equal to the number of elements that are to be written to the buffer, or263/// the writer will panic while writing. Dropping the writer will schedule the buffer write into the provided264/// [`RenderQueue`].265///266/// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously267/// allocated does not have enough capacity to hold `max_count` elements, a new GPU-side buffer is created.268///269/// Returns `None` if there is no allocated GPU-side buffer, and `max_count` is 0.270///271/// [`push`]: Self::push272/// [`write_buffer`]: Self::write_buffer273#[inline]274pub fn get_writer<'a>(275&'a mut self,276max_count: usize,277device: &RenderDevice,278queue: &'a RenderQueue,279) -> Option<DynamicUniformBufferWriter<'a, T>> {280let alignment = if cfg!(target_abi = "sim") {281// On iOS simulator on silicon macs, metal validation check that the host OS alignment282// is respected, but the device reports the correct value for iOS, which is smaller.283// Use the larger value.284// See https://github.com/gfx-rs/wgpu/issues/7057 - remove if it's not needed anymore.285AlignmentValue::new(256)286} else {287AlignmentValue::new(device.limits().min_uniform_buffer_offset_alignment as u64)288};289290let mut capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);291let size = alignment292.round_up(T::min_size().get())293.checked_mul(max_count as u64)294.unwrap();295296if capacity < size || (self.changed && size > 0) {297let buffer = device.create_buffer(&BufferDescriptor {298label: self.label.as_deref(),299usage: self.buffer_usage,300size,301mapped_at_creation: false,302});303capacity = buffer.size();304self.buffer = Some(buffer);305self.changed = false;306}307308if let Some(buffer) = self.buffer.as_deref() {309let buffer_view = queue310.write_buffer_with(buffer, 0, NonZero::<u64>::new(buffer.size())?)311.unwrap();312Some(DynamicUniformBufferWriter {313buffer: encase::DynamicUniformBuffer::new_with_alignment(314QueueWriteBufferViewWrapper {315capacity: capacity as usize,316buffer_view,317},318alignment.get(),319),320_marker: PhantomData,321})322} else {323None324}325}326327/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]328/// and the provided [`RenderQueue`].329///330/// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously331/// allocated does not have enough capacity, a new GPU-side buffer is created.332#[inline]333pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {334let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);335let size = self.scratch.as_ref().len() as u64;336337if capacity < size || (self.changed && size > 0) {338self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {339label: self.label.as_deref(),340usage: self.buffer_usage,341contents: self.scratch.as_ref(),342}));343self.changed = false;344} else if let Some(buffer) = &self.buffer {345queue.write_buffer(buffer, 0, self.scratch.as_ref());346}347}348349#[inline]350pub fn clear(&mut self) {351self.scratch.as_mut().clear();352self.scratch.set_offset(0);353}354}355356/// A writer that can be used to directly write elements into the target buffer.357///358/// For more information, see [`DynamicUniformBuffer::get_writer`].359pub struct DynamicUniformBufferWriter<'a, T> {360buffer: encase::DynamicUniformBuffer<QueueWriteBufferViewWrapper<'a>>,361_marker: PhantomData<fn() -> T>,362}363364impl<'a, T: ShaderType + WriteInto> DynamicUniformBufferWriter<'a, T> {365pub fn write(&mut self, value: &T) -> u32 {366self.buffer.write(value).unwrap() as u32367}368}369370/// A wrapper to work around the orphan rule so that [`wgpu::QueueWriteBufferView`] can implement371/// [`BufferMut`].372struct QueueWriteBufferViewWrapper<'a> {373buffer_view: wgpu::QueueWriteBufferView<'a>,374// Must be kept separately and cannot be retrieved from buffer_view, as the read-only access will375// invoke a panic.376capacity: usize,377}378379impl<'a> BufferMut for QueueWriteBufferViewWrapper<'a> {380#[inline]381fn capacity(&self) -> usize {382self.capacity383}384385#[inline]386fn write<const N: usize>(&mut self, offset: usize, val: &[u8; N]) {387self.buffer_view.write(offset, val);388}389390#[inline]391fn write_slice(&mut self, offset: usize, val: &[u8]) {392self.buffer_view.write_slice(offset, val);393}394}395396impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicUniformBuffer<T> {397#[inline]398fn into_binding(self) -> BindingResource<'a> {399self.binding().unwrap()400}401}402403404