Skip to main content

trueno_gpu/backend/
mod.rs

1//! Multi-Backend Abstraction
2//!
3//! Provides a unified interface for different GPU backends:
4//! - CUDA (NVIDIA) - Primary, uses PTX
5//! - WGPU (WebGPU) - Cross-platform, uses WGSL (Vulkan/Metal/DX12/WebGPU)
6//! - Metal (Apple) - shader source only; no dispatcher (see `metal_shaders`)
7//! - Vulkan (cross-platform, future)
8
9pub mod metal_shaders;
10
11/// Backend trait for GPU operations
12pub trait Backend: Send + Sync {
13    /// Backend name
14    fn name(&self) -> &str;
15
16    /// Check if backend is available
17    fn is_available(&self) -> bool;
18
19    /// Get device count
20    fn device_count(&self) -> usize;
21}
22
23/// CUDA backend (NVIDIA GPUs)
24#[derive(Debug, Default)]
25pub struct CudaBackend;
26
27impl Backend for CudaBackend {
28    fn name(&self) -> &str {
29        "CUDA"
30    }
31
32    fn is_available(&self) -> bool {
33        #[cfg(feature = "cuda")]
34        {
35            crate::driver::cuda_available()
36        }
37        #[cfg(not(feature = "cuda"))]
38        {
39            false
40        }
41    }
42
43    fn device_count(&self) -> usize {
44        #[cfg(feature = "cuda")]
45        {
46            if self.is_available() {
47                crate::driver::device_count().unwrap_or(0)
48            } else {
49                0
50            }
51        }
52        #[cfg(not(feature = "cuda"))]
53        {
54            0
55        }
56    }
57}
58
59/// Metal backend (Apple GPUs) - placeholder
60///
61/// Reports unavailable on every platform. This crate contains Metal shader
62/// source (`metal_shaders`) but no dispatcher: nothing here calls
63/// `MTLDevice::newLibraryWithSource` or `MTLComputeCommandEncoder`. On macOS,
64/// use the `wgpu` feature, which reaches Apple GPUs through wgpu's Metal
65/// backend and does execute.
66#[derive(Debug, Default)]
67pub struct MetalBackend;
68
69impl Backend for MetalBackend {
70    fn name(&self) -> &str {
71        "Metal"
72    }
73
74    fn is_available(&self) -> bool {
75        false // No dispatcher; see struct docs.
76    }
77
78    fn device_count(&self) -> usize {
79        0
80    }
81}
82
83/// Vulkan backend (cross-platform) - placeholder
84#[derive(Debug, Default)]
85pub struct VulkanBackend;
86
87impl Backend for VulkanBackend {
88    fn name(&self) -> &str {
89        "Vulkan"
90    }
91
92    fn is_available(&self) -> bool {
93        false // Not implemented yet
94    }
95
96    fn device_count(&self) -> usize {
97        0
98    }
99}
100
101/// WGPU backend (WebGPU - cross-platform via wgpu crate)
102///
103/// Uses WGSL shading language, runs on:
104/// - Vulkan (Linux, Windows, Android)
105/// - Metal (macOS, iOS)
106/// - DX12 (Windows)
107/// - WebGPU (browsers via wasm)
108#[derive(Debug, Default)]
109pub struct WgpuBackend;
110
111impl Backend for WgpuBackend {
112    fn name(&self) -> &str {
113        "WGPU"
114    }
115
116    fn is_available(&self) -> bool {
117        // Availability based on wgpu feature flag
118        cfg!(feature = "wgpu")
119    }
120
121    fn device_count(&self) -> usize {
122        // Returns 1 if wgpu is available, 0 otherwise (adapter enumeration not yet wired)
123        usize::from(self.is_available())
124    }
125}
126
127/// Detect best available backend
128///
129/// Priority order:
130/// 1. CUDA (NVIDIA) - highest performance for NVIDIA GPUs
131/// 2. WGPU - cross-platform fallback (Vulkan/Metal/DX12)
132/// 3. Metal - Apple-specific (subset of WGPU)
133/// 4. Vulkan - direct Vulkan (subset of WGPU)
134#[must_use]
135pub fn detect_backend() -> Box<dyn Backend> {
136    let cuda = CudaBackend;
137    if cuda.is_available() {
138        return Box::new(cuda);
139    }
140
141    let wgpu = WgpuBackend;
142    if wgpu.is_available() {
143        return Box::new(wgpu);
144    }
145
146    let metal = MetalBackend;
147    if metal.is_available() {
148        return Box::new(metal);
149    }
150
151    let vulkan = VulkanBackend;
152    if vulkan.is_available() {
153        return Box::new(vulkan);
154    }
155
156    // Return CUDA as default (even if unavailable) for PTX generation
157    Box::new(CudaBackend)
158}
159
160#[cfg(test)]
161mod tests;