use encase::{
ShaderType, StorageBuffer, UniformBuffer,
internal::{ReadFrom, WriteInto},
};
use thiserror::Error;
use wgpu::{
BindingResource, BufferDescriptor,
util::{BufferInitDescriptor, DeviceExt},
};
use crate::Device;
#[derive(Clone, Copy)]
pub enum BufferType {
StorageBuffer {
output: bool,
read_only: bool,
},
UniformBuffer,
}
pub struct Buffer<'a> {
device: &'a Device,
buffer_type: BufferType,
contents: BufferContents,
buffer: wgpu::Buffer,
}
pub enum BufferInit<T>
where
T: ShaderType + WriteInto,
{
WithSize(usize),
WithData(T),
}
pub enum BufferContents {
Size(u32),
Data(Vec<u8>),
}
impl BufferContents {
pub fn size(&self) -> u32 {
match self {
BufferContents::Size(size) => *size,
BufferContents::Data(data) => data.len() as u32,
}
}
}
#[derive(Error, Debug)]
pub enum BufferError {
#[error("Cannot read from a non-output buffer.")]
NotOutputBuffer,
}
impl<'a> Buffer<'a> {
pub(crate) fn new(
label: Option<&str>,
device: &'a Device,
buffer_type: BufferType,
contents: BufferContents,
) -> Self {
let usage = {
let buffer_type = match buffer_type {
BufferType::StorageBuffer { .. } => wgpu::BufferUsages::STORAGE,
BufferType::UniformBuffer => wgpu::BufferUsages::UNIFORM,
};
buffer_type | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST
};
let buffer = match &contents {
BufferContents::Size(size) => device.device().create_buffer(&BufferDescriptor {
label,
size: *size as u64,
usage,
mapped_at_creation: false,
}),
BufferContents::Data(data) => {
device.device().create_buffer_init(&BufferInitDescriptor {
label,
contents: &data[..],
usage,
})
}
};
device.queue().submit([]);
Self {
device,
buffer_type,
contents,
buffer,
}
}
pub fn size(&self) -> u32 {
self.contents.size()
}
pub fn output(&self) -> bool {
matches!(
self.buffer_type,
BufferType::StorageBuffer { output: true, .. }
)
}
pub fn data(&self) -> Option<&Vec<u8>> {
match &self.contents {
BufferContents::Size(_) => None,
BufferContents::Data(data) => Some(data),
}
}
pub fn buffer_type(&self) -> BufferType {
self.buffer_type
}
pub(crate) fn as_entire_binding(&self) -> BindingResource<'_> {
self.buffer.as_entire_binding()
}
pub(crate) fn buffer(&self) -> &wgpu::Buffer {
&self.buffer
}
pub fn write<T>(&self, data: &T)
where
T: ShaderType + WriteInto,
{
let data: Vec<u8> = match self.buffer_type {
BufferType::StorageBuffer { .. } => {
let mut buffer = StorageBuffer::new(vec![]);
buffer.write(&data).unwrap();
buffer.into_inner()
}
BufferType::UniformBuffer => {
let mut buffer = UniformBuffer::new(vec![]);
buffer.write(&data).unwrap();
buffer.into_inner()
}
};
self.device.queue().write_buffer(&self.buffer, 0, &data);
self.device.queue().submit([]);
}
pub async fn read<T>(&self, output: &mut T) -> Result<(), BufferError>
where
T: ShaderType + ReadFrom,
{
if !self.output() {
return Err(BufferError::NotOutputBuffer);
}
self.device.copy_to_staging(self);
let staging = self.device.staging().borrow();
if let Some(staging) = staging.as_ref() {
let output_size = self.size() as u64;
let slice = staging.slice(..output_size);
let (tx, rx) = flume::bounded(1);
slice.map_async(wgpu::MapMode::Read, move |r| tx.send(r).unwrap());
self.device
.device()
.poll(wgpu::Maintain::wait())
.panic_on_timeout();
rx.recv_async().await.unwrap().unwrap();
{
let view = slice.get_mapped_range();
let buffer = StorageBuffer::new(&*view);
buffer.read(output).unwrap();
}
staging.unmap();
}
Ok(())
}
}