Skip to main content

iced_core/
backend.rs

1//! Graphical backends are designed to aid in rendering computer graphics to a monitor.
2use std::env;
3use std::fmt;
4
5/// A graphical backend.
6///
7/// You can override the default strategy by setting the `ICED_BACKEND` environment variable.
8/// If you are using the default renderer, the available options are:
9///   - `wgpu` for the hardware accelerated backend.
10///   - `tiny-skia` for the software-based backend.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Backend {
13    /// Auto-detect and choose the best available [`Backend`].
14    Best,
15    /// Hardware accelerated graphics backend with the given [`Api`].
16    Hardware(Api),
17    /// Sofware graphics backend; quite slower than hardware-based backends, but more compatible.
18    Software,
19    /// A custom rendering backend with the given name.
20    Custom(String),
21}
22
23impl Backend {
24    /// All the possible known combinations of [`Backend`].
25    pub const ALL: &[Self] = &[
26        Self::Best,
27        Self::Hardware(Api::Best),
28        Self::Hardware(Api::Vulkan),
29        Self::Hardware(Api::Metal),
30        Self::Hardware(Api::DirectX12),
31        Self::Hardware(Api::OpenGL),
32        Self::Hardware(Api::WebGPU),
33        Self::Software,
34    ];
35
36    /// Returns true if the [`Backend`] is [`Backend::Hardware`].
37    pub fn hardware(&self) -> Option<Api> {
38        match self {
39            Backend::Hardware(api) => Some(*api),
40            _ => None,
41        }
42    }
43
44    /// Returns true if the [`Backend`] is [`Backend::Software`].
45    pub fn is_software(&self) -> bool {
46        matches!(self, Self::Software)
47    }
48
49    /// Returns true if the [`Backend`] is [`Backend::Best`] or matches the given name.
50    pub fn matches(&self, target: &str) -> bool {
51        match self {
52            Backend::Best => true,
53            Backend::Custom(name) => name == target || name == &target.replace("-", "_"),
54            _ => false,
55        }
56    }
57}
58
59impl From<String> for Backend {
60    fn from(backend: String) -> Self {
61        Self::Custom(backend)
62    }
63}
64
65impl From<&str> for Backend {
66    fn from(backend: &str) -> Self {
67        Self::Custom(backend.to_owned())
68    }
69}
70
71impl fmt::Display for Backend {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match self {
74            Backend::Best => write!(f, "Best Backend"),
75            Backend::Hardware(api) => write!(f, "Hardware Backend ({api})"),
76            Backend::Software => write!(f, "Software Backend"),
77            Backend::Custom(name) => write!(f, "Custom Backend ({name})"),
78        }
79    }
80}
81
82/// A hardware graphics API.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
84pub enum Api {
85    /// Auto-detect and choose the best available graphics [`Api`].
86    #[default]
87    Best,
88    /// Vulkan API (Windows, Linux, Android, MacOS via vulkan-portability/MoltenVK)
89    Vulkan,
90    /// Metal API (Apple platforms)
91    Metal,
92    /// Direct3D-12 (Windows)
93    DirectX12,
94    /// OpenGL 3.3+ (Windows), OpenGL ES 3.0+ (Linux, Android, MacOS via Angle), and WebGL2
95    OpenGL,
96    /// WebGPU (Web Browser)
97    WebGPU,
98}
99
100impl Default for Backend {
101    fn default() -> Self {
102        let Ok(backend) = env::var("ICED_BACKEND") else {
103            return Self::Best;
104        };
105
106        Self::Custom(backend.to_owned())
107    }
108}
109
110impl fmt::Display for Api {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        f.write_str(match self {
113            Api::Best => "Best",
114            Api::Vulkan => "Vulkan",
115            Api::Metal => "Metal",
116            Api::DirectX12 => "DirectX 12",
117            Api::OpenGL => "OpenGL",
118            Api::WebGPU => "WebGPU",
119        })
120    }
121}
122
123/// The settings usted to configure a [`Backend`].
124#[derive(Debug, Clone, PartialEq)]
125pub struct Settings {
126    /// The graphical backend to use.
127    ///
128    /// It defaults to [`Backend::Best`].
129    pub backend: Backend,
130
131    /// If set to true, the renderer will try to perform antialiasing for some
132    /// primitives.
133    ///
134    /// Enabling it can produce a smoother result in some widgets, like the
135    /// `Canvas`, at a performance cost.
136    ///
137    /// By default, it is `true`.
138    pub antialiasing: bool,
139
140    /// Whether or not to synchronize frames.
141    ///
142    /// By default, it is `true`.
143    pub vsync: bool,
144}
145
146impl Default for Settings {
147    fn default() -> Settings {
148        Settings {
149            backend: Backend::Best,
150            antialiasing: true,
151            vsync: true,
152        }
153    }
154}
155
156impl From<&crate::Settings> for Settings {
157    fn from(settings: &crate::Settings) -> Self {
158        Self {
159            backend: settings.backend.clone(),
160            antialiasing: settings.antialiasing,
161            vsync: settings.vsync,
162        }
163    }
164}
165
166/// An error that occurred while creating an application's graphical context.
167#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
168pub enum Error {
169    /// The requested backend version is not supported.
170    #[error("the requested backend version is not supported")]
171    VersionNotSupported,
172
173    /// Failed to find any pixel format that matches the criteria.
174    #[error("failed to find any pixel format that matches the criteria")]
175    NoAvailablePixelFormat,
176
177    /// A suitable graphics adapter or device could not be found.
178    #[error("a suitable graphics adapter could not be found: {reason}")]
179    GraphicsAdapterNotFound {
180        /// The name of the backend where the error happened
181        backend: &'static str,
182        /// The reason why this backend could not be used
183        reason: Reason,
184    },
185
186    /// An error occurred in the context's internal backend
187    #[error("an error occurred in the context's internal backend")]
188    BackendError(String),
189
190    /// Multiple errors occurred
191    #[error("multiple errors occurred:\n{}", error_list(.0))]
192    List(Vec<Self>),
193}
194
195/// The reason why a graphics adapter could not be found
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub enum Reason {
198    /// The backend did not match the preference
199    DidNotMatch {
200        /// The preferred backend
201        preferred_backend: Backend,
202    },
203    /// The request to create the backend failed
204    RequestFailed(String),
205}
206
207impl fmt::Display for Reason {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        match self {
210            Reason::DidNotMatch { preferred_backend } => {
211                write!(
212                    f,
213                    "the backend did not match the preference: {preferred_backend}"
214                )
215            }
216            Reason::RequestFailed(error) => f.write_str(error),
217        }
218    }
219}
220
221fn error_list(errors: &Vec<Error>) -> String {
222    let mut list = String::new();
223
224    for error in errors {
225        list.push_str("- ");
226        list.push_str(&error.to_string());
227        list.push('\n');
228    }
229
230    list
231}