rendy_init/
lib.rs

1//!
2//! Simple rendy initialization.
3//! Takes most bolierplate required for init rendy on different platforms/backends.
4//! It is still possible to construct everything manually if your case is not supported by this module.
5//!
6
7// #[allow(unused)]
8use {
9    rendy_command::Families,
10    rendy_core::{
11        backend_enum,
12        hal::{device::CreationError, Backend, Instance as _, UnsupportedBackend},
13        rendy_backend, rendy_with_dx12_backend, rendy_with_empty_backend, rendy_with_gl_backend,
14        rendy_with_metal_backend, rendy_with_vulkan_backend, EnabledBackend, Instance,
15    },
16    rendy_factory::{Config, DevicesConfigure, Factory, HeapsConfigure, QueuesConfigure},
17};
18
19#[cfg(feature = "winit")]
20mod windowed;
21
22#[cfg(feature = "winit")]
23pub use windowed::*;
24
25/// Error during rendy initialization
26#[derive(Clone, Debug, PartialEq)]
27pub enum RendyInitError {
28    /// Gfx creation error.
29    CreationError(CreationError),
30
31    /// Backend is unsupported.
32    UnsupportedBackend(UnsupportedBackend),
33}
34
35impl From<CreationError> for RendyInitError {
36    fn from(err: CreationError) -> Self {
37        RendyInitError::CreationError(err)
38    }
39}
40
41impl From<UnsupportedBackend> for RendyInitError {
42    fn from(err: UnsupportedBackend) -> Self {
43        RendyInitError::UnsupportedBackend(err)
44    }
45}
46
47impl std::fmt::Display for RendyInitError {
48    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            RendyInitError::CreationError(err) => write!(fmt, "Cannot init rendy: {:#?}", err),
51            RendyInitError::UnsupportedBackend(err) => write!(fmt, "Cannot init rendy: {:#?}", err),
52        }
53    }
54}
55
56impl std::error::Error for RendyInitError {
57    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
58        match self {
59            RendyInitError::CreationError(_err) => None, // Should be `Some(err)`
60            RendyInitError::UnsupportedBackend(_err) => None, // Should be `Some(err)`
61        }
62    }
63}
64
65/// Initialized rendy instance without window.
66/// Create with `Rendy::init`.
67///
68/// OpenGL can't be initialized without window, see `WindowedRendy` to initialize rendy on OpenGL.
69#[derive(Debug)]
70pub struct Rendy<B: Backend> {
71    pub families: Families<B>,
72    pub factory: Factory<B>,
73}
74
75impl<B: Backend> Rendy<B> {
76    pub fn init(
77        config: &Config<impl DevicesConfigure, impl HeapsConfigure, impl QueuesConfigure>,
78    ) -> Result<Self, RendyInitError> {
79        let instance = B::Instance::create("Rendy", 1)?;
80        let (factory, families) =
81            rendy_factory::init_with_instance(Instance::new(instance), config)?;
82        Ok(Rendy { factory, families })
83    }
84}
85
86/// Error type that may be returned by `AnyRendy::init_auto`
87pub struct RendyAutoInitError {
88    pub errors: Vec<(EnabledBackend, RendyInitError)>,
89}
90
91impl std::fmt::Debug for RendyAutoInitError {
92    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        std::fmt::Display::fmt(self, fmt)
94    }
95}
96
97impl std::fmt::Display for RendyAutoInitError {
98    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        if fmt.alternate() {
100            if self.errors.is_empty() {
101                writeln!(fmt, "No enabled backends among:")?;
102                for &backend in &BASIC_PRIORITY {
103                    writeln!(fmt, "  {:#}", backend)?;
104                }
105            } else {
106                writeln!(fmt, "Initialization failed for all backends")?;
107                for (backend, error) in &self.errors {
108                    writeln!(fmt, "  {:#}: {:#}", backend, error)?;
109                }
110            }
111        } else {
112            if self.errors.is_empty() {
113                write!(fmt, "No enabled backends among:")?;
114                for &backend in &BASIC_PRIORITY {
115                    write!(fmt, "  {}", backend)?;
116                }
117            } else {
118                write!(fmt, "Initialization failed for all backends")?;
119                for (backend, error) in &self.errors {
120                    write!(fmt, "  {}: {}", backend, error)?;
121                }
122            }
123        }
124        Ok(())
125    }
126}
127
128backend_enum! { #[derive(Debug)] pub enum AnyRendy(Rendy); }
129
130impl AnyRendy {
131    pub fn init_auto(
132        config: &Config<impl DevicesConfigure, impl HeapsConfigure, impl QueuesConfigure>,
133    ) -> Result<Self, RendyAutoInitError> {
134        let mut errors = Vec::with_capacity(5);
135
136        for backend in BASIC_PRIORITY
137            .iter()
138            .filter_map(|b| std::convert::TryInto::try_into(*b).ok())
139        {
140            match Self::init(backend, config) {
141                Ok(rendy) => return Ok(rendy),
142                Err(err) => errors.push((backend, err)),
143            }
144        }
145
146        Err(RendyAutoInitError { errors })
147    }
148
149    #[rustfmt::skip]
150    pub fn init(
151        back: EnabledBackend,
152        config: &Config<impl DevicesConfigure, impl HeapsConfigure, impl QueuesConfigure>,
153    ) -> Result<Self, RendyInitError> {
154        #![allow(unused_variables)]
155        rendy_backend!(match (back): EnabledBackend {
156            Dx12 => { Ok(AnyRendy::Dx12(Rendy::<rendy_core::dx12::Backend>::init(config)?)) }
157            Empty => { Ok(AnyRendy::Empty(Rendy::<rendy_core::empty::Backend>::init(config)?)) }
158            Gl => { Ok(AnyRendy::Gl(Rendy::<rendy_core::gl::Backend>::init(config)?)) }
159            Metal => { Ok(AnyRendy::Metal(Rendy::<rendy_core::metal::Backend>::init(config)?)) }
160            Vulkan => { Ok(AnyRendy::Vulkan(Rendy::<rendy_core::vulkan::Backend>::init(config)?)) }
161        })
162    }
163}
164
165/// Get available backends
166pub fn available_backends() -> smallvec::SmallVec<[EnabledBackend; 5]> {
167    #[allow(unused_mut)]
168    let mut backends = smallvec::SmallVec::<[EnabledBackend; 5]>::new();
169    rendy_with_dx12_backend!(backends.push(EnabledBackend::Dx12));
170    rendy_with_empty_backend!(backends.push(EnabledBackend::Empty));
171    rendy_with_gl_backend!(backends.push(EnabledBackend::Gl));
172    rendy_with_metal_backend!(backends.push(EnabledBackend::Metal));
173    rendy_with_vulkan_backend!(backends.push(EnabledBackend::Vulkan));
174    backends
175}
176
177pub const BASIC_PRIORITY: [rendy_core::Backend; 4] = [
178    rendy_core::Backend::Vulkan,
179    rendy_core::Backend::Dx12,
180    rendy_core::Backend::Metal,
181    rendy_core::Backend::Gl,
182];
183
184pub fn pick_backend(
185    priority: impl IntoIterator<Item = rendy_core::Backend>,
186) -> Option<EnabledBackend> {
187    priority
188        .into_iter()
189        .filter_map(|b| std::convert::TryInto::try_into(b).ok())
190        .next()
191}