1use crate::{api_log, present};
6use wgt::TextureFormat;
7
8pub(crate) fn resolve_auto_color_space(
25 format: TextureFormat,
26 color_spaces: wgt::SurfaceColorSpaces,
27) -> Option<wgt::SurfaceColorSpace> {
28 let fallbacks: &[_] = if format == TextureFormat::Rgba16Float {
29 &[
30 wgt::SurfaceColorSpace::ExtendedSrgbLinear,
31 wgt::SurfaceColorSpace::Srgb,
32 ]
33 } else {
34 &[wgt::SurfaceColorSpace::Srgb]
35 };
36 fallbacks
37 .iter()
38 .copied()
39 .find(|fallback| color_spaces.contains(fallback.to_color_spaces().unwrap()))
40}
41
42pub(crate) fn validate_surface_configuration(
45 config: &mut hal::SurfaceConfiguration,
46 caps: &hal::SurfaceCapabilities,
47 max_texture_dimension_2d: u32,
48) -> Result<(), present::ConfigureSurfaceError> {
49 use present::ConfigureSurfaceError as E;
50 let width = config.extent.width;
51 let height = config.extent.height;
52
53 if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
54 return Err(E::TooLarge {
55 width,
56 height,
57 max_texture_dimension_2d,
58 });
59 }
60
61 if !caps.present_modes.contains(&config.present_mode) {
62 let fallbacks = match config.present_mode {
66 wgt::PresentMode::AutoVsync => {
67 &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
68 }
69 wgt::PresentMode::AutoNoVsync => &[
71 wgt::PresentMode::Immediate,
72 wgt::PresentMode::Mailbox,
73 wgt::PresentMode::Fifo,
74 ][..],
75 _ => {
76 return Err(E::UnsupportedPresentMode {
77 requested: config.present_mode,
78 available: caps.present_modes.clone(),
79 });
80 }
81 };
82
83 let new_mode = fallbacks
84 .iter()
85 .copied()
86 .find(|fallback| caps.present_modes.contains(fallback))
87 .unwrap_or_else(|| {
88 unreachable!(
89 "Fallback system failed to choose present mode. \
90 This is a bug. Mode: {:?}, Options: {:?}",
91 config.present_mode, &caps.present_modes
92 );
93 });
94
95 api_log!(
96 "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
97 config.present_mode
98 );
99 config.present_mode = new_mode;
100 }
101 let Some(format_caps) = caps.formats.iter().find(|fc| fc.format == config.format) else {
102 return Err(E::UnsupportedFormat {
103 requested: config.format,
104 available: caps.texture_formats().collect(),
105 });
106 };
107 if config.color_space == wgt::SurfaceColorSpace::Auto {
108 let Some(new_color_space) =
109 resolve_auto_color_space(config.format, format_caps.color_spaces)
110 else {
111 return Err(E::UnsupportedColorSpace {
115 requested: config.color_space,
116 format: config.format,
117 available: format_caps.color_spaces,
118 });
119 };
120
121 api_log!(
122 "Automatically choosing color space by rule {:?}. Chose {new_color_space:?}",
123 config.color_space
124 );
125 config.color_space = new_color_space;
126 }
127 if !format_caps
128 .color_spaces
129 .contains(config.color_space.to_color_spaces().unwrap())
130 {
131 return Err(E::UnsupportedColorSpace {
132 requested: config.color_space,
133 format: config.format,
134 available: format_caps.color_spaces,
135 });
136 }
137 if !caps
138 .composite_alpha_modes
139 .contains(&config.composite_alpha_mode)
140 {
141 let new_alpha_mode = 'alpha: {
142 let fallbacks = match config.composite_alpha_mode {
144 wgt::CompositeAlphaMode::Auto => &[
145 wgt::CompositeAlphaMode::Opaque,
146 wgt::CompositeAlphaMode::Inherit,
147 ][..],
148 _ => {
149 return Err(E::UnsupportedAlphaMode {
150 requested: config.composite_alpha_mode,
151 available: caps.composite_alpha_modes.clone(),
152 });
153 }
154 };
155
156 for &fallback in fallbacks {
157 if caps.composite_alpha_modes.contains(&fallback) {
158 break 'alpha fallback;
159 }
160 }
161
162 unreachable!(
163 "Fallback system failed to choose alpha mode. This is a bug. \
164 AlphaMode: {:?}, Options: {:?}",
165 config.composite_alpha_mode, &caps.composite_alpha_modes
166 );
167 };
168
169 api_log!(
170 "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
171 config.composite_alpha_mode
172 );
173 config.composite_alpha_mode = new_alpha_mode;
174 }
175 if !caps.usage.contains(config.usage) {
176 return Err(E::UnsupportedUsage {
177 requested: config.usage,
178 available: caps.usage,
179 });
180 }
181 if width == 0 || height == 0 {
182 return Err(E::ZeroArea);
183 }
184 Ok(())
185}
186
187#[cfg(test)]
188mod surface_configuration_tests {
189 use alloc::{vec, vec::Vec};
190
191 use super::validate_surface_configuration;
192 use crate::present::ConfigureSurfaceError;
193
194 fn caps(formats: Vec<wgt::SurfaceFormatCapabilities>) -> hal::SurfaceCapabilities {
195 hal::SurfaceCapabilities {
196 formats,
197 maximum_frame_latency: 1..=3,
198 current_extent: None,
199 usage: wgt::TextureUses::COLOR_TARGET,
200 present_modes: vec![wgt::PresentMode::Fifo],
201 composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque],
202 }
203 }
204
205 fn config(
206 format: wgt::TextureFormat,
207 color_space: wgt::SurfaceColorSpace,
208 ) -> hal::SurfaceConfiguration {
209 hal::SurfaceConfiguration {
210 maximum_frame_latency: 2,
211 present_mode: wgt::PresentMode::Fifo,
212 composite_alpha_mode: wgt::CompositeAlphaMode::Opaque,
213 format,
214 color_space,
215 extent: wgt::Extent3d {
216 width: 100,
217 height: 100,
218 depth_or_array_layers: 1,
219 },
220 usage: wgt::TextureUses::COLOR_TARGET,
221 view_formats: Vec::new(),
222 }
223 }
224
225 fn format_caps(
226 format: wgt::TextureFormat,
227 color_spaces: wgt::SurfaceColorSpaces,
228 ) -> wgt::SurfaceFormatCapabilities {
229 wgt::SurfaceFormatCapabilities {
230 format,
231 color_spaces,
232 }
233 }
234
235 #[test]
238 fn auto_resolves_fp16_to_extended_srgb_linear() {
239 let caps = caps(vec![format_caps(
240 wgt::TextureFormat::Rgba16Float,
241 wgt::SurfaceColorSpaces::EXTENDED_SRGB_LINEAR | wgt::SurfaceColorSpaces::BT2100_PQ,
242 )]);
243 let mut config = config(
244 wgt::TextureFormat::Rgba16Float,
245 wgt::SurfaceColorSpace::Auto,
246 );
247 validate_surface_configuration(&mut config, &caps, 4096).unwrap();
248 assert_eq!(
249 config.color_space,
250 wgt::SurfaceColorSpace::ExtendedSrgbLinear
251 );
252 }
253
254 #[test]
258 fn auto_never_resolves_to_extended_srgb() {
259 let caps = caps(vec![format_caps(
260 wgt::TextureFormat::Rgba16Float,
261 wgt::SurfaceColorSpaces::SRGB | wgt::SurfaceColorSpaces::EXTENDED_SRGB,
262 )]);
263 let mut config = config(
264 wgt::TextureFormat::Rgba16Float,
265 wgt::SurfaceColorSpace::Auto,
266 );
267 validate_surface_configuration(&mut config, &caps, 4096).unwrap();
268 assert_eq!(config.color_space, wgt::SurfaceColorSpace::Srgb);
269 }
270
271 #[test]
274 fn auto_resolves_fp16_to_srgb_without_extended() {
275 let caps = caps(vec![format_caps(
276 wgt::TextureFormat::Rgba16Float,
277 wgt::SurfaceColorSpaces::SRGB,
278 )]);
279 let mut config = config(
280 wgt::TextureFormat::Rgba16Float,
281 wgt::SurfaceColorSpace::Auto,
282 );
283 validate_surface_configuration(&mut config, &caps, 4096).unwrap();
284 assert_eq!(config.color_space, wgt::SurfaceColorSpace::Srgb);
285 }
286
287 #[test]
291 fn auto_refuses_hdr_only_formats() {
292 let caps = caps(vec![format_caps(
293 wgt::TextureFormat::Rgb10a2Unorm,
294 wgt::SurfaceColorSpaces::BT2100_PQ,
295 )]);
296 let mut config = config(
297 wgt::TextureFormat::Rgb10a2Unorm,
298 wgt::SurfaceColorSpace::Auto,
299 );
300 let err = validate_surface_configuration(&mut config, &caps, 4096).unwrap_err();
301 assert!(matches!(
302 err,
303 ConfigureSurfaceError::UnsupportedColorSpace { .. }
304 ));
305 }
306
307 #[test]
310 fn auto_prefers_srgb_for_non_fp16() {
311 let caps = caps(vec![format_caps(
312 wgt::TextureFormat::Rgb10a2Unorm,
313 wgt::SurfaceColorSpaces::SRGB | wgt::SurfaceColorSpaces::BT2100_PQ,
314 )]);
315 let mut config = config(
316 wgt::TextureFormat::Rgb10a2Unorm,
317 wgt::SurfaceColorSpace::Auto,
318 );
319 validate_surface_configuration(&mut config, &caps, 4096).unwrap();
320 assert_eq!(config.color_space, wgt::SurfaceColorSpace::Srgb);
321 }
322
323 #[test]
328 fn auto_non_fp16_with_srgb_and_extended_linear_resolves_to_srgb() {
329 let caps = caps(vec![format_caps(
330 wgt::TextureFormat::Rgb10a2Unorm,
331 wgt::SurfaceColorSpaces::SRGB | wgt::SurfaceColorSpaces::EXTENDED_SRGB_LINEAR,
332 )]);
333 let mut config = config(
334 wgt::TextureFormat::Rgb10a2Unorm,
335 wgt::SurfaceColorSpace::Auto,
336 );
337 validate_surface_configuration(&mut config, &caps, 4096).unwrap();
338 assert_eq!(config.color_space, wgt::SurfaceColorSpace::Srgb);
339 }
340
341 #[test]
343 fn explicit_hdr10_is_honored() {
344 let caps = caps(vec![format_caps(
345 wgt::TextureFormat::Rgb10a2Unorm,
346 wgt::SurfaceColorSpaces::SRGB | wgt::SurfaceColorSpaces::BT2100_PQ,
347 )]);
348 let mut config = config(
349 wgt::TextureFormat::Rgb10a2Unorm,
350 wgt::SurfaceColorSpace::Bt2100Pq,
351 );
352 validate_surface_configuration(&mut config, &caps, 4096).unwrap();
353 assert_eq!(config.color_space, wgt::SurfaceColorSpace::Bt2100Pq);
354 }
355
356 #[test]
358 fn explicit_unsupported_color_space_errors() {
359 let caps = caps(vec![format_caps(
360 wgt::TextureFormat::Bgra8UnormSrgb,
361 wgt::SurfaceColorSpaces::SRGB,
362 )]);
363 let mut config = config(
364 wgt::TextureFormat::Bgra8UnormSrgb,
365 wgt::SurfaceColorSpace::Bt2100Pq,
366 );
367 let err = validate_surface_configuration(&mut config, &caps, 4096).unwrap_err();
368 assert!(matches!(
369 err,
370 ConfigureSurfaceError::UnsupportedColorSpace {
371 requested: wgt::SurfaceColorSpace::Bt2100Pq,
372 ..
373 }
374 ));
375 }
376}