use bytemuck::{Pod, Zeroable};
use super::AcceleratorError;
#[cfg(not(feature = "cuda"))]
pub trait DeviceValue: Pod + Zeroable + Send + Sync + 'static {}
#[cfg(not(feature = "cuda"))]
impl<T> DeviceValue for T where T: Pod + Zeroable + Send + Sync + 'static {}
#[cfg(feature = "cuda")]
pub trait DeviceValue:
Pod
+ Zeroable
+ Send
+ Sync
+ 'static
+ cudarc::driver::DeviceRepr
+ cudarc::driver::ValidAsZeroBits
{
}
#[cfg(feature = "cuda")]
impl<T> DeviceValue for T where
T: Pod
+ Zeroable
+ Send
+ Sync
+ 'static
+ cudarc::driver::DeviceRepr
+ cudarc::driver::ValidAsZeroBits
{
}
pub trait DeviceBuffer<T: DeviceValue>: Send + Sync {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait AcceleratorBackend: Send + Sync {
type Buffer<T: DeviceValue>: DeviceBuffer<T>;
type Event: Send + Sync;
type Error: std::error::Error + Send + Sync + 'static;
fn identity(&self) -> u64;
fn upload<T: DeviceValue>(&self, values: &[T]) -> Result<Self::Buffer<T>, Self::Error>;
fn allocate<T: DeviceValue>(&self, len: usize) -> Result<Self::Buffer<T>, Self::Error>;
fn upload_into<T: DeviceValue>(
&self,
values: &[T],
buffer: &mut Self::Buffer<T>,
) -> Result<(), Self::Error>;
fn download<T: DeviceValue>(
&self,
buffer: &Self::Buffer<T>,
values: &mut [T],
) -> Result<(), Self::Error>;
fn synchronize(&self) -> Result<(), Self::Error>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct CpuBuffer<T>(pub(crate) Vec<T>);
impl<T: DeviceValue> DeviceBuffer<T> for CpuBuffer<T> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<T> CpuBuffer<T> {
pub fn as_slice(&self) -> &[T] {
&self.0
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.0
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct CpuBackend;
impl AcceleratorBackend for CpuBackend {
type Buffer<T: DeviceValue> = CpuBuffer<T>;
type Event = ();
type Error = AcceleratorError;
fn identity(&self) -> u64 {
0
}
fn upload<T: DeviceValue>(&self, values: &[T]) -> Result<Self::Buffer<T>, Self::Error> {
Ok(CpuBuffer(values.to_vec()))
}
fn allocate<T: DeviceValue>(&self, len: usize) -> Result<Self::Buffer<T>, Self::Error> {
Ok(CpuBuffer(vec![T::zeroed(); len]))
}
fn upload_into<T: DeviceValue>(
&self,
values: &[T],
buffer: &mut Self::Buffer<T>,
) -> Result<(), Self::Error> {
if values.len() != buffer.0.len() {
return Err(AcceleratorError::LengthMismatch {
expected: buffer.0.len(),
found: values.len(),
});
}
buffer.0.copy_from_slice(values);
Ok(())
}
fn download<T: DeviceValue>(
&self,
buffer: &Self::Buffer<T>,
values: &mut [T],
) -> Result<(), Self::Error> {
if values.len() != buffer.0.len() {
return Err(AcceleratorError::LengthMismatch {
expected: buffer.0.len(),
found: values.len(),
});
}
values.copy_from_slice(&buffer.0);
Ok(())
}
fn synchronize(&self) -> Result<(), Self::Error> {
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ComputeBackend {
Cpu,
Wgpu,
Cuda { device_ordinal: usize },
Auto,
}
impl ComputeBackend {
pub fn resolve(&self) -> Result<(Self, String), AcceleratorError> {
match self {
Self::Cpu => Ok((Self::Cpu, "CPU backend explicitly requested".into())),
Self::Wgpu => {
#[cfg(feature = "wgpu")]
return Ok((Self::Wgpu, "WGPU backend explicitly requested".into()));
#[cfg(not(feature = "wgpu"))]
Err(AcceleratorError::BackendUnavailable(
"mesh-sieve was built without the `wgpu` feature".into(),
))
}
Self::Cuda { device_ordinal } => {
#[cfg(feature = "cuda")]
{
super::cuda::CudaBackend::new(super::cuda::CudaOptions {
device_ordinal: *device_ordinal,
..Default::default()
})?;
Ok((
self.clone(),
format!("CUDA 13.0.3 ABI device {device_ordinal} and NVRTC initialized"),
))
}
#[cfg(not(feature = "cuda"))]
{
let _ = device_ordinal;
Err(AcceleratorError::BackendUnavailable(
"mesh-sieve was built without the `cuda` feature".into(),
))
}
}
Self::Auto => {
#[cfg(feature = "cuda")]
{
return match super::cuda::CudaBackend::new(super::cuda::CudaOptions::default())
{
Ok(_) => Ok((
Self::Cuda { device_ordinal: 0 },
"CUDA 13.0.3 ABI device 0 and NVRTC initialized successfully".into(),
)),
Err(error) => Ok((
Self::Cpu,
format!("CUDA initialization failed ({error}); using CPU"),
)),
};
}
#[cfg(not(feature = "cuda"))]
Ok((
Self::Cpu,
"CUDA support is not compiled in; using CPU".into(),
))
}
}
}
}