use crate::shader::{BindGroupLayoutInfo, ShaderArgsError};
use bytemuck::{AnyBitPattern, NoUninit};
use std::error::Error;
use std::ops::RangeBounds;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompileTarget {
Wgsl,
Ptx,
Spirv,
}
#[cfg(feature = "webgpu")]
pub use webgpu::WebGpu;
#[cfg(feature = "webgpu")]
mod webgpu;
#[cfg(feature = "cuda")]
pub use cuda::Cuda;
#[cfg(feature = "cuda")]
pub mod cuda;
mod any_backend;
pub use any_backend::*;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BufferUsages: u32 {
const MAP_READ = 1 << 0;
const MAP_WRITE = 1 << 1;
const COPY_SRC = 1 << 2;
const COPY_DST = 1 << 3;
const INDEX = 1 << 4;
const VERTEX = 1 << 5;
const UNIFORM = 1 << 6;
const STORAGE = 1 << 7;
const INDIRECT = 1 << 8;
const QUERY_RESOLVE = 1 << 9;
}
}
#[cfg(feature = "webgpu")]
impl From<BufferUsages> for wgpu::BufferUsages {
fn from(usage: BufferUsages) -> Self {
wgpu::BufferUsages::from_bits_truncate(usage.bits())
}
}
#[cfg(feature = "webgpu")]
impl From<wgpu::BufferUsages> for BufferUsages {
fn from(usage: wgpu::BufferUsages) -> Self {
BufferUsages::from_bits_truncate(usage.bits())
}
}
pub type BufferOptions = BufferUsages;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DescriptorType {
Uniform,
Storage {
read_only: bool,
},
}
impl DescriptorType {
pub fn storage() -> Self {
Self::Storage { read_only: false }
}
pub fn storage_readonly() -> Self {
Self::Storage { read_only: true }
}
}
impl Default for DescriptorType {
fn default() -> Self {
Self::storage()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ShaderBinding {
pub space: u32,
pub index: u32,
pub descriptor_type: DescriptorType,
}
impl From<(u32, u32)> for ShaderBinding {
fn from((space, index): (u32, u32)) -> Self {
Self {
space,
index,
descriptor_type: DescriptorType::default(),
}
}
}
pub unsafe trait DeviceValue: 'static + Clone + Copy + MaybeSendSync {}
unsafe impl<T: 'static + Clone + Copy + MaybeSendSync> DeviceValue for T {}
#[cfg(target_arch = "wasm32")]
pub trait MaybeSendSync {}
#[cfg(target_arch = "wasm32")]
impl<T> MaybeSendSync for T {}
#[cfg(not(target_arch = "wasm32"))]
pub trait MaybeSendSync: Send + Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Send + Sync> MaybeSendSync for T {}
pub trait Backend: 'static + Sized + MaybeSendSync {
const NAME: &'static str;
const TARGET: CompileTarget;
type Error: Error + 'static + Send + Sync + From<ShaderArgsError>;
type Buffer<T: DeviceValue>: MaybeSendSync + Buffer<Self, T>;
type BufferSlice<'b, T: DeviceValue>;
type Encoder: MaybeSendSync + Encoder<Self>;
type Pass: MaybeSendSync;
type Timestamps;
type Module;
type Function: MaybeSendSync;
type Dispatch<'a>: Dispatch<'a, Self>
where
Self: 'a;
#[cfg(feature = "webgpu")]
fn as_webgpu(&self) -> Option<&WebGpu> {
None
}
#[cfg(feature = "cuda")]
fn as_cuda(&self) -> Option<&Cuda> {
None
}
fn load_module(&self, data: &str) -> Result<Self::Module, Self::Error> {
self.load_module_bytes(data.as_bytes())
}
fn load_module_bytes(&self, data: &[u8]) -> Result<Self::Module, Self::Error>;
fn load_function(
&self,
module: &Self::Module,
entry_point: &str,
push_constant_size: u32,
) -> Result<Self::Function, Self::Error>;
fn load_function_with_layouts(
&self,
module: &Self::Module,
entry_point: &str,
push_constant_size: u32,
_layouts: &BindGroupLayoutInfo,
) -> Result<Self::Function, Self::Error> {
self.load_function(module, entry_point, push_constant_size)
}
fn begin_encoding(&self) -> Self::Encoder;
fn begin_dispatch<'a>(
&'a self,
pass: &'a mut Self::Pass,
function: &'a Self::Function,
) -> Self::Dispatch<'a>;
fn synchronize(&self) -> Result<(), Self::Error>;
fn submit(&self, encoder: Self::Encoder) -> Result<(), Self::Error>;
fn init_buffer<T: DeviceValue + NoUninit>(
&self,
data: &[T],
usage: BufferUsages,
) -> Result<Self::Buffer<T>, Self::Error>;
fn uninit_buffer<T: DeviceValue + NoUninit>(
&self,
len: usize,
usage: BufferUsages,
) -> Result<Self::Buffer<T>, Self::Error>;
fn write_buffer<T: DeviceValue + NoUninit>(
&self,
buffer: &mut Self::Buffer<T>,
offset: u64,
data: &[T],
) -> Result<(), Self::Error>;
fn read_buffer<T: MaybeSendSync + DeviceValue + AnyBitPattern>(
&self,
buffer: &Self::Buffer<T>,
data: &mut [T],
) -> impl Future<Output = Result<(), Self::Error>> + MaybeSendSync;
fn slow_read_buffer<T: MaybeSendSync + DeviceValue + AnyBitPattern>(
&self,
buffer: &Self::Buffer<T>,
data: &mut [T],
) -> impl Future<Output = Result<(), Self::Error>> + MaybeSendSync;
fn slow_read_vec<T: MaybeSendSync + DeviceValue + AnyBitPattern + Default>(
&self,
buffer: &Self::Buffer<T>,
) -> impl Future<Output = Result<Vec<T>, Self::Error>> + MaybeSendSync {
async move {
let mut result = vec![T::default(); buffer.len()];
self.slow_read_buffer(buffer, &mut result).await?;
Ok(result)
}
}
}
pub trait Encoder<B: Backend> {
fn begin_pass(&mut self, label: &str, timestamps: Option<&mut B::Timestamps>) -> B::Pass;
fn copy_buffer_to_buffer<T: DeviceValue + NoUninit>(
&mut self,
source: &B::Buffer<T>,
source_offset: usize,
target: &mut B::Buffer<T>,
target_offset: usize,
copy_len: usize,
) -> Result<(), B::Error>;
}
pub trait Dispatch<'a, B: Backend> {
#[cfg(feature = "push_constants")]
fn set_push_constants(&mut self, data: &[u8]);
fn launch<'b>(
self,
grid: impl Into<DispatchGrid<'b, B>>,
workgroups: [u32; 3],
) -> Result<(), B::Error>;
}
pub trait Buffer<B: Backend, T: DeviceValue> {
fn is_empty(&self) -> bool;
fn len(&self) -> usize
where
T: Sized;
fn as_slice(&self) -> B::BufferSlice<'_, T> {
self.slice(..)
}
fn slice(&self, range: impl RangeBounds<usize>) -> B::BufferSlice<'_, T>;
fn usage(&self) -> BufferUsages;
}
pub enum DispatchGrid<'a, B: Backend> {
Grid([u32; 3]),
ThreadCount([u32; 3]),
Indirect(&'a B::Buffer<[u32; 3]>),
}
impl<'a, B: Backend> DispatchGrid<'a, B> {
pub fn resolve(self, workgroup_size: [u32; 3]) -> Self {
match self {
DispatchGrid::ThreadCount(threads) => DispatchGrid::Grid([
threads[0].div_ceil(workgroup_size[0]),
threads[1].div_ceil(workgroup_size[1]),
threads[2].div_ceil(workgroup_size[2]),
]),
other => other,
}
}
}
impl<'a, B: Backend> From<u32> for DispatchGrid<'a, B> {
fn from(num_threads: u32) -> DispatchGrid<'a, B> {
DispatchGrid::ThreadCount([num_threads, 1, 1])
}
}
impl<'a, B: Backend> From<usize> for DispatchGrid<'a, B> {
fn from(num_threads: usize) -> DispatchGrid<'a, B> {
DispatchGrid::ThreadCount([num_threads as u32, 1, 1])
}
}
impl<'a, B: Backend> From<i32> for DispatchGrid<'a, B> {
fn from(num_threads: i32) -> DispatchGrid<'a, B> {
assert!(num_threads >= 0);
DispatchGrid::ThreadCount([num_threads as u32, 1, 1])
}
}
impl<'a, B: Backend> From<[u32; 3]> for DispatchGrid<'a, B> {
fn from(num_threads: [u32; 3]) -> DispatchGrid<'a, B> {
DispatchGrid::ThreadCount(num_threads)
}
}
impl<'a, B: Backend> From<[usize; 3]> for DispatchGrid<'a, B> {
fn from(num_threads: [usize; 3]) -> DispatchGrid<'a, B> {
DispatchGrid::ThreadCount(num_threads.map(|num_threads| num_threads as u32))
}
}
impl<'a, B: Backend> From<[i32; 3]> for DispatchGrid<'a, B> {
fn from(num_threads: [i32; 3]) -> DispatchGrid<'a, B> {
DispatchGrid::ThreadCount(num_threads.map(|num_threads| {
assert!(num_threads >= 0);
num_threads as u32
}))
}
}