Skip to main content

cubecl_wgpu/
graphics.rs

1use crate::WgpuBackend;
2pub use wgpu::Backend;
3/// The basic trait to specify which graphics API to use as Backend.
4///
5/// Options are:
6///   - [Vulkan](Vulkan)
7///   - [Metal](Metal)
8///   - [OpenGL](OpenGl)
9///   - [DirectX 12](Dx12)
10///   - [WebGpu](WebGpu)
11pub trait GraphicsApi: Send + Sync + core::fmt::Debug + Default + Clone + 'static {
12    /// The wgpu backend.
13    fn backend() -> Backend;
14
15    /// The graphics API selection for `device`, before automatic selection is resolved.
16    ///
17    /// A named API returns its own backend. [`AutoGraphicsApi`] returns the device's backend,
18    /// preserving [`WgpuBackend::Auto`] when the device does not pin an API.
19    ///
20    /// # Panics
21    ///
22    /// The default implementation rejects the noop backend, which has no `WgpuBackend` variant.
23    fn backend_for(_device: &crate::WgpuDevice) -> WgpuBackend {
24        match Self::backend() {
25            Backend::Vulkan => WgpuBackend::Vulkan,
26            Backend::Metal => WgpuBackend::Metal,
27            Backend::Dx12 => WgpuBackend::Dx12,
28            Backend::Gl => WgpuBackend::Gl,
29            Backend::BrowserWebGpu => WgpuBackend::WebGpu,
30            Backend::Noop => panic!("The noop backend has no WgpuBackend selection"),
31        }
32    }
33}
34
35/// Vulkan graphics API.
36#[derive(Default, Debug, Clone)]
37pub struct Vulkan;
38
39/// Metal graphics API.
40#[derive(Default, Debug, Clone)]
41pub struct Metal;
42
43/// OpenGL graphics API.
44#[derive(Default, Debug, Clone)]
45pub struct OpenGl;
46
47/// DirectX 12 graphics API.
48#[derive(Default, Debug, Clone)]
49pub struct Dx12;
50
51/// `WebGpu` graphics API.
52#[derive(Default, Debug, Clone)]
53pub struct WebGpu;
54
55/// Automatic graphics API based on OS.
56#[derive(Default, Debug, Clone)]
57pub struct AutoGraphicsApi;
58
59impl GraphicsApi for Vulkan {
60    fn backend() -> Backend {
61        Backend::Vulkan
62    }
63}
64
65impl GraphicsApi for Metal {
66    fn backend() -> Backend {
67        Backend::Metal
68    }
69}
70
71impl GraphicsApi for OpenGl {
72    fn backend() -> Backend {
73        Backend::Gl
74    }
75}
76
77impl GraphicsApi for Dx12 {
78    fn backend() -> Backend {
79        Backend::Dx12
80    }
81}
82
83impl GraphicsApi for WebGpu {
84    fn backend() -> Backend {
85        Backend::BrowserWebGpu
86    }
87}
88
89impl AutoGraphicsApi {
90    /// The graphics APIs to try on this machine, best first.
91    ///
92    /// Vulkan leads wherever it reaches a GPU: it is the one that compiles to
93    /// `SPIR-V`, and the rest are what a machine without it still offers. An
94    /// API with only a software rasterizer is passed over for a later one
95    /// with a GPU, and taken only when none has one. Backends this machine has
96    /// no driver for enumerate nothing, so a list costs only the asking.
97    ///
98    /// This crate's own tests can narrow it to one with `AUTO_GRAPHICS_BACKEND`,
99    /// which then holds for every `Auto` device, not only those set up through
100    /// [`GraphicsApi::backend`].
101    pub fn chain() -> alloc::vec::Vec<Backend> {
102        #[cfg(all(feature = "std", test))]
103        if let Ok(backend) = std::env::var("AUTO_GRAPHICS_BACKEND") {
104            let backend = match backend.to_lowercase().as_str() {
105                "metal" => Backend::Metal,
106                "vulkan" => Backend::Vulkan,
107                "dx12" => Backend::Dx12,
108                "opengl" => Backend::Gl,
109                "webgpu" => Backend::BrowserWebGpu,
110                _ => {
111                    eprintln!(
112                        "Invalid graphics backend specified in AUTO_GRAPHICS_BACKEND environment \
113                         variable"
114                    );
115                    std::process::exit(1);
116                }
117            };
118
119            return alloc::vec![backend];
120        }
121
122        cfg_if::cfg_if! {
123            if #[cfg(target_family = "wasm")] {
124                alloc::vec![Backend::BrowserWebGpu]
125            } else if #[cfg(target_os = "macos")] {
126                alloc::vec![Backend::Metal]
127            } else {
128                alloc::vec![Backend::Vulkan, Backend::Dx12, Backend::Gl]
129            }
130        }
131    }
132}
133
134impl GraphicsApi for AutoGraphicsApi {
135    /// The first of the [chain](Self::chain) this machine has an adapter for —
136    /// the API a [`WgpuBackend::Auto`](crate::WgpuBackend::Auto) device comes
137    /// up on, so a setup made through this lands where a client made on first
138    /// use would.
139    fn backend() -> Backend {
140        crate::runtime::resolve_backend(crate::WgpuBackend::Auto)
141    }
142
143    /// The API `device` pins, or [`WgpuBackend::Auto`] when it pins none.
144    fn backend_for(device: &crate::WgpuDevice) -> WgpuBackend {
145        device.backend
146    }
147}
148
149#[cfg(test)]
150mod selection_tests {
151    use super::*;
152
153    #[test]
154    fn named_apis_preserve_their_backend() {
155        fn check<G: GraphicsApi>(expected: WgpuBackend) {
156            let selected = G::backend_for(&crate::WgpuDevice::default());
157            assert_eq!(selected, expected);
158        }
159
160        check::<Vulkan>(WgpuBackend::Vulkan);
161        check::<Metal>(WgpuBackend::Metal);
162        check::<Dx12>(WgpuBackend::Dx12);
163        check::<OpenGl>(WgpuBackend::Gl);
164        check::<WebGpu>(WgpuBackend::WebGpu);
165    }
166
167    #[test]
168    fn automatic_api_preserves_auto_and_pinned_selections() {
169        for backend in [
170            WgpuBackend::Auto,
171            WgpuBackend::Vulkan,
172            WgpuBackend::Metal,
173            WgpuBackend::Dx12,
174            WgpuBackend::Gl,
175            WgpuBackend::WebGpu,
176        ] {
177            let device = crate::WgpuDevice::default().on(backend);
178            assert_eq!(AutoGraphicsApi::backend_for(&device), backend);
179        }
180    }
181}