1use std::env;
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Backend {
13 Best,
15 Hardware(Api),
17 Software,
19 Custom(String),
21}
22
23impl Backend {
24 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 pub fn hardware(&self) -> Option<Api> {
38 match self {
39 Backend::Hardware(api) => Some(*api),
40 _ => None,
41 }
42 }
43
44 pub fn is_software(&self) -> bool {
46 matches!(self, Self::Software)
47 }
48
49 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
84pub enum Api {
85 #[default]
87 Best,
88 Vulkan,
90 Metal,
92 DirectX12,
94 OpenGL,
96 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)]
125pub struct Settings {
126 pub backend: Backend,
130
131 pub antialiasing: bool,
139
140 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#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
168pub enum Error {
169 #[error("the requested backend version is not supported")]
171 VersionNotSupported,
172
173 #[error("failed to find any pixel format that matches the criteria")]
175 NoAvailablePixelFormat,
176
177 #[error("a suitable graphics adapter could not be found: {reason}")]
179 GraphicsAdapterNotFound {
180 backend: &'static str,
182 reason: Reason,
184 },
185
186 #[error("an error occurred in the context's internal backend")]
188 BackendError(String),
189
190 #[error("multiple errors occurred:\n{}", error_list(.0))]
192 List(Vec<Self>),
193}
194
195#[derive(Debug, Clone, PartialEq, Eq)]
197pub enum Reason {
198 DidNotMatch {
200 preferred_backend: Backend,
202 },
203 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}