use std::{marker::PhantomData, mem, num::NonZero, sync::Arc};
use wgpu::util::{DeviceExt, DownloadBuffer};
use super::Bind;
pub trait BufferBindType: std::fmt::Debug {
fn buffer_binding_type() -> wgpu::BufferBindingType;
fn buffer_usages() -> wgpu::BufferUsages;
}
#[derive(Debug)]
pub struct UniformType;
#[derive(Debug)]
pub struct StorageReadOnlyType;
#[derive(Debug)]
pub struct StorageReadWriteType;
impl BufferBindType for UniformType {
fn buffer_binding_type() -> wgpu::BufferBindingType {
wgpu::BufferBindingType::Uniform
}
fn buffer_usages() -> wgpu::BufferUsages {
wgpu::BufferUsages::UNIFORM
}
}
impl BufferBindType for StorageReadOnlyType {
fn buffer_binding_type() -> wgpu::BufferBindingType {
wgpu::BufferBindingType::Storage { read_only: true }
}
fn buffer_usages() -> wgpu::BufferUsages {
wgpu::BufferUsages::STORAGE
}
}
impl BufferBindType for StorageReadWriteType {
fn buffer_binding_type() -> wgpu::BufferBindingType {
wgpu::BufferBindingType::Storage { read_only: false }
}
fn buffer_usages() -> wgpu::BufferUsages {
wgpu::BufferUsages::STORAGE
}
}
pub struct BufferBind<T: bytemuck::Pod + bytemuck::Zeroable, Y: BufferBindType> {
binding_offset: usize,
binding: wgpu::Buffer,
_data: PhantomData<T>,
_ty: PhantomData<Y>,
}
pub type UniformBufferBind<T> = BufferBind<T, UniformType>;
pub type StorageReadBufferBind<T> = BufferBind<T, StorageReadOnlyType>;
pub type StorageBufferBind<T> = BufferBind<T, StorageReadWriteType>;
impl<T: bytemuck::Pod + bytemuck::Zeroable, Y: BufferBindType> Bind for BufferBind<T, Y> {
type Data = T;
type CreateInfo = ();
fn binding_type() -> wgpu::BindingType {
wgpu::BindingType::Buffer {
ty: Y::buffer_binding_type(),
has_dynamic_offset: false,
min_binding_size: None,
}
}
fn bind(&self) -> wgpu::BindingResource {
wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &self.binding,
offset: self.binding_offset as u64,
size: Some(NonZero::new(mem::size_of::<T>() as u64).unwrap()),
})
}
fn download(&self, device: &crate::Device) -> Self::Data {
let buffer_content = Arc::new(std::sync::Mutex::new(Some(Vec::new())));
let buffer_content_clone = buffer_content.clone();
DownloadBuffer::read_buffer(
&device.device,
&device.queue,
&self.binding.slice(..),
move |download_buffer| {
buffer_content_clone
.lock()
.unwrap()
.as_mut()
.unwrap()
.extend_from_slice(&download_buffer.unwrap());
},
);
device.device.poll(wgpu::Maintain::Wait);
let data = buffer_content.lock().unwrap().take().unwrap();
bytemuck::cast_slice::<u8, T>(data.as_slice())[0]
}
fn new_empty(device: &crate::Device, _ci: ()) -> Self {
Self {
binding_offset: 0,
binding: device.device.create_buffer(&wgpu::BufferDescriptor {
label: Some(std::any::type_name::<T>()),
size: mem::size_of::<T>() as u64,
usage: wgpu::BufferUsages::COPY_SRC
| wgpu::BufferUsages::COPY_DST
| Y::buffer_usages(),
mapped_at_creation: false,
}),
_data: PhantomData,
_ty: PhantomData,
}
}
fn new_init(device: &crate::Device, data: T) -> Self {
Self {
binding_offset: 0,
binding: device
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(std::any::type_name::<T>()),
usage: wgpu::BufferUsages::COPY_SRC
| wgpu::BufferUsages::COPY_DST
| Y::buffer_usages(),
contents: bytemuck::cast_slice(&[data]),
}),
_data: PhantomData,
_ty: PhantomData,
}
}
}