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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
48pub enum EnabledBackend {
49 #[cfg(all(
51 feature = "dx12",
52 all(target_os = "windows", not(target_arch = "wasm32"))
53 ))]
54 Dx12,
55
56 #[cfg(feature = "empty")]
58 Empty,
59
60 #[cfg(feature = "gl")]
62 Gl,
63
64 #[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 #[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 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
166pub enum Backend {
167 Dx12,
169
170 Empty,
172
173 Gl,
175
176 Metal,
178
179 Vulkan,
181}
182
183impl Backend {
184 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#[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#[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#[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#[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#[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}