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#[derive(Debug, Clone, PartialEq, Copy, Default)]
124/// The power-usage preference for graphics adapters.
125pub enum PowerPreference {
126    /// No power preference in which adapter should be chosen.
127    ///
128    /// This is the default.
129    #[default]
130    None,
131
132    /// The backend will prefer low power adapters over high performance ones.
133    LowPower,
134
135    /// The backend will prefer adapters that are high performance.
136    HighPerformance,
137}
138
139/// The settings usted to configure a [`Backend`].
140#[derive(Debug, Clone, PartialEq)]
141pub struct Settings {
142    /// The graphical backend to use.
143    ///
144    /// By default, it is [`Backend::Best`].
145    pub backend: Backend,
146
147    /// The [`PowerPreference`] of the backend.
148    ///
149    /// By default, it is [`PowerPreference::None`].
150    pub power_preference: PowerPreference,
151
152    /// If set to true, the renderer will try to perform antialiasing for some
153    /// primitives.
154    ///
155    /// Enabling it can produce a smoother result in some widgets, like the
156    /// `Canvas`, at a performance cost.
157    ///
158    /// By default, it is `true`.
159    pub antialiasing: bool,
160
161    /// Whether or not to synchronize frames.
162    ///
163    /// By default, it is `true`.
164    pub vsync: bool,
165}
166
167impl Default for Settings {
168    fn default() -> Settings {
169        Settings {
170            backend: Backend::Best,
171            antialiasing: true,
172            vsync: true,
173            power_preference: PowerPreference::None,
174        }
175    }
176}
177
178impl From<&crate::Settings> for Settings {
179    fn from(settings: &crate::Settings) -> Self {
180        Self {
181            backend: settings.backend.clone(),
182            antialiasing: settings.antialiasing,
183            vsync: settings.vsync,
184            power_preference: settings.power_preference,
185        }
186    }
187}
188
189/// An error that occurred while creating an application's graphical context.
190#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
191pub enum Error {
192    /// The requested backend version is not supported.
193    #[error("the requested backend version is not supported")]
194    VersionNotSupported,
195
196    /// Failed to find any pixel format that matches the criteria.
197    #[error("failed to find any pixel format that matches the criteria")]
198    NoAvailablePixelFormat,
199
200    /// A suitable graphics adapter or device could not be found.
201    #[error("a suitable graphics adapter could not be found: {reason}")]
202    GraphicsAdapterNotFound {
203        /// The name of the backend where the error happened
204        backend: &'static str,
205        /// The reason why this backend could not be used
206        reason: Reason,
207    },
208
209    /// An error occurred in the context's internal backend
210    #[error("an error occurred in the context's internal backend")]
211    BackendError(String),
212
213    /// Multiple errors occurred
214    #[error("multiple errors occurred:\n{}", error_list(.0))]
215    List(Vec<Self>),
216}
217
218/// The reason why a graphics adapter could not be found
219#[derive(Debug, Clone, PartialEq, Eq)]
220pub enum Reason {
221    /// The backend did not match the preference
222    DidNotMatch {
223        /// The preferred backend
224        preferred_backend: Backend,
225    },
226    /// The request to create the backend failed
227    RequestFailed(String),
228}
229
230impl fmt::Display for Reason {
231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232        match self {
233            Reason::DidNotMatch { preferred_backend } => {
234                write!(
235                    f,
236                    "the backend did not match the preference: {preferred_backend}"
237                )
238            }
239            Reason::RequestFailed(error) => f.write_str(error),
240        }
241    }
242}
243
244fn error_list(errors: &Vec<Error>) -> String {
245    let mut list = String::new();
246
247    for error in errors {
248        list.push_str("- ");
249        list.push_str(&error.to_string());
250        list.push('\n');
251    }
252
253    list
254}