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) - Native Apple GPU compute via manzana crate
7//! - Vulkan (cross-platform, future)
8
9#[cfg(all(target_os = "macos", feature = "metal"))]
10pub mod metal_shaders;
11
12/// Backend trait for GPU operations
13pub trait Backend: Send + Sync {
14    /// Backend name
15    fn name(&self) -> &str;
16
17    /// Check if backend is available
18    fn is_available(&self) -> bool;
19
20    /// Get device count
21    fn device_count(&self) -> usize;
22}
23
24/// CUDA backend (NVIDIA GPUs)
25#[derive(Debug, Default)]
26pub struct CudaBackend;
27
28impl Backend for CudaBackend {
29    fn name(&self) -> &str {
30        "CUDA"
31    }
32
33    fn is_available(&self) -> bool {
34        #[cfg(feature = "cuda")]
35        {
36            crate::driver::cuda_available()
37        }
38        #[cfg(not(feature = "cuda"))]
39        {
40            false
41        }
42    }
43
44    fn device_count(&self) -> usize {
45        #[cfg(feature = "cuda")]
46        {
47            if self.is_available() {
48                crate::driver::device_count().unwrap_or(0)
49            } else {
50                0
51            }
52        }
53        #[cfg(not(feature = "cuda"))]
54        {
55            0
56        }
57    }
58}
59
60/// Metal backend (Apple GPUs)
61///
62/// Uses manzana crate for safe Rust Metal bindings on macOS.
63/// Enable with `--features metal` on macOS.
64#[derive(Debug, Default)]
65pub struct MetalBackend;
66
67impl Backend for MetalBackend {
68    fn name(&self) -> &str {
69        "Metal"
70    }
71
72    #[cfg(all(target_os = "macos", feature = "metal"))]
73    fn is_available(&self) -> bool {
74        manzana::metal::is_available()
75    }
76
77    #[cfg(not(all(target_os = "macos", feature = "metal")))]
78    fn is_available(&self) -> bool {
79        false
80    }
81
82    #[cfg(all(target_os = "macos", feature = "metal"))]
83    fn device_count(&self) -> usize {
84        manzana::metal::MetalCompute::devices().len()
85    }
86
87    #[cfg(not(all(target_os = "macos", feature = "metal")))]
88    fn device_count(&self) -> usize {
89        0
90    }
91}
92
93/// Metal device information (re-exported from manzana when feature enabled)
94#[cfg(all(target_os = "macos", feature = "metal"))]
95pub use manzana::metal::{CompiledShader as MetalShader, MetalBuffer, MetalCompute, MetalDevice};
96
97/// Vulkan backend (cross-platform) - placeholder
98#[derive(Debug, Default)]
99pub struct VulkanBackend;
100
101impl Backend for VulkanBackend {
102    fn name(&self) -> &str {
103        "Vulkan"
104    }
105
106    fn is_available(&self) -> bool {
107        false // Not implemented yet
108    }
109
110    fn device_count(&self) -> usize {
111        0
112    }
113}
114
115/// WGPU backend (WebGPU - cross-platform via wgpu crate)
116///
117/// Uses WGSL shading language, runs on:
118/// - Vulkan (Linux, Windows, Android)
119/// - Metal (macOS, iOS)
120/// - DX12 (Windows)
121/// - WebGPU (browsers via wasm)
122#[derive(Debug, Default)]
123pub struct WgpuBackend;
124
125impl Backend for WgpuBackend {
126    fn name(&self) -> &str {
127        "WGPU"
128    }
129
130    fn is_available(&self) -> bool {
131        // Availability based on wgpu feature flag
132        cfg!(feature = "wgpu")
133    }
134
135    fn device_count(&self) -> usize {
136        // Returns 1 if wgpu is available, 0 otherwise (adapter enumeration not yet wired)
137        usize::from(self.is_available())
138    }
139}
140
141/// Detect best available backend
142///
143/// Priority order:
144/// 1. CUDA (NVIDIA) - highest performance for NVIDIA GPUs
145/// 2. WGPU - cross-platform fallback (Vulkan/Metal/DX12)
146/// 3. Metal - Apple-specific (subset of WGPU)
147/// 4. Vulkan - direct Vulkan (subset of WGPU)
148#[must_use]
149pub fn detect_backend() -> Box<dyn Backend> {
150    let cuda = CudaBackend;
151    if cuda.is_available() {
152        return Box::new(cuda);
153    }
154
155    let wgpu = WgpuBackend;
156    if wgpu.is_available() {
157        return Box::new(wgpu);
158    }
159
160    let metal = MetalBackend;
161    if metal.is_available() {
162        return Box::new(metal);
163    }
164
165    let vulkan = VulkanBackend;
166    if vulkan.is_available() {
167        return Box::new(vulkan);
168    }
169
170    // Return CUDA as default (even if unavailable) for PTX generation
171    Box::new(CudaBackend)
172}
173
174#[cfg(test)]
175mod tests;