use core::marker::PhantomData;
use super::Buffer;
use crate::renderer::{RenderDevice, RenderQueue};
use encase::{
internal::WriteInto, DynamicStorageBuffer as DynamicStorageBufferWrapper, ShaderType,
StorageBuffer as StorageBufferWrapper,
};
use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferSize, BufferUsages};
use super::IntoBinding;
pub struct StorageBuffer<T: ShaderType> {
value: T,
scratch: StorageBufferWrapper<Vec<u8>>,
buffer: Option<Buffer>,
label: Option<String>,
changed: bool,
buffer_usage: BufferUsages,
last_written_size: Option<BufferSize>,
}
impl<T: ShaderType> From<T> for StorageBuffer<T> {
fn from(value: T) -> Self {
Self {
value,
scratch: StorageBufferWrapper::new(Vec::new()),
buffer: None,
label: None,
changed: false,
buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
last_written_size: None,
}
}
}
impl<T: ShaderType + Default> Default for StorageBuffer<T> {
fn default() -> Self {
Self {
value: T::default(),
scratch: StorageBufferWrapper::new(Vec::new()),
buffer: None,
label: None,
changed: false,
buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
last_written_size: None,
}
}
}
impl<T: ShaderType + WriteInto> StorageBuffer<T> {
#[inline]
pub fn buffer(&self) -> Option<&Buffer> {
self.buffer.as_ref()
}
#[inline]
pub fn binding(&self) -> Option<BindingResource<'_>> {
Some(BindingResource::Buffer(BufferBinding {
buffer: self.buffer()?,
offset: 0,
size: self.last_written_size,
}))
}
pub fn set(&mut self, value: T) {
self.value = value;
}
pub fn get(&self) -> &T {
&self.value
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.value
}
pub fn set_label(&mut self, label: Option<&str>) {
let label = label.map(str::to_string);
if label != self.label {
self.changed = true;
}
self.label = label;
}
pub fn get_label(&self) -> Option<&str> {
self.label.as_deref()
}
pub fn add_usages(&mut self, usage: BufferUsages) {
self.buffer_usage |= usage;
self.changed = true;
}
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
self.scratch.write(&self.value).unwrap();
let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);
let size = self.scratch.as_ref().len() as u64;
if capacity < size || self.changed {
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
label: self.label.as_deref(),
usage: self.buffer_usage,
contents: self.scratch.as_ref(),
}));
self.changed = false;
} else if let Some(buffer) = &self.buffer {
queue.write_buffer(buffer, 0, self.scratch.as_ref());
}
self.last_written_size = BufferSize::new(size);
}
}
impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a StorageBuffer<T> {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
self.binding().expect("Failed to get buffer")
}
}
pub struct DynamicStorageBuffer<T: ShaderType> {
scratch: DynamicStorageBufferWrapper<Vec<u8>>,
buffer: Option<Buffer>,
label: Option<String>,
changed: bool,
buffer_usage: BufferUsages,
last_written_size: Option<BufferSize>,
_marker: PhantomData<fn() -> T>,
}
impl<T: ShaderType> Default for DynamicStorageBuffer<T> {
fn default() -> Self {
Self {
scratch: DynamicStorageBufferWrapper::new(Vec::new()),
buffer: None,
label: None,
changed: false,
buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
last_written_size: None,
_marker: PhantomData,
}
}
}
impl<T: ShaderType + WriteInto> DynamicStorageBuffer<T> {
#[inline]
pub fn buffer(&self) -> Option<&Buffer> {
self.buffer.as_ref()
}
#[inline]
pub fn binding(&self) -> Option<BindingResource<'_>> {
Some(BindingResource::Buffer(BufferBinding {
buffer: self.buffer()?,
offset: 0,
size: self.last_written_size,
}))
}
#[inline]
pub fn is_empty(&self) -> bool {
self.scratch.as_ref().is_empty()
}
#[inline]
pub fn push(&mut self, value: T) -> u32 {
self.scratch.write(&value).unwrap() as u32
}
pub fn set_label(&mut self, label: Option<&str>) {
let label = label.map(str::to_string);
if label != self.label {
self.changed = true;
}
self.label = label;
}
pub fn get_label(&self) -> Option<&str> {
self.label.as_deref()
}
pub fn add_usages(&mut self, usage: BufferUsages) {
self.buffer_usage |= usage;
self.changed = true;
}
#[inline]
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);
let size = self.scratch.as_ref().len() as u64;
if capacity < size || (self.changed && size > 0) {
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
label: self.label.as_deref(),
usage: self.buffer_usage,
contents: self.scratch.as_ref(),
}));
self.changed = false;
} else if let Some(buffer) = &self.buffer {
queue.write_buffer(buffer, 0, self.scratch.as_ref());
}
self.last_written_size = BufferSize::new(size);
}
#[inline]
pub fn clear(&mut self) {
self.scratch.as_mut().clear();
self.scratch.set_offset(0);
}
}
impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicStorageBuffer<T> {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
self.binding().expect("Failed to get buffer")
}
}