rendy_core/
backend.rs

1use std::any::TypeId;
2
3#[cfg(all(
4    feature = "dx12",
5    all(target_os = "windows", not(target_arch = "wasm32"))
6))]
7fn dx12_backend_type_id() -> TypeId {
8    TypeId::of::<crate::dx12::Backend>()
9}
10
11#[cfg(feature = "empty")]
12fn empty_backend_type_id() -> TypeId {
13    TypeId::of::<crate::empty::Backend>()
14}
15
16#[cfg(feature = "gl")]
17fn gl_backend_type_id() -> TypeId {
18    TypeId::of::<crate::gl::Backend>()
19}
20
21#[cfg(all(
22    feature = "metal",
23    any(
24        all(not(target_arch = "wasm32"), target_os = "macos"),
25        all(target_arch = "aarch64", target_os = "ios")
26    )
27))]
28fn metal_backend_type_id() -> TypeId {
29    TypeId::of::<crate::metal::Backend>()
30}
31
32#[cfg(all(
33    feature = "vulkan",
34    any(all(
35        any(
36            target_os = "windows",
37            all(unix, not(any(target_os = "macos", target_os = "ios")))
38        ),
39        not(target_arch = "wasm32")
40    ))
41))]
42fn vulkan_backend_type_id() -> TypeId {
43    TypeId::of::<crate::vulkan::Backend>()
44}
45
46/// Backend enumerator.
47#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
48pub enum EnabledBackend {
49    /// Dx12 backend.
50    #[cfg(all(
51        feature = "dx12",
52        all(target_os = "windows", not(target_arch = "wasm32"))
53    ))]
54    Dx12,
55
56    /// Empty mock backend.
57    #[cfg(feature = "empty")]
58    Empty,
59
60    /// OpenGL backend.
61    #[cfg(feature = "gl")]
62    Gl,
63
64    /// Metal backend.
65    #[cfg(all(
66        feature = "metal",
67        any(
68            all(not(target_arch = "wasm32"), target_os = "macos"),
69            all(target_arch = "aarch64", target_os = "ios")
70        )
71    ))]
72    Metal,
73
74    /// Vulkan backend.
75    #[cfg(all(
76        feature = "vulkan",
77        all(
78            any(
79                target_os = "windows",
80                all(unix, not(any(target_os = "macos", target_os = "ios")))
81            ),
82            not(target_arch = "wasm32")
83        )
84    ))]
85    Vulkan,
86}
87
88impl EnabledBackend {
89    /// Check which backend is it.
90    pub fn which<B: crate::hal::Backend>() -> Self {
91        #[allow(unused_variables)]
92        let tid = TypeId::of::<B>();
93        match () {
94            #[cfg(all(
95                feature = "dx12",
96                all(target_os = "windows", not(target_arch = "wasm32"))
97            ))]
98            _ if tid == dx12_backend_type_id() => EnabledBackend::Dx12,
99            #[cfg(feature = "empty")]
100            _ if tid == empty_backend_type_id() => EnabledBackend::Empty,
101            #[cfg(feature = "gl")]
102            _ if tid == gl_backend_type_id() => EnabledBackend::Gl,
103            #[cfg(all(
104                feature = "metal",
105                any(
106                    all(not(target_arch = "wasm32"), target_os = "macos"),
107                    all(target_arch = "aarch64", target_os = "ios")
108                )
109            ))]
110            _ if tid == metal_backend_type_id() => EnabledBackend::Metal,
111            #[cfg(all(
112                feature = "vulkan",
113                all(
114                    any(
115                        target_os = "windows",
116                        all(unix, not(any(target_os = "macos", target_os = "ios")))
117                    ),
118                    not(target_arch = "wasm32")
119                )
120            ))]
121            _ if tid == vulkan_backend_type_id() => EnabledBackend::Vulkan,
122            _ => panic!("Unsupported gfx-hal backend"),
123        }
124    }
125}
126
127impl std::fmt::Display for EnabledBackend {
128    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        #![allow(unreachable_code)]
130
131        fmt.write_str(match *self {
132            #[cfg(all(
133                feature = "dx12",
134                all(target_os = "windows", not(target_arch = "wasm32"))
135            ))]
136            EnabledBackend::Dx12 => "dx12",
137            #[cfg(feature = "empty")]
138            EnabledBackend::Empty => "empty",
139            #[cfg(feature = "gl")]
140            EnabledBackend::Gl => "gl",
141            #[cfg(all(
142                feature = "metal",
143                any(
144                    all(not(target_arch = "wasm32"), target_os = "macos"),
145                    all(target_arch = "aarch64", target_os = "ios")
146                )
147            ))]
148            EnabledBackend::Metal => "metal",
149            #[cfg(all(
150                feature = "vulkan",
151                all(
152                    any(
153                        target_os = "windows",
154                        all(unix, not(any(target_os = "macos", target_os = "ios")))
155                    ),
156                    not(target_arch = "wasm32")
157                )
158            ))]
159            EnabledBackend::Vulkan => "vulkan",
160        })
161    }
162}
163
164/// Backend enumerator.
165#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
166pub enum Backend {
167    /// Microsoft's DirectX 12 (tm) backend
168    Dx12,
169
170    /// Empty backend. Most functions are `unimplemented!()`
171    Empty,
172
173    /// Khronos' OpenGL and WebGL backends.
174    Gl,
175
176    /// Apple's Metal (tm) backend.
177    Metal,
178
179    /// Khronos' Vulkan backend.
180    Vulkan,
181}
182
183impl Backend {
184    /// Check which backend is it.
185    pub fn which<B: crate::hal::Backend>() -> Self {
186        EnabledBackend::which::<B>().into()
187    }
188}
189
190impl std::fmt::Display for Backend {
191    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192        fmt.write_str(match self {
193            Backend::Dx12 => "dx12",
194            Backend::Empty => "empty",
195            Backend::Gl => "gl",
196            Backend::Metal => "metal",
197            Backend::Vulkan => "vulkan",
198        })
199    }
200}
201
202impl From<EnabledBackend> for Backend {
203    fn from(back: EnabledBackend) -> Self {
204        match back {
205            #[cfg(all(
206                feature = "dx12",
207                all(target_os = "windows", not(target_arch = "wasm32"))
208            ))]
209            EnabledBackend::Dx12 => Backend::Dx12,
210            #[cfg(feature = "empty")]
211            EnabledBackend::Empty => Backend::Empty,
212            #[cfg(feature = "gl")]
213            EnabledBackend::Gl => Backend::Gl,
214            #[cfg(all(
215                feature = "metal",
216                any(
217                    all(not(target_arch = "wasm32"), target_os = "macos"),
218                    all(target_arch = "aarch64", target_os = "ios")
219                )
220            ))]
221            EnabledBackend::Metal => Backend::Metal,
222            #[cfg(all(
223                feature = "vulkan",
224                all(
225                    any(
226                        target_os = "windows",
227                        all(unix, not(any(target_os = "macos", target_os = "ios")))
228                    ),
229                    not(target_arch = "wasm32")
230                )
231            ))]
232            EnabledBackend::Vulkan => Backend::Vulkan,
233        }
234    }
235}
236
237/// Unknown backend errors.
238#[derive(Clone, Debug, PartialEq, Eq)]
239pub struct ParseBackendError(String);
240
241impl std::fmt::Display for ParseBackendError {
242    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        write!(fmt, "Unknown backend \"{}\"", self.0)
244    }
245}
246
247impl std::error::Error for ParseBackendError {}
248
249impl std::str::FromStr for Backend {
250    type Err = ParseBackendError;
251
252    fn from_str(string: &str) -> Result<Self, ParseBackendError> {
253        match string {
254            "Dx12" | "dx12" | "DirectX 12" => Ok(Backend::Dx12),
255            "Empty" | "empty" => Ok(Backend::Empty),
256            "Gl" | "gl" => Ok(Backend::Gl),
257            "Metal" | "metal" => Ok(Backend::Metal),
258            "Vulkan" | "vulkan" => Ok(Backend::Vulkan),
259            _ => Err(ParseBackendError(string.to_string())),
260        }
261    }
262}
263
264/// Error signaling that particular backend is not enabled.
265#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
266pub struct NotEnabled(pub Backend);
267
268impl std::fmt::Display for NotEnabled {
269    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270        write!(fmt, "Backend \"{}\" is not enabled", self.0)
271    }
272}
273
274impl std::error::Error for NotEnabled {}
275
276impl std::convert::TryFrom<Backend> for EnabledBackend {
277    type Error = NotEnabled;
278
279    fn try_from(back: Backend) -> Result<Self, NotEnabled> {
280        match back {
281            #[cfg(all(
282                feature = "dx12",
283                all(target_os = "windows", not(target_arch = "wasm32"))
284            ))]
285            Backend::Dx12 => Ok(EnabledBackend::Dx12),
286            #[cfg(feature = "empty")]
287            Backend::Empty => Ok(EnabledBackend::Empty),
288            #[cfg(feature = "gl")]
289            Backend::Gl => Ok(EnabledBackend::Gl),
290            #[cfg(all(
291                feature = "metal",
292                any(all(
293                    target_os = "macos",
294                    not(target_arch = "wasm32"),
295                    all(target_arch = "aarch64", target_os = "ios")
296                ))
297            ))]
298            Backend::Metal => Ok(EnabledBackend::Metal),
299            #[cfg(all(
300                feature = "vulkan",
301                all(
302                    any(
303                        target_os = "windows",
304                        all(unix, not(any(target_os = "macos", target_os = "ios")))
305                    ),
306                    not(target_arch = "wasm32")
307                )
308            ))]
309            Backend::Vulkan => Ok(EnabledBackend::Vulkan),
310            not_enabled => Err(NotEnabled(not_enabled)),
311        }
312    }
313}
314
315#[doc(hidden)]
316pub trait BackendSwitch {
317    type Dx12;
318    type Empty;
319    type Gl;
320    type Metal;
321    type Vulkan;
322}
323
324#[doc(hidden)]
325#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
326pub enum Never {}
327
328/// Allows create new enumerations with variants for each active backend.
329/// `rendy_backend!` can be used to match over it.
330#[macro_export]
331macro_rules! backend_enum {
332    ($(#[$meta:meta])* pub enum $name:ident($data:ident $(<$($p:ty),*>)?);) => {
333        $(#[$meta])*
334        pub enum $name {
335            Dx12(<Self as $crate::BackendSwitch>::Dx12),
336            Empty(<Self as $crate::BackendSwitch>::Empty),
337            Gl(<Self as $crate::BackendSwitch>::Gl),
338            Metal(<Self as $crate::BackendSwitch>::Metal),
339            Vulkan(<Self as $crate::BackendSwitch>::Vulkan),
340        }
341
342        impl $name {
343            $crate::rendy_with_dx12_backend! {
344                pub fn dx12(value: $data<$crate::dx12::Backend $($(, $p)*)?>) -> Self {
345                    $name::Dx12(value)
346                }
347            }
348            $crate::rendy_with_empty_backend! {
349                pub fn empty(value: $data<$crate::empty::Backend $($(, $p)*)?>) -> Self {
350                    $name::Empty(value)
351                }
352            }
353            $crate::rendy_with_gl_backend! {
354                pub fn gl(value: $data<$crate::gl::Backend $($(, $p)*)?>) -> Self {
355                    $name::Gl(value)
356                }
357            }
358            $crate::rendy_with_metal_backend! {
359                pub fn metal(value: $data<$crate::metal::Backend $($(, $p)*)?>) -> Self {
360                    $name::Metal(value)
361                }
362            }
363            $crate::rendy_with_vulkan_backend! {
364                pub fn vulkan(value: $data<$crate::vulkan::Backend $($(, $p)*)?>) -> Self {
365                    $name::Vulkan(value)
366                }
367            }
368        }
369
370        impl $crate::BackendSwitch for $name {
371            $crate::rendy_with_dx12_backend! { type Dx12 = $data<$crate::dx12::Backend $($(, $p)*)?>; }
372            $crate::rendy_with_empty_backend! { type Empty = $data<$crate::empty::Backend $($(, $p)*)?>; }
373            $crate::rendy_with_gl_backend! { type Gl = $data<$crate::gl::Backend $($(, $p)*)?>; }
374            $crate::rendy_with_metal_backend! { type Metal = $data<$crate::metal::Backend $($(, $p)*)?>; }
375            $crate::rendy_with_vulkan_backend! { type Vulkan = $data<$crate::vulkan::Backend $($(, $p)*)?>; }
376
377            $crate::rendy_without_dx12_backend! { type Dx12 = $crate::Never; }
378            $crate::rendy_without_empty_backend! { type Empty = $crate::Never; }
379            $crate::rendy_without_gl_backend! { type Gl = $crate::Never; }
380            $crate::rendy_without_metal_backend! { type Metal = $crate::Never; }
381            $crate::rendy_without_vulkan_backend! { type Vulkan = $crate::Never; }
382        }
383    };
384}
385
386/// Execute arm with matching backend.
387/// If particular backend is disabled
388/// then its arm is stripped from compilation altogether.
389#[macro_export]
390macro_rules! rendy_backend {
391    (match ($target:expr) : $enum_type:path {
392        $(Dx12 => $dx12_code:block)?
393        $(Empty => $empty_code:block)?
394        $(Gl => $gl_code:block)?
395        $(Metal => $metal_code:block)?
396        $(Vulkan => $vulkan_code:block)?
397        $($(use $back:ident;)?_ => $code:block)?
398    }) => {{#[allow(unreachable_code, irrefutable_let_patterns)]
399        || -> _ {
400            #[allow(unused)] use $enum_type as EnumType;
401            $($crate::rendy_with_dx12_backend!(if let EnumType :: Dx12 = $target { return { $dx12_code }; });)?
402            $($crate::rendy_with_empty_backend!(if let EnumType :: Empty = $target { return { $empty_code }; });)?
403            $($crate::rendy_with_gl_backend!(if let EnumType :: Gl = $target { return { $gl_code }; });)?
404            $($crate::rendy_with_metal_backend!(if let EnumType :: Metal = $target { return { $metal_code }; });)?
405            $($crate::rendy_with_vulkan_backend!(if let EnumType :: Vulkan = $target { return { $vulkan_code }; });)?
406
407            $($crate::rendy_with_dx12_backend!(if let EnumType :: Dx12 = $target { $(use $crate::dx12 as $back;)? return { $code }; });)?
408            $($crate::rendy_with_empty_backend!(if let EnumType :: Empty = $target { $(use $crate::empty as $back;)? return { $code }; });)?
409            $($crate::rendy_with_gl_backend!(if let EnumType :: Gl = $target { $(use $crate::gl as $back;)? return { $code }; });)?
410            $($crate::rendy_with_metal_backend!(if let EnumType :: Metal = $target { $(use $crate::metal as $back;)? return { $code }; });)?
411            $($crate::rendy_with_vulkan_backend!(if let EnumType :: Vulkan = $target { $(use $crate::vulkan as $back;)? return { $code }; });)?
412
413            unreachable!()
414        }()
415    }};
416
417    (match ($target:expr) : $enum_type:path {
418        $(Dx12($dx12_pat:pat) => $dx12_code:block)?
419        $(Empty($empty_pat:pat) => $empty_code:block)?
420        $(Gl($gl_pat:pat) => $gl_code:block)?
421        $(Metal($metal_pat:pat) => $metal_code:block)?
422        $(Vulkan($vulkan_pat:pat) => $vulkan_code:block$)?
423        $($(use $back:ident;)?_($pat:pat) => $code:block)?
424    }) => {{#[allow(unreachable_code, irrefutable_let_patterns)]
425        || -> _ {
426            #[allow(unused)] use $enum_type as EnumType;
427            $($crate::rendy_with_dx12_backend!(if let EnumType :: Dx12($dx12_pat) = $target { return { $dx12_code }; });)?
428            $($crate::rendy_with_empty_backend!(if let EnumType :: Empty($empty_pat) = $target { return { $empty_code }; });)?
429            $($crate::rendy_with_gl_backend!(if let EnumType :: Gl($gl_pat) = $target { return { $gl_code }; });)?
430            $($crate::rendy_with_metal_backend!(if let EnumType :: Metal($metal_pat) = $target { return { $metal_code }; });)?
431            $($crate::rendy_with_vulkan_backend!(if let EnumType :: Vulkan($vulkan_pat) = $target { return { $vulkan_code }; });)?
432
433            $($crate::rendy_with_dx12_backend!(if let EnumType :: Dx12($pat) = $target { $(use $crate::dx12 as $back;)? return { $code }; });)?
434            $($crate::rendy_with_empty_backend!(if let EnumType :: Empty($pat) = $target { $(use $crate::empty as $back;)? return { $code }; });)?
435            $($crate::rendy_with_gl_backend!(if let EnumType :: Gl($pat) = $target { $(use $crate::gl as $back;)? return { $code }; });)?
436            $($crate::rendy_with_metal_backend!(if let EnumType :: Metal($pat) = $target { $(use $crate::metal as $back;)? return { $code }; });)?
437            $($crate::rendy_with_vulkan_backend!(if let EnumType :: Vulkan($pat) = $target { $(use $crate::vulkan as $back;)? return { $code }; });)?
438
439            unreachable!()
440        }()
441    }};
442}
443
444/// Check if specified backend would use pipeline barriers or using them is futile.
445/// Piece of internal knowledge.
446#[inline]
447#[rustfmt::skip]
448pub fn uses_pipeline_barriers<B: crate::hal::Backend>(_device: &B::Device) -> bool {
449    rendy_backend!(match (EnabledBackend::which::<B>()): EnabledBackend {
450        Gl => { false }
451        Metal => { false }
452        _ => { true }
453    })
454}