wrgpgpu 0.1.5

Wren's library for GPGPU compute shaders, based on WGPU
Documentation
use std::{marker::PhantomData, mem, num::NonZero, sync::Arc};

use wgpu::util::{DeviceExt, DownloadBuffer};

use super::Bind;

/// Type of buffer binding
pub trait BufferBindType: std::fmt::Debug {
	fn buffer_binding_type() -> wgpu::BufferBindingType;
	fn buffer_usages() -> wgpu::BufferUsages;
}

#[derive(Debug)]
/// Uniform Binding type.
///
/// See also: [`UniformBufferBind`]
pub struct UniformType;

#[derive(Debug)]
/// Readonly Storage Binding type.
///
/// See also: [`StorageReadBufferBind`]
pub struct StorageReadOnlyType;

#[derive(Debug)]
/// Storage Binding type.
///
/// See also: [`StorageBufferBind`]
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
	}
}

/// A buffer binding
pub struct BufferBind<T: bytemuck::Pod + bytemuck::Zeroable, Y: BufferBindType> {
	binding_offset: usize,
	binding: wgpu::Buffer,
	_data: PhantomData<T>,
	_ty: PhantomData<Y>,
}

/// A buffer binding as a uniform buffer
pub type UniformBufferBind<T> = BufferBind<T, UniformType>;
/// A buffer binding as a readonly storage buffer
///
/// This can store more data than a [`UniformBufferBind`], but is also much slower, and potentially
/// has other limitations depending on the GPU. See webgpu spec for more information.
pub type StorageReadBufferBind<T> = BufferBind<T, StorageReadOnlyType>;
/// A buffer binding as a read-write storage buffer
///
/// This is the only buffer binding type that is writable
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,
		}
	}
}