Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Deref(Mut) on BufferVec/UniformVec #3532

Closed
wants to merge 15 commits into from
5 changes: 2 additions & 3 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ impl ViewClusterBindings {
let component = self.n_offsets & ((1 << 2) - 1);
let packed = pack_offset_and_count(offset, count);

self.cluster_offsets_and_counts.get_mut(0)[array_index][component] = packed;
self.cluster_offsets_and_counts[0][array_index][component] = packed;

self.n_offsets += 1;
}
Expand All @@ -946,8 +946,7 @@ impl ViewClusterBindings {
let sub_index = self.n_indices & ((1 << 2) - 1);
let index = index as u32 & POINT_LIGHT_INDEX_MASK;

self.cluster_light_index_lists.get_mut(0)[array_index][component] |=
index << (8 * sub_index);
self.cluster_light_index_lists[0][array_index][component] |= index << (8 * sub_index);

self.n_indices += 1;
}
Expand Down
69 changes: 33 additions & 36 deletions crates/bevy_render/src/render_resource/buffer_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use crate::{
renderer::{RenderDevice, RenderQueue},
};
use bevy_core::{cast_slice, Pod};
use copyless::VecHelper;
use std::ops::{Deref, DerefMut};
use wgpu::BufferUsages;

/// A user-friendly wrapper around a [`Buffer`] that provides a `Vec`-like
/// interface for constructing the buffer.
pub struct BufferVec<T: Pod> {
values: Vec<T>,
james7132 marked this conversation as resolved.
Show resolved Hide resolved
buffer: Option<Buffer>,
capacity: usize,
item_size: usize,
james7132 marked this conversation as resolved.
Show resolved Hide resolved
buffer_usage: BufferUsages,
}

Expand All @@ -21,49 +22,48 @@ impl<T: Pod> Default for BufferVec<T> {
buffer: None,
capacity: 0,
buffer_usage: BufferUsages::all(),
item_size: std::mem::size_of::<T>(),
}
}
}

impl<T: Pod> BufferVec<T> {
/// Creates a new [`BufferVec`] with the associated [`BufferUsages`].
///
/// This does not immediately allocate a buffer.
james7132 marked this conversation as resolved.
Show resolved Hide resolved
pub fn new(buffer_usage: BufferUsages) -> Self {
Self {
buffer_usage,
..Default::default()
}
}

/// Gets the reference to the underlying buffer, if one has been allocated.
#[inline]
pub fn buffer(&self) -> Option<&Buffer> {
self.buffer.as_ref()
}

james7132 marked this conversation as resolved.
Show resolved Hide resolved
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
james7132 marked this conversation as resolved.
Show resolved Hide resolved

#[inline]
pub fn len(&self) -> usize {
self.values.len()
}

#[inline]
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}

pub fn push(&mut self, value: T) -> usize {
let index = self.values.len();
self.values.alloc().init(value);
index
/// Queues up a copy of the contents of the [`BufferVec`] into the underlying
/// buffer.
///
/// If no buffer has been allocated yet or if the current size of the contents
/// exceeds the size of the underlying buffer, a new buffer will be allocated.
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
james7132 marked this conversation as resolved.
Show resolved Hide resolved
if self.values.is_empty() {
return;
}
self.reserve_buffer(self.values.len(), device);
if let Some(buffer) = &self.buffer {
let range = 0..std::mem::size_of::<T>() * self.values.len();
james7132 marked this conversation as resolved.
Show resolved Hide resolved
let bytes: &[u8] = cast_slice(&self.values);
queue.write_buffer(buffer, 0, &bytes[range]);
}
}

pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
fn reserve_buffer(&mut self, capacity: usize, device: &RenderDevice) {
if capacity > self.capacity {
self.capacity = capacity;
let size = self.item_size * capacity;
let size = std::mem::size_of::<T>() * capacity;
james7132 marked this conversation as resolved.
Show resolved Hide resolved
self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: size as wgpu::BufferAddress,
Expand All @@ -72,20 +72,17 @@ impl<T: Pod> BufferVec<T> {
}));
}
}
}

pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
if self.values.is_empty() {
return;
}
self.reserve(self.values.len(), device);
if let Some(buffer) = &self.buffer {
let range = 0..self.item_size * self.values.len();
let bytes: &[u8] = cast_slice(&self.values);
queue.write_buffer(buffer, 0, &bytes[range]);
}
impl<T: Pod> Deref for BufferVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.values
}
}

pub fn clear(&mut self) {
self.values.clear();
impl<T: Pod> DerefMut for BufferVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.values
}
}
120 changes: 68 additions & 52 deletions crates/bevy_render/src/render_resource/uniform_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@ use crate::{
render_resource::Buffer,
renderer::{RenderDevice, RenderQueue},
};
use std::num::NonZeroU64;
use std::{
num::NonZeroU64,
ops::{Deref, DerefMut},
};
use wgpu::{BindingResource, BufferBinding, BufferDescriptor, BufferUsages};

/// A user-friendly wrapper around a [`Buffer`] that provides a `Vec`-like
/// interface for constructing the buffer.
///
/// Intended strictly for use with uniform buffers. For other use cases,
/// see [`BufferVec`][buffervec] instead.
///
/// [buffervec]: crate::render_resource::BufferVec
pub struct UniformVec<T: AsStd140> {
values: Vec<T>,
scratch: Vec<u8>,
uniform_buffer: Option<Buffer>,
capacity: usize,
item_size: usize,
}

impl<T: AsStd140> Default for UniformVec<T> {
Expand All @@ -20,57 +28,68 @@ impl<T: AsStd140> Default for UniformVec<T> {
values: Vec::new(),
scratch: Vec::new(),
uniform_buffer: None,
capacity: 0,
item_size: (T::std140_size_static() + <T as AsStd140>::Output::ALIGNMENT - 1)
& !(<T as AsStd140>::Output::ALIGNMENT - 1),
}
}
}

