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;