mesh_sieve/accelerator/
backend.rs1use bytemuck::{Pod, Zeroable};
4
5use super::AcceleratorError;
6
7#[cfg(not(feature = "cuda"))]
9pub trait DeviceValue: Pod + Zeroable + Send + Sync + 'static {}
10
11#[cfg(not(feature = "cuda"))]
12impl<T> DeviceValue for T where T: Pod + Zeroable + Send + Sync + 'static {}
13
14#[cfg(feature = "cuda")]
16pub trait DeviceValue:
17 Pod
18 + Zeroable
19 + Send
20 + Sync
21 + 'static
22 + cudarc::driver::DeviceRepr
23 + cudarc::driver::ValidAsZeroBits
24{
25}
26
27#[cfg(feature = "cuda")]
28impl<T> DeviceValue for T where
29 T: Pod
30 + Zeroable
31 + Send
32 + Sync
33 + 'static
34 + cudarc::driver::DeviceRepr
35 + cudarc::driver::ValidAsZeroBits
36{
37}
38
39pub trait DeviceBuffer<T: DeviceValue>: Send + Sync {
41 fn len(&self) -> usize;
43
44 fn is_empty(&self) -> bool {
46 self.len() == 0
47 }
48}
49
50pub trait AcceleratorBackend: Send + Sync {
52 type Buffer<T: DeviceValue>: DeviceBuffer<T>;
54 type Event: Send + Sync;
56 type Error: std::error::Error + Send + Sync + 'static;
58
59 fn identity(&self) -> u64;
62
63 fn upload<T: DeviceValue>(&self, values: &[T]) -> Result<Self::Buffer<T>, Self::Error>;
65 fn allocate<T: DeviceValue>(&self, len: usize) -> Result<Self::Buffer<T>, Self::Error>;
67 fn upload_into<T: DeviceValue>(
69 &self,
70 values: &[T],
71 buffer: &mut Self::Buffer<T>,
72 ) -> Result<(), Self::Error>;
73 fn download<T: DeviceValue>(
75 &self,
76 buffer: &Self::Buffer<T>,
77 values: &mut [T],
78 ) -> Result<(), Self::Error>;
79 fn synchronize(&self) -> Result<(), Self::Error>;
81}
82
83#[derive(Clone, Debug, PartialEq)]
85pub struct CpuBuffer<T>(pub(crate) Vec<T>);
86
87impl<T: DeviceValue> DeviceBuffer<T> for CpuBuffer<T> {
88 fn len(&self) -> usize {
89 self.0.len()
90 }
91}
92
93impl<T> CpuBuffer<T> {
94 pub fn as_slice(&self) -> &[T] {
96 &self.0
97 }
98
99 pub fn as_mut_slice(&mut self) -> &mut [T] {
101 &mut self.0
102 }
103}
104
105#[derive(Clone, Copy, Debug, Default)]
107pub struct CpuBackend;
108
109impl AcceleratorBackend for CpuBackend {
110 type Buffer<T: DeviceValue> = CpuBuffer<T>;
111 type Event = ();
112 type Error = AcceleratorError;
113
114 fn identity(&self) -> u64 {
115 0
116 }
117
118 fn upload<T: DeviceValue>(&self, values: &[T]) -> Result<Self::Buffer<T>, Self::Error> {
119 Ok(CpuBuffer(values.to_vec()))
120 }
121
122 fn allocate<T: DeviceValue>(&self, len: usize) -> Result<Self::Buffer<T>, Self::Error> {
123 Ok(CpuBuffer(vec![T::zeroed(); len]))
124 }
125
126 fn upload_into<T: DeviceValue>(
127 &self,
128 values: &[T],
129 buffer: &mut Self::Buffer<T>,
130 ) -> Result<(), Self::Error> {
131 if values.len() != buffer.0.len() {
132 return Err(AcceleratorError::LengthMismatch {
133 expected: buffer.0.len(),
134 found: values.len(),
135 });
136 }
137 buffer.0.copy_from_slice(values);
138 Ok(())
139 }
140
141 fn download<T: DeviceValue>(
142 &self,
143 buffer: &Self::Buffer<T>,
144 values: &mut [T],
145 ) -> Result<(), Self::Error> {
146 if values.len() != buffer.0.len() {
147 return Err(AcceleratorError::LengthMismatch {
148 expected: buffer.0.len(),
149 found: values.len(),
150 });
151 }
152 values.copy_from_slice(&buffer.0);
153 Ok(())
154 }
155
156 fn synchronize(&self) -> Result<(), Self::Error> {
157 Ok(())
158 }
159}
160
161#[derive(Clone, Debug, PartialEq, Eq)]
163pub enum ComputeBackend {
164 Cpu,
166 Wgpu,
168 Cuda { device_ordinal: usize },
170 Auto,
172}
173
174impl ComputeBackend {
175 pub fn resolve(&self) -> Result<(Self, String), AcceleratorError> {
180 match self {
181 Self::Cpu => Ok((Self::Cpu, "CPU backend explicitly requested".into())),
182 Self::Wgpu => {
183 #[cfg(feature = "wgpu")]
184 return Ok((Self::Wgpu, "WGPU backend explicitly requested".into()));
185 #[cfg(not(feature = "wgpu"))]
186 Err(AcceleratorError::BackendUnavailable(
187 "mesh-sieve was built without the `wgpu` feature".into(),
188 ))
189 }
190 Self::Cuda { device_ordinal } => {
191 #[cfg(feature = "cuda")]
192 {
193 super::cuda::CudaBackend::new(super::cuda::CudaOptions {
194 device_ordinal: *device_ordinal,
195 ..Default::default()
196 })?;
197 Ok((
198 self.clone(),
199 format!("CUDA 13.0.3 ABI device {device_ordinal} and NVRTC initialized"),
200 ))
201 }
202 #[cfg(not(feature = "cuda"))]
203 {
204 let _ = device_ordinal;
205 Err(AcceleratorError::BackendUnavailable(
206 "mesh-sieve was built without the `cuda` feature".into(),
207 ))
208 }
209 }
210 Self::Auto => {
211 #[cfg(feature = "cuda")]
212 {
213 return match super::cuda::CudaBackend::new(super::cuda::CudaOptions::default())
214 {
215 Ok(_) => Ok((
216 Self::Cuda { device_ordinal: 0 },
217 "CUDA 13.0.3 ABI device 0 and NVRTC initialized successfully".into(),
218 )),
219 Err(error) => Ok((
220 Self::Cpu,
221 format!("CUDA initialization failed ({error}); using CPU"),
222 )),
223 };
224 }
225 #[cfg(not(feature = "cuda"))]
226 Ok((
227 Self::Cpu,
228 "CUDA support is not compiled in; using CPU".into(),
229 ))
230 }
231 }
232 }
233}