impl<T: AsStd140> UniformVec<T> {
const ITEM_SIZE: usize =
(std::mem::size_of::<T::Output>() + <T as AsStd140>::Output::ALIGNMENT - 1)
& !(<T as AsStd140>::Output::ALIGNMENT - 1);

/// Gets the reference to the underlying buffer, if one has been allocated.
#[inline]
pub fn uniform_buffer(&self) -> Option<&Buffer> {
pub fn buffer(&self) -> Option<&Buffer> {
self.uniform_buffer.as_ref()
}

/// Creates a binding for the underlying buffer.
/// Returns `None` if no buffer has been allocated.
#[inline]
pub fn binding(&self) -> Option<BindingResource> {
Some(BindingResource::Buffer(BufferBinding {
buffer: self.uniform_buffer()?,
buffer: self.buffer()?,
offset: 0,
size: Some(NonZeroU64::new(self.item_size as u64).unwrap()),
size: Some(NonZeroU64::new(Self::ITEM_SIZE as u64).unwrap()),
}))
}

/// Gets the capacity of the underlying buffer.
///
/// Will return 0 if no buffer has been allocated yet.
#[inline]
pub fn len(&self) -> usize {
pub fn buffer_capacity(&self) -> usize {
self.values.len()
}

#[inline]
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}

#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}

pub fn push(&mut self, value: T) -> usize {
let index = self.values.len();
self.values.push(value);
index
}
james7132 marked this conversation as resolved.
Show resolved Hide resolved

james7132 marked this conversation as resolved.
Show resolved Hide resolved
pub fn get_mut(&mut self, index: usize) -> &mut T {
&mut self.values[index]
/// Queues up a copy of the contents of the [`UniformVec`] into the underlying
/// buffer.
///
/// If no buffer has been allocated yet or if the current size of the contents
/// exceeds the size of the underlying buffer, a new buffer will be allocated.
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
if self.values.is_empty() {
return;
}
self.reserve_buffer(self.values.len(), device);
james7132 marked this conversation as resolved.
Show resolved Hide resolved
if let Some(uniform_buffer) = &self.uniform_buffer {
let range = 0..Self::ITEM_SIZE * self.values.len();
james7132 marked this conversation as resolved.
Show resolved Hide resolved
let mut writer = std140::Writer::new(&mut self.scratch[range.clone()]);
writer.write(self.values.as_slice()).unwrap();
queue.write_buffer(uniform_buffer, 0, &self.scratch[range]);
}
}

pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) -> bool {
if capacity > self.capacity {
fn reserve_buffer(&mut self, capacity: usize, device: &RenderDevice) -> bool {
if capacity > self.scratch.len() {
self.capacity = capacity;
james7132 marked this conversation as resolved.
Show resolved Hide resolved
let size = self.item_size * capacity;
let size = Self::ITEM_SIZE * capacity;
james7132 marked this conversation as resolved.
Show resolved Hide resolved
self.scratch.resize(size, 0);
self.uniform_buffer = Some(device.create_buffer(&BufferDescriptor {
label: None,
Expand All @@ -83,26 +102,18 @@ impl<T: AsStd140> UniformVec<T> {
false
}
}
}

pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
if self.values.is_empty() {
return;
}
self.reserve(self.values.len(), device);
if let Some(uniform_buffer) = &self.uniform_buffer {
let range = 0..self.item_size * self.values.len();
let mut writer = std140::Writer::new(&mut self.scratch[range.clone()]);
writer.write(self.values.as_slice()).unwrap();
queue.write_buffer(uniform_buffer, 0, &self.scratch[range]);
}
}

pub fn clear(&mut self) {
self.values.clear();
impl<T: AsStd140> Deref for UniformVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.values
}
}

pub fn values(&self) -> &[T] {
&self.values
impl<T: AsStd140> DerefMut for UniformVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.values
}
}

Expand All @@ -119,11 +130,14 @@ impl<T: AsStd140> Default for DynamicUniformVec<T> {
}

impl<T: AsStd140> DynamicUniformVec<T> {
/// Gets the reference to the underlying buffer, if one has been allocated.
#[inline]
pub fn uniform_buffer(&self) -> Option<&Buffer> {
self.uniform_vec.uniform_buffer()
pub fn buffer(&self) -> Option<&Buffer> {
self.uniform_vec.buffer()
}

/// Creates a binding for the underlying buffer.
/// Returns `None` if no buffer has been allocated.
#[inline]
pub fn binding(&self) -> Option<BindingResource> {
self.uniform_vec.binding()
Expand All @@ -139,21 +153,23 @@ impl<T: AsStd140> DynamicUniformVec<T> {
self.uniform_vec.is_empty()
}

/// Gets the capacity of the underlying buffer, in bytes.
james7132 marked this conversation as resolved.
Show resolved Hide resolved
#[inline]
pub fn capacity(&self) -> usize {
self.uniform_vec.capacity()
pub fn buffer_capacity(&self) -> usize {
self.uniform_vec.buffer_capacity()
}

#[inline]
pub fn push(&mut self, value: T) -> u32 {
james7132 marked this conversation as resolved.
Show resolved Hide resolved
(self.uniform_vec.push(DynamicUniform(value)) * self.uniform_vec.item_size) as u32
}

#[inline]
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
self.uniform_vec.reserve(capacity, device);
(self.uniform_vec.push(DynamicUniform(value)) * UniformVec::<DynamicUniform<T>>::ITEM_SIZE)
as u32
}

/// Queues up a copy of the contents of the [`UniformVec`] into the underlying
/// buffer.
///
/// If no buffer has been allocated yet or if the current size of the contents
/// exceeds the size of the underlying buffer, a new buffer will be allocated.
#[inline]
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
self.uniform_vec.write_buffer(device, queue);
Expand Down