wgpu_types/surface.rs
1//! Surface presentation configuration: present modes, alpha compositing, and
2//! color-space types (HDR and wide-gamut output).
3//!
4//! This module is re-exported flatly from `wgpu-types`; the user-facing color
5//! space and HDR primer lives in the `wgpu` crate's top-level docs.
6
7use alloc::{vec, vec::Vec};
8
9use crate::{link_to_wgpu_docs, link_to_wgpu_item, TextureFormat, TextureUsages};
10
11#[cfg(any(feature = "serde", test))]
12use serde::{Deserialize, Serialize};
13
14/// Timing and queueing with which frames are actually displayed to the user.
15///
16/// Use this as part of a [`SurfaceConfiguration`] to control the behavior of
17/// [`SurfaceTexture::present()`].
18///
19/// Some modes are only supported by some backends.
20/// You can use one of the `Auto*` modes, [`Fifo`](Self::Fifo),
21/// or choose one of the supported modes from [`SurfaceCapabilities::present_modes`].
22///
23#[doc = link_to_wgpu_docs!(["presented"]: "struct.SurfaceTexture.html#method.present")]
24#[doc = link_to_wgpu_docs!(["`SurfaceTexture::present()`"]: "struct.SurfaceTexture.html#method.present")]
25#[repr(C)]
26#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub enum PresentMode {
29 /// Chooses the first supported mode out of:
30 ///
31 /// 1. [`FifoRelaxed`](Self::FifoRelaxed)
32 /// 2. [`Fifo`](Self::Fifo)
33 ///
34 /// Because of the fallback behavior, this is supported everywhere.
35 AutoVsync = 0,
36
37 /// Chooses the first supported mode out of:
38 ///
39 /// 1. [`Immediate`](Self::Immediate)
40 /// 2. [`Mailbox`](Self::Mailbox)
41 /// 3. [`Fifo`](Self::Fifo)
42 ///
43 /// Because of the fallback behavior, this is supported everywhere.
44 AutoNoVsync = 1,
45
46 /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames
47 /// long. Every vertical blanking period, the presentation engine will pop a frame
48 /// off the queue to display. If there is no frame to display, it will present the same
49 /// frame again until the next vblank.
50 ///
51 /// When a present command is executed on the GPU, the presented image is added on the queue.
52 ///
53 /// Calls to [`Surface::get_current_texture()`] will block until there is a spot in the queue.
54 ///
55 /// * **Tearing:** No tearing will be observed.
56 /// * **Supported on**: All platforms.
57 /// * **Also known as**: "Vsync On"
58 ///
59 /// This is the [default](Self::default) value for `PresentMode`.
60 /// If you don't know what mode to choose, choose this mode.
61 ///
62 #[doc = link_to_wgpu_docs!(["`Surface::get_current_texture()`"]: "struct.Surface.html#method.get_current_texture")]
63 #[default]
64 Fifo = 2,
65
66 /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames
67 /// long. Every vertical blanking period, the presentation engine will pop a frame
68 /// off the queue to display. If there is no frame to display, it will present the
69 /// same frame until there is a frame in the queue. The moment there is a frame in the
70 /// queue, it will immediately pop the frame off the queue.
71 ///
72 /// When a present command is executed on the GPU, the presented image is added on the queue.
73 ///
74 /// Calls to [`Surface::get_current_texture()`] will block until there is a spot in the queue.
75 ///
76 /// * **Tearing**:
77 /// Tearing will be observed if frames last more than one vblank as the front buffer.
78 /// * **Supported on**: AMD on Vulkan.
79 /// * **Also known as**: "Adaptive Vsync"
80 ///
81 #[doc = link_to_wgpu_docs!(["`Surface::get_current_texture()`"]: "struct.Surface.html#method.get_current_texture")]
82 FifoRelaxed = 3,
83
84 /// Presentation frames are not queued at all. The moment a present command
85 /// is executed on the GPU, the presented image is swapped onto the front buffer
86 /// immediately.
87 ///
88 /// * **Tearing**: Tearing can be observed.
89 /// * **Supported on**: Most platforms except older DX12 and Wayland.
90 /// * **Also known as**: "Vsync Off"
91 Immediate = 4,
92
93 /// Presentation frames are kept in a single-frame queue. Every vertical blanking period,
94 /// the presentation engine will pop a frame from the queue. If there is no frame to display,
95 /// it will present the same frame again until the next vblank.
96 ///
97 /// When a present command is executed on the GPU, the frame will be put into the queue.
98 /// If there was already a frame in the queue, the new frame will _replace_ the old frame
99 /// on the queue.
100 ///
101 /// * **Tearing**: No tearing will be observed.
102 /// * **Supported on**: DX12 on Windows 10, NVidia on Vulkan and Wayland on Vulkan.
103 /// * **Also known as**: "Fast Vsync"
104 Mailbox = 5,
105}
106
107/// Specifies how the alpha channel of the textures should be handled during
108/// compositing.
109#[repr(C)]
110#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
113pub enum CompositeAlphaMode {
114 /// Chooses either `Opaque` or `Inherit` automatically, depending on the
115 /// `alpha_mode` that the current surface can support.
116 #[default]
117 Auto = 0,
118 /// The alpha channel, if it exists, of the textures is ignored in the
119 /// compositing process. Instead, the textures is treated as if it has a
120 /// constant alpha of 1.0.
121 Opaque = 1,
122 /// The alpha channel, if it exists, of the textures is respected in the
123 /// compositing process. The non-alpha channels of the textures are
124 /// expected to already be multiplied by the alpha channel by the
125 /// application.
126 PreMultiplied = 2,
127 /// The alpha channel, if it exists, of the textures is respected in the
128 /// compositing process. The non-alpha channels of the textures are not
129 /// expected to already be multiplied by the alpha channel by the
130 /// application; instead, the compositor will multiply the non-alpha
131 /// channels of the texture by the alpha channel during compositing.
132 PostMultiplied = 3,
133 /// The alpha channel, if it exists, of the textures is unknown for processing
134 /// during compositing. Instead, the application is responsible for setting
135 /// the composite alpha blending mode using native WSI command. If not set,
136 /// then a platform-specific default will be used.
137 Inherit = 4,
138}
139
140/// The color space in which the presentation engine interprets the values
141/// written to a surface texture.
142///
143/// A color space defines the *primaries*, *white point*, and *transfer
144/// function* of the output signal (see the Terminology section below),
145/// following the same convention as [CSS predefined color spaces] and
146/// [`VkColorSpaceKHR`].
147/// It does **not** change the texel format of the surface; it changes how the
148/// compositor / display pipeline interprets those texels.
149///
150/// Support is queried via [`SurfaceCapabilities`], which reports a set of
151/// [`SurfaceColorSpaces`] for every supported texture format. Selecting a
152/// color space other than [`Srgb`](Self::Srgb) is how an application opts a
153/// surface into high-dynamic-range (HDR) or wide-color-gamut output on
154/// platforms that support it.
155///
156/// New to HDR? The `wgpu` crate's top-level docs include a [color space and HDR
157/// primer] covering the concepts and the steps to get HDR output on screen.
158///
159/// # Terminology
160///
161/// Each variant is described by four properties:
162///
163/// * **Primaries** (the *gamut*): the chromaticities of the red, green, and
164/// blue that color values address, and so the range of colors that can be
165/// expressed. [BT.709] (the sRGB / HDTV primaries) is the standard-gamut set;
166/// [Display P3] and [BT.2020] are progressively wider.
167/// * **White point**: the chromaticity produced by equal red, green, and blue.
168/// Every color space here uses [D65], the standard daylight white.
169/// * **Transfer function** (the *OETF*): how stored values map to light, such
170/// as the [sRGB] transfer function, a linear transfer, or an HDR transfer
171/// function like [PQ] or [HLG]. Your shader applies this encoding transfer
172/// function; the display applies the inverse (the *EOTF*). Except for writes
173/// to an `*Srgb` texture format (where the hardware applies the sRGB encoding
174/// for you), wgpu does **not** encode for you: the values your shader writes
175/// to the surface texture must already be in whatever encoding the chosen
176/// color space expects (linear for a linear transfer). The [HDR surface
177/// example] shows the encoder each variant expects.
178/// * **Dynamic range**: standard dynamic range (SDR), where `1.0` is reference
179/// (SDR) white and values outside 0.0..=1.0 are clamped, or high dynamic
180/// range (HDR), where `(1.0, 1.0, 1.0)` is SDR reference white and values
181/// above `1.0` drive brighter-than-SDR output on HDR displays.
182///
183#[doc = include_str!("color_gamuts.svg")]
184///
185/// *The primaries of each color space form a triangle on the CIE 1931
186/// chromaticity diagram; colors inside it are expressible, colors outside are
187/// not. [`Srgb`](Self::Srgb) uses the [BT.709] gamut;
188/// [`DisplayP3`](Self::DisplayP3) and [`Bt2100Pq`](Self::Bt2100Pq)'s [BT.2020]
189/// are progressively wider. All share the [D65] white point.*
190///
191#[doc = include_str!("sdr_hdr_range.svg")]
192///
193/// *`0.0` is black and `1.0` is SDR reference white. [`Srgb`](Self::Srgb) and
194/// [`DisplayP3`](Self::DisplayP3) clamp above `1.0`; the extended-range and HDR
195/// color spaces drive values above `1.0` as brighter-than-SDR output, up to the
196/// display's headroom (query it via [`DisplayHdrInfo::tone_map_headroom`]).*
197///
198/// # Extended-range variants: linear vs encoded
199///
200/// The extended-range color spaces come in two forms that share a range but
201/// differ in transfer: [`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear) carries
202/// a **linear** signal, while [`ExtendedSrgb`](Self::ExtendedSrgb) and
203/// [`ExtendedDisplayP3`](Self::ExtendedDisplayP3) carry the **sRGB-encoded**
204/// (gamma) signal: the sRGB transfer function is applied as usual and then
205/// continued to values above `1.0` (brighter than SDR white) and below `0.0`
206/// (colors outside the base gamut). Pick by whether the values your shader
207/// writes to the surface texture are linear or encoded; confusing the two is the
208/// most common HDR setup mistake.
209///
210/// # Web (WebGPU) backend
211///
212/// Browsers do not expose these named color spaces directly. A WebGPU canvas is
213/// configured with a [`colorSpace`] (only `"srgb"` or `"display-p3"`) plus a
214/// separate [`toneMapping`] mode (`"standard"` or `"extended"`), so on the web
215/// wgpu offers only the combinations that pair can produce: [`Srgb`](Self::Srgb)
216/// and [`DisplayP3`](Self::DisplayP3) with standard tone mapping, plus their
217/// extended-range HDR forms [`ExtendedSrgb`](Self::ExtendedSrgb) and
218/// [`ExtendedDisplayP3`](Self::ExtendedDisplayP3) with extended tone mapping.
219/// There is no linear-transfer canvas color space, so
220/// [`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear) (scRGB) is native-only, and
221/// [`Bt2100Pq`](Self::Bt2100Pq) and [`Bt2100Hlg`](Self::Bt2100Hlg) are
222/// unavailable (browsers expose no PQ or HLG canvas signaling).
223///
224/// [CSS predefined color spaces]: https://www.w3.org/TR/css-color-4/#predefined
225/// [`VkColorSpaceKHR`]: https://registry.khronos.org/vulkan/specs/latest/man/html/VkColorSpaceKHR.html
226///
227/// [BT.709]: https://www.itu.int/rec/R-REC-BT.709
228/// [BT.2020]: https://www.itu.int/rec/R-REC-BT.2020
229/// [Display P3]: https://en.wikipedia.org/wiki/DCI-P3#Display_P3
230/// [D65]: https://en.wikipedia.org/wiki/Standard_illuminant#D65_values
231/// [sRGB]: https://registry.color.org/rgb-registry/srgb
232/// [PQ]: https://en.wikipedia.org/wiki/Perceptual_quantizer
233/// [HLG]: https://www.itu.int/rec/R-REC-BT.2100
234/// [HDR surface example]: https://github.com/gfx-rs/wgpu/tree/trunk/examples/standalone/03_hdr_surface
235///
236/// [`colorSpace`]: https://www.w3.org/TR/webgpu/#dom-gpucanvasconfiguration-colorspace
237/// [`toneMapping`]: https://www.w3.org/TR/webgpu/#gpucanvastonemappingmode
238#[doc = link_to_wgpu_docs!(["color space and HDR primer"]: "index.html#surface-color-spaces-and-hdr-output")]
239#[repr(C)]
240#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
241#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
242pub enum SurfaceColorSpace {
243 /// Let the backend choose a color space, reproducing wgpu's historical
244 /// behavior:
245 ///
246 /// * [`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear) if the format is
247 /// [`TextureFormat::Rgba16Float`] and the surface supports it for that
248 /// format,
249 /// * otherwise [`Srgb`](Self::Srgb), if the surface supports it for the
250 /// format.
251 ///
252 /// Apart from the linear [`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear)
253 /// above (which fp16 surfaces have historically used and which needs no
254 /// extra encoding), `Auto` never resolves to a wide-gamut or HDR color
255 /// space, since those would change how the application must encode its
256 /// output. If a format is only available in such color spaces (which some
257 /// drivers report when the OS is in HDR mode), configuring it with `Auto`
258 /// fails validation; such formats are listed in
259 /// [`SurfaceCapabilities::format_capabilities`] but excluded from
260 /// [`SurfaceCapabilities::formats`].
261 ///
262 /// On the browser WebGPU backend, `Auto` always keeps the canvas
263 /// defaults (sRGB with standard tone mapping), even for
264 /// [`TextureFormat::Rgba16Float`]; request
265 /// [`ExtendedSrgb`](Self::ExtendedSrgb) explicitly for HDR canvas output
266 /// ([`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear) is native-only).
267 #[default]
268 Auto = 0,
269
270 /// The sRGB color space: BT.709 primaries, D65 white point, sRGB transfer
271 /// function, standard dynamic range.
272 ///
273 /// Values outside of 0.0..=1.0 (after format encoding) are clamped by
274 /// the display pipeline.
275 ///
276 /// This is what every backend produces today for non-`Rgba16Float`
277 /// formats and is supported everywhere.
278 ///
279 /// Note that the transfer function is applied by the *format*, not this
280 /// color space choice: an `*Srgb` format applies sRGB encoding on write,
281 /// while writes to a non-`*Srgb` format are interpreted as already
282 /// sRGB-encoded.
283 Srgb = 1,
284
285 /// Extended linear sRGB, also known as [scRGB] (the **linear** encoding of
286 /// IEC 61966-2-2): BT.709 primaries, D65 white point, **linear** transfer
287 /// function, extended dynamic range. Typically used with
288 /// [`TextureFormat::Rgba16Float`].
289 ///
290 /// The linear counterpart to the sRGB-encoded
291 /// [`ExtendedSrgb`](Self::ExtendedSrgb) (IEC 61966-2-2 defines both); pick
292 /// this one if the values your shader writes to the surface texture are
293 /// **linear**.
294 ///
295 /// This corresponds to Vulkan's `VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT`,
296 /// Metal's extended dynamic range (EDR), and DXGI's
297 /// `DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709`.
298 ///
299 /// * **Supported on**: native only (Vulkan, Metal, DX12). Not available on
300 /// the browser WebGPU backend, which cannot express a linear-transfer
301 /// canvas color space; use [`ExtendedSrgb`](Self::ExtendedSrgb) for HDR
302 /// canvas output on the web.
303 /// * **Also known as**: scRGB.
304 ///
305 /// [scRGB]: https://en.wikipedia.org/wiki/ScRGB
306 ExtendedSrgbLinear = 2,
307
308 /// The [Display P3] color space: P3 primaries, D65 white point, sRGB
309 /// transfer function, standard dynamic range.
310 ///
311 /// A wide-gamut SDR color space covering roughly 25% more area than sRGB in
312 /// the CIE chromaticity diagram. It uses the wide P3 primaries of theatrical
313 /// DCI-P3, but with the D65 white point and the sRGB transfer function (not
314 /// DCI's white point and 2.6 gamma).
315 ///
316 /// Like [`Srgb`](Self::Srgb), this is standard dynamic range (values outside
317 /// 0.0..=1.0 are clamped). For wide-gamut HDR that keeps the P3 primaries
318 /// but extends the range, use [`ExtendedDisplayP3`](Self::ExtendedDisplayP3).
319 ///
320 /// * **Supported on**: Vulkan (where the driver exposes it), Metal, and the
321 /// browser WebGPU backend (canvas color space `"display-p3"`). Not
322 /// reported on DX12.
323 ///
324 /// [Display P3]: https://en.wikipedia.org/wiki/DCI-P3#Display_P3
325 DisplayP3 = 3,
326
327 /// BT.2100 perceptual quantization (HDR10): BT.2020/2100 primaries, D65 white
328 /// point, SMPTE ST 2084 perceptual quantizer ([PQ]) transfer function, high
329 /// dynamic range.
330 ///
331 /// Texel values are interpreted as a PQ-encoded signal whose encoded range,
332 /// `0.0..=1.0`, maps to absolute luminance from 0 to 10,000 nits. The values
333 /// your shader writes to the surface texture must already be in the BT.2020
334 /// gamut and PQ-encoded into that `0.0..=1.0` range; the [HDR surface example]
335 /// shows how. The format is non-sRGB, typically
336 /// [`TextureFormat::Rgb10a2Unorm`].
337 ///
338 /// Commonly known as **HDR10** — though that term additionally implies static
339 /// ST 2086 / MaxCLL mastering metadata, which wgpu does not set; this
340 /// configures only the PQ color space.
341 ///
342 /// * **Supported on**: Vulkan (where the driver exposes it), DX12 (on
343 /// `Rgb10a2Unorm`), and Metal. Unavailable on the browser WebGPU backend
344 /// (no PQ canvas signaling).
345 ///
346 /// [PQ]: https://en.wikipedia.org/wiki/Perceptual_quantizer
347 /// [HDR surface example]: https://github.com/gfx-rs/wgpu/tree/trunk/examples/standalone/03_hdr_surface
348 Bt2100Pq = 4,
349
350 /// BT.2100 hybrid log-gamma: BT.2020/2100 primaries, D65 white point, [HLG]
351 /// (ARIB STD-B67) transfer function, high dynamic range.
352 ///
353 /// A relative-luminance HDR signal, primarily used for broadcast content. The
354 /// values your shader writes to the surface texture must already be in the
355 /// BT.2020 gamut and HLG-encoded into `0.0..=1.0`; the [HDR surface example]
356 /// shows how. The format is non-sRGB, typically
357 /// [`TextureFormat::Rgb10a2Unorm`].
358 ///
359 /// Unlike [`Bt2100Pq`](Self::Bt2100Pq)'s PQ, the HLG signal is *relative*:
360 /// `1.0` maps to the display's nominal peak luminance rather than a fixed
361 /// absolute level. BT.2100 defines its reference OOTF at a nominal peak of
362 /// 1000 cd/m² (system gamma 1.2); the [HDR surface example] normalizes its
363 /// absolute-nit test pattern onto that 1000-nit nominal peak.
364 ///
365 /// * **Supported on**: Vulkan (where the driver exposes it) and Metal.
366 /// Unavailable on DX12 and the browser WebGPU backend (no HLG canvas
367 /// signaling).
368 ///
369 /// [HLG]: https://www.itu.int/rec/R-REC-BT.2100
370 /// [HDR surface example]: https://github.com/gfx-rs/wgpu/tree/trunk/examples/standalone/03_hdr_surface
371 Bt2100Hlg = 5,
372
373 /// Extended-range sRGB (encoded): BT.709 primaries, D65 white point, the
374 /// **nonlinear sRGB transfer function extended beyond 0.0..=1.0**,
375 /// extended dynamic range.
376 ///
377 /// The sRGB-encoded sibling of
378 /// [`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear): the signal is
379 /// sRGB-*encoded* (gamma), not linear. Typically used with
380 /// [`TextureFormat::Rgba16Float`].
381 ///
382 /// If the values your shader writes to the surface texture are **linear**,
383 /// you want [`ExtendedSrgbLinear`](Self::ExtendedSrgbLinear) (scRGB) instead;
384 /// confusing the two is the most common HDR setup mistake. See the [HDR
385 /// surface example] for the encoder.
386 ///
387 /// This is the "encoded extended range" sRGB used by browser WebGPU (canvas
388 /// color space `"srgb"` with `"extended"` tone mapping). It corresponds to
389 /// Vulkan's `VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT` and Metal's
390 /// `kCGColorSpaceExtendedSRGB`.
391 ///
392 /// * **Supported on**: Vulkan (where the driver exposes it), Metal, and the
393 /// browser WebGPU backend. Not available on DX12, which has no
394 /// encoded-extended-sRGB swapchain color space.
395 ///
396 /// [HDR surface example]: https://github.com/gfx-rs/wgpu/tree/trunk/examples/standalone/03_hdr_surface
397 ExtendedSrgb = 6,
398
399 /// Extended-range Display-P3 (encoded): P3 primaries, D65 white point, the
400 /// **nonlinear sRGB transfer function extended beyond 0.0..=1.0**,
401 /// extended dynamic range.
402 ///
403 /// The wide-gamut (P3) analogue of [`ExtendedSrgb`](Self::ExtendedSrgb), and
404 /// the HDR counterpart to the SDR-only [`DisplayP3`](Self::DisplayP3): it
405 /// keeps the P3 primaries but extends the encoded range for HDR. Like
406 /// [`ExtendedSrgb`](Self::ExtendedSrgb) the signal is sRGB-*encoded* (gamma),
407 /// not linear. Typically used with [`TextureFormat::Rgba16Float`].
408 ///
409 /// * **Supported on**: Metal and the browser WebGPU backend (canvas color
410 /// space `"display-p3"` with `"extended"` tone mapping; Metal's
411 /// `kCGColorSpaceExtendedDisplayP3`). Not available on Vulkan or DX12,
412 /// neither of which has an encoded-extended-Display-P3 swapchain color
413 /// space.
414 ExtendedDisplayP3 = 7,
415}
416
417impl SurfaceColorSpace {
418 /// Returns the [`SurfaceColorSpaces`] flag set holding just this color space,
419 /// or `None` for [`Auto`](Self::Auto) (which maps to no specific flag).
420 #[must_use]
421 pub const fn to_color_spaces(self) -> Option<SurfaceColorSpaces> {
422 match self {
423 Self::Auto => None,
424 Self::Srgb => Some(SurfaceColorSpaces::SRGB),
425 Self::ExtendedSrgbLinear => Some(SurfaceColorSpaces::EXTENDED_SRGB_LINEAR),
426 Self::DisplayP3 => Some(SurfaceColorSpaces::DISPLAY_P3),
427 Self::Bt2100Pq => Some(SurfaceColorSpaces::BT2100_PQ),
428 Self::Bt2100Hlg => Some(SurfaceColorSpaces::BT2100_HLG),
429 Self::ExtendedSrgb => Some(SurfaceColorSpaces::EXTENDED_SRGB),
430 Self::ExtendedDisplayP3 => Some(SurfaceColorSpaces::EXTENDED_DISPLAY_P3),
431 }
432 }
433
434 /// Whether this is a high-dynamic-range color space: one that drives values
435 /// above SDR white (`1.0`) as brighter-than-white output.
436 ///
437 /// `true` for the extended-range and PQ/HLG spaces; `false` for the SDR ones
438 /// ([`Srgb`](Self::Srgb) and the wide-gamut-but-SDR
439 /// [`DisplayP3`](Self::DisplayP3)). [`Auto`](Self::Auto) is `false`: it defers
440 /// to the backend and is the SDR-safe default, so check the resolved color
441 /// space if you need certainty.
442 ///
443 /// Use this to branch after picking a color space from
444 /// [`SurfaceCapabilities`]: an HDR result is the one whose highlights you
445 /// scale by [`DisplayHdrInfo::tone_map_headroom`].
446 #[must_use]
447 pub const fn is_hdr(self) -> bool {
448 match self {
449 Self::ExtendedSrgbLinear
450 | Self::ExtendedSrgb
451 | Self::ExtendedDisplayP3
452 | Self::Bt2100Pq
453 | Self::Bt2100Hlg => true,
454 Self::Auto | Self::Srgb | Self::DisplayP3 => false,
455 }
456 }
457}
458
459bitflags::bitflags! {
460 /// A set of [`SurfaceColorSpace`]s supported by a surface for a particular
461 /// texture format.
462 ///
463 /// Reported per format in [`SurfaceCapabilities::formats`] via
464 /// [`SurfaceFormatCapabilities`].
465 #[repr(transparent)]
466 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
467 #[cfg_attr(feature = "serde", serde(transparent))]
468 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
469 pub struct SurfaceColorSpaces: u32 {
470 /// [`SurfaceColorSpace::Srgb`] is supported.
471 const SRGB = 1 << 0;
472 /// [`SurfaceColorSpace::ExtendedSrgbLinear`] is supported.
473 const EXTENDED_SRGB_LINEAR = 1 << 1;
474 /// [`SurfaceColorSpace::DisplayP3`] is supported.
475 const DISPLAY_P3 = 1 << 2;
476 /// [`SurfaceColorSpace::Bt2100Pq`] is supported.
477 const BT2100_PQ = 1 << 3;
478 /// [`SurfaceColorSpace::Bt2100Hlg`] is supported.
479 const BT2100_HLG = 1 << 4;
480 /// [`SurfaceColorSpace::ExtendedSrgb`] is supported.
481 const EXTENDED_SRGB = 1 << 5;
482 /// [`SurfaceColorSpace::ExtendedDisplayP3`] is supported.
483 const EXTENDED_DISPLAY_P3 = 1 << 6;
484 }
485}
486
487/// A texture format supported by a surface, together with the color spaces
488/// in which the surface can present it.
489#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
490#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
491pub struct SurfaceFormatCapabilities {
492 /// The texture format.
493 pub format: TextureFormat,
494 /// The set of color spaces the surface supports for this format.
495 ///
496 /// This reports which color spaces the surface can be *configured* with; it
497 /// does not reflect whether the display is currently in HDR mode. For the
498 /// display's live HDR state, see [`DisplayHdrInfo`].
499 ///
500 /// Guaranteed to be non-empty.
501 pub color_spaces: SurfaceColorSpaces,
502}
503
504/// Defines the capabilities of a given surface and adapter.
505#[derive(Debug)]
506pub struct SurfaceCapabilities {
507 /// List of supported formats to use with the given adapter. The first format in the vector is preferred.
508 ///
509 /// Only contains formats that can be configured with the default
510 /// [`SurfaceColorSpace::Auto`]; formats available exclusively in
511 /// explicit-opt-in (wide-gamut / HDR) color spaces appear only in
512 /// [`format_capabilities`](Self::format_capabilities).
513 ///
514 /// Returns an empty vector if the surface is incompatible with the adapter.
515 pub formats: Vec<TextureFormat>,
516 /// List of supported formats together with the color spaces supported for
517 /// each format, in the same preference order as
518 /// [`formats`](Self::formats), of which it is a superset.
519 ///
520 /// Returns an empty vector if the surface is incompatible with the adapter.
521 pub format_capabilities: Vec<SurfaceFormatCapabilities>,
522 /// List of supported presentation modes to use with the given adapter.
523 ///
524 /// Returns an empty vector if the surface is incompatible with the adapter.
525 pub present_modes: Vec<PresentMode>,
526 /// List of supported alpha modes to use with the given adapter.
527 ///
528 /// Will return at least one element, [`CompositeAlphaMode::Opaque`] or [`CompositeAlphaMode::Inherit`].
529 pub alpha_modes: Vec<CompositeAlphaMode>,
530 /// Bitflag of supported texture usages for the surface to use with the given adapter.
531 ///
532 /// The usage [`TextureUsages::RENDER_ATTACHMENT`] is guaranteed.
533 pub usages: TextureUsages,
534}
535
536impl SurfaceCapabilities {
537 /// Returns the set of color spaces supported for the given format, or an
538 /// empty set if the format is not supported.
539 ///
540 /// This is a convenience lookup over
541 /// [`format_capabilities`](Self::format_capabilities): an empty result
542 /// means `format` is absent from that list.
543 #[must_use]
544 pub fn color_spaces(&self, format: TextureFormat) -> SurfaceColorSpaces {
545 self.format_capabilities
546 .iter()
547 .filter(|fc| fc.format == format)
548 .fold(SurfaceColorSpaces::empty(), |acc, fc| acc | fc.color_spaces)
549 }
550}
551
552impl Default for SurfaceCapabilities {
553 fn default() -> Self {
554 Self {
555 formats: Vec::new(),
556 format_capabilities: Vec::new(),
557 present_modes: Vec::new(),
558 alpha_modes: vec![CompositeAlphaMode::Opaque],
559 usages: TextureUsages::RENDER_ATTACHMENT,
560 }
561 }
562}
563
564/// HDR and luminance characteristics of the display backing a [`Surface`], as
565/// reported by the platform at query time.
566///
567/// This describes the display; it does not configure it. Set the output color
568/// space through [`SurfaceConfiguration::color_space`]; wgpu does not write HDR
569/// metadata (`vkSetHdrMetadataEXT` / DXGI `SetHDRMetaData`).
570///
571/// Use it for tone mapping, not to decide whether to enable HDR - that is a
572/// capability question for [`SurfaceCapabilities`], and holds even when the panel
573/// has no headroom right now. The live highlight multiplier is
574/// [`tone_map_headroom`](Self::tone_map_headroom).
575///
576/// The values change as the display does, so re-query after the surface moves or
577/// resizes or the display configuration changes.
578///
579/// Every field is [`Option`] and no platform reports them all; `None` means
580/// unknown, never zero and never SDR (Windows reports nits, macOS only a headroom
581/// multiplier). The numbers are advisory hints, not contracts: OS/EDID figures run
582/// optimistic and report the panel's claim, not what survives the compositor.
583///
584#[doc = link_to_wgpu_item!(struct Surface)]
585#[derive(Clone, Debug, Default, PartialEq)]
586#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
587pub struct DisplayHdrInfo {
588 /// Absolute-nit luminance levels. `Some` only on platforms that report
589 /// absolute nits (Windows, via DXGI). `None` on Apple EDR, the web, Vulkan
590 /// on non-Windows, and GLES.
591 pub luminance: Option<DisplayLuminance>,
592
593 /// Relative EDR-headroom multipliers. `Some` only on Apple. `None`
594 /// elsewhere.
595 pub headroom: Option<DisplayHeadroom>,
596
597 /// Chromaticity of the display's primaries and white point (CIE 1931 xy).
598 /// `Some` only on Windows (via DXGI). `None` on Apple (which exposes no
599 /// primaries), the web (boolean-only), Vulkan on non-Windows, and GLES.
600 /// Advisory (often EDID-sourced).
601 pub chromaticity: Option<DisplayChromaticity>,
602
603 /// Coarse, boolean dynamic-range + gamut bucket. The only luminance-adjacent
604 /// data the web exposes (CSS `dynamic-range` / `color-gamut`), and a useful
605 /// cross-check elsewhere. `None` only when nothing at all is known.
606 pub coarse: Option<DisplayCoarseRange>,
607
608 /// Output signal bit depth, e.g. `8` / `10` / `12` (DXGI `BitsPerColor`).
609 /// Advisory and often unreliable (may report `8` on a 10-bit panel). `None`
610 /// if unreported.
611 pub bits_per_color: Option<u8>,
612}
613
614/// Absolute luminance levels in nits (cd/m²). Populated only on Windows (via
615/// DXGI); `None` on every other platform.
616///
617/// Advisory: OS/EDID figures run optimistic. A `0.0` from the OS stays
618/// `Some(0.0)`; absence is `None`. These are achromatic (luminance = CIE Y), not a
619/// per-color ceiling: a display can't reach [`max_nits`](Self::max_nits) at a
620/// saturated chromaticity. Pair them with [`DisplayChromaticity`] for gamut mapping.
621#[derive(Clone, Copy, Debug, Default, PartialEq)]
622#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
623pub struct DisplayLuminance {
624 /// Peak luminance of a small patch, nits. DXGI `MaxLuminance`.
625 pub max_nits: Option<f32>,
626 /// Sustained full-white-frame luminance, nits: the ceiling for a fully-lit
627 /// frame, which power/thermal limits can hold below the small-patch peak
628 /// [`max_nits`](Self::max_nits). May equal `max_nits` if the OS reports no
629 /// distinct limit. Prefer it over `max_nits` for large bright regions; don't
630 /// derive it from `max_nits`. DXGI `MaxFullFrameLuminance`.
631 pub max_full_frame_nits: Option<f32>,
632 /// Minimum (black) luminance, nits. DXGI `MinLuminance`.
633 pub min_nits: Option<f32>,
634 /// Luminance the OS maps SDR reference white to, nits; moves with the
635 /// brightness slider. Converts between absolute nits and relative EDR headroom
636 /// (`max_nits / sdr_white_nits`). Read via the `DISPLAYCONFIG_SDR_WHITE_LEVEL`
637 /// query, separate from the other nits, so `None` only if that query fails.
638 pub sdr_white_nits: Option<f32>,
639}
640
641/// Relative EDR headroom (Apple): unitless multipliers over current SDR white,
642/// where `1.0` means no headroom. Moves with brightness, ambient light, battery,
643/// and which display the window is on. Apple exposes no absolute-nit equivalent,
644/// so this is separate from [`DisplayLuminance`] and can't be converted to nits.
645///
646/// Populated only on macOS; `None` on iOS, tvOS, and visionOS.
647#[derive(Clone, Copy, Debug, Default, PartialEq)]
648#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
649pub struct DisplayHeadroom {
650 /// Headroom available *right now* (`maximumExtendedDynamicRangeColorComponentValue`
651 /// / iOS `UIScreen.currentEDRHeadroom`). `1.0` means no headroom at this
652 /// instant, even on an HDR-capable panel.
653 pub current: Option<f32>,
654 /// Headroom the display could reach under ideal conditions
655 /// (`maximumPotentialExtendedDynamicRangeColorComponentValue` /
656 /// `UIScreen.potentialEDRHeadroom`).
657 pub potential: Option<f32>,
658 /// Headroom for reference-white content
659 /// (`maximumReferenceExtendedDynamicRangeColorComponentValue`). `None` if
660 /// unreported.
661 pub reference: Option<f32>,
662}
663
664/// CIE 1931 xy chromaticity of a display's primaries and white point. Each
665/// coordinate is `[x, y]`; a coordinate the platform omits is `None`.
666#[derive(Clone, Copy, Debug, Default, PartialEq)]
667#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
668pub struct DisplayChromaticity {
669 /// xy of the red primary.
670 pub red: Option<[f32; 2]>,
671 /// xy of the green primary.
672 pub green: Option<[f32; 2]>,
673 /// xy of the blue primary.
674 pub blue: Option<[f32; 2]>,
675 /// xy of the white point.
676 pub white: Option<[f32; 2]>,
677}
678
679/// Coarse, boolean dynamic-range and gamut signal.
680///
681/// This is the only luminance-adjacent data the web exposes, and a useful
682/// cross-check on other platforms.
683#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
684#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
685pub struct DisplayCoarseRange {
686 /// CSS `@media (dynamic-range: high)`: the display *can* present HDR-range
687 /// content. Best-effort and platform-defined — "capable", not "an HDR mode is
688 /// active". It feeds [`tone_map_headroom`](DisplayHdrInfo::tone_map_headroom):
689 /// `Some(false)` marks a definitively-SDR display, collapsing the headroom to
690 /// `1.0`.
691 pub high_dynamic_range: Option<bool>,
692 /// Best gamut bucket the display covers (CSS `color-gamut`).
693 pub gamut: Option<DisplayGamut>,
694}
695
696/// Coarse gamut classification, mirroring CSS `color-gamut`.
697///
698/// These variants are **not** ordered by containment; do not rely on their
699/// declaration order to compare gamut sizes.
700#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
701#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
702#[non_exhaustive]
703pub enum DisplayGamut {
704 /// Approximately sRGB / Rec.709.
705 Srgb,
706 /// Approximately Display-P3.
707 DisplayP3,
708 /// Approximately Rec.2020.
709 Rec2020,
710}
711
712impl DisplayHdrInfo {
713 /// Best-effort tone-map headroom: the linear multiplier of SDR white the
714 /// display can drive before clipping. This is the single value most
715 /// tone-mappers want, with the subjective parts left to the application.
716 ///
717 /// Resolution order, first match wins:
718 /// 1. Apple EDR: [`DisplayHeadroom::current`] (already a multiplier).
719 /// 2. `Some(1.0)` when [`DisplayCoarseRange::high_dynamic_range`] is
720 /// `Some(false)` — a definitively-SDR display (Windows and the web both set
721 /// this flag for an SDR output). Its panel peak may sit above its SDR white,
722 /// but that ratio isn't headroom you can drive, so it isn't reported as
723 /// such.
724 /// 3. Absolute nits: `max_nits / sdr_white_nits`, when both are known and
725 /// `sdr_white_nits > 0.0`.
726 /// 4. Otherwise `None`: the available figures don't pin a multiplier (e.g.
727 /// `max_nits` known but `sdr_white_nits` unknown).
728 ///
729 /// Use `unwrap_or(1.0)` on the result for the SDR fallback. Never returns a
730 /// non-finite value.
731 #[must_use]
732 pub fn tone_map_headroom(&self) -> Option<f32> {
733 // Apple EDR reports a multiplier directly.
734 if let Some(h) = self
735 .headroom
736 .and_then(|h| h.current)
737 .filter(|h| h.is_finite())
738 {
739 return Some(h);
740 }
741 // A definitively-SDR display still reports a physical peak against a
742 // default SDR white; checked before the nit ratio so that unusable ratio
743 // can't surface as phantom headroom.
744 if self.coarse.and_then(|c| c.high_dynamic_range) == Some(false) {
745 return Some(1.0);
746 }
747 // Otherwise derive the multiplier from absolute nits, when both the peak
748 // and the SDR white level are known.
749 if let Some((max, sdr)) = self
750 .luminance
751 .and_then(|l| l.max_nits.zip(l.sdr_white_nits))
752 .filter(|&(max, sdr)| sdr > 0.0 && max.is_finite() && sdr.is_finite())
753 {
754 return Some(max / sdr);
755 }
756 None
757 }
758}
759
760#[cfg(test)]
761mod display_hdr_info_tests {
762 use super::*;
763
764 #[test]
765 fn default_is_unknown() {
766 // Nothing known, so no headroom is derived — it never guesses SDR vs HDR.
767 assert_eq!(DisplayHdrInfo::default().tone_map_headroom(), None);
768 }
769
770 #[test]
771 fn apple_headroom_is_used_directly() {
772 // Apple reports a live multiplier; it's returned as-is.
773 let info = DisplayHdrInfo {
774 headroom: Some(DisplayHeadroom {
775 current: Some(3.0),
776 potential: Some(5.0),
777 reference: None,
778 }),
779 ..Default::default()
780 };
781 assert_eq!(info.tone_map_headroom(), Some(3.0));
782 }
783
784 #[test]
785 fn apple_uses_current_not_potential() {
786 // A capable panel with no headroom right now (current 1.0, potential 16.0
787 // — e.g. macOS at full brightness). The live value wins; the potential
788 // ceiling is never tone-mapped against.
789 let info = DisplayHdrInfo {
790 headroom: Some(DisplayHeadroom {
791 current: Some(1.0),
792 potential: Some(16.0),
793 reference: None,
794 }),
795 ..Default::default()
796 };
797 assert_eq!(info.tone_map_headroom(), Some(1.0));
798 }
799
800 #[test]
801 fn windows_nits_derive_headroom_only_with_sdr_white() {
802 // Both nits present and sdr_white > 0, so it returns the ratio.
803 let info = DisplayHdrInfo {
804 luminance: Some(DisplayLuminance {
805 max_nits: Some(800.0),
806 sdr_white_nits: Some(200.0),
807 ..Default::default()
808 }),
809 ..Default::default()
810 };
811 assert_eq!(info.tone_map_headroom(), Some(4.0));
812
813 // max_nits known but sdr_white unknown, so it won't guess across frames.
814 let info = DisplayHdrInfo {
815 luminance: Some(DisplayLuminance {
816 max_nits: Some(800.0),
817 sdr_white_nits: None,
818 ..Default::default()
819 }),
820 ..Default::default()
821 };
822 assert_eq!(info.tone_map_headroom(), None);
823 }
824
825 #[test]
826 fn sdr_display_collapses_to_unity() {
827 // A definitively-SDR display (`dynamic-range: standard`) has no usable
828 // headroom, even with no luminance figures at all.
829 let info = DisplayHdrInfo {
830 coarse: Some(DisplayCoarseRange {
831 high_dynamic_range: Some(false),
832 gamut: Some(DisplayGamut::Srgb),
833 }),
834 ..Default::default()
835 };
836 assert_eq!(info.tone_map_headroom(), Some(1.0));
837 }
838
839 #[test]
840 fn sdr_display_overrides_panel_nits() {
841 // An SDR-mode output still reports its EDID peak (270 nits) against a
842 // default 80-nit SDR white. That 270/80 ratio is unusable, so the SDR flag
843 // wins and the headroom collapses to 1.0 rather than 3.375.
844 let info = DisplayHdrInfo {
845 luminance: Some(DisplayLuminance {
846 max_nits: Some(270.0),
847 sdr_white_nits: Some(80.0),
848 ..Default::default()
849 }),
850 coarse: Some(DisplayCoarseRange {
851 high_dynamic_range: Some(false),
852 gamut: Some(DisplayGamut::DisplayP3),
853 }),
854 ..Default::default()
855 };
856 assert_eq!(info.tone_map_headroom(), Some(1.0));
857 }
858
859 #[test]
860 fn coarse_hdr_capable_alone_derives_nothing() {
861 // `dynamic-range: high` (the web's only signal) means the display is
862 // HDR-capable, not that headroom is available — and it carries no
863 // luminance to derive one from, so the headroom stays unknown.
864 let info = DisplayHdrInfo {
865 coarse: Some(DisplayCoarseRange {
866 high_dynamic_range: Some(true),
867 gamut: Some(DisplayGamut::Rec2020),
868 }),
869 ..Default::default()
870 };
871 assert_eq!(info.tone_map_headroom(), None);
872 }
873
874 #[test]
875 fn non_finite_current_falls_through_to_nits() {
876 // A non-finite EDR read is skipped, not leaked; the nit ratio answers.
877 let info = DisplayHdrInfo {
878 headroom: Some(DisplayHeadroom {
879 current: Some(f32::INFINITY),
880 ..Default::default()
881 }),
882 luminance: Some(DisplayLuminance {
883 max_nits: Some(1000.0),
884 sdr_white_nits: Some(100.0),
885 ..Default::default()
886 }),
887 ..Default::default()
888 };
889 assert_eq!(info.tone_map_headroom(), Some(10.0));
890 }
891}
892
893/// Configures a [`Surface`] for presentation.
894///
895#[doc = link_to_wgpu_item!(struct Surface)]
896#[repr(C)]
897#[derive(Clone, Debug, PartialEq, Eq, Hash)]
898#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
899pub struct SurfaceConfiguration<V> {
900 /// The usage of the swap chain. The only usage guaranteed to be supported is [`TextureUsages::RENDER_ATTACHMENT`].
901 pub usage: TextureUsages,
902 /// The texture format of the swap chain. The only formats that are guaranteed are
903 /// [`TextureFormat::Bgra8Unorm`] and [`TextureFormat::Bgra8UnormSrgb`].
904 pub format: TextureFormat,
905 /// The color space in which the presentation engine interprets the values
906 /// written to the swap chain.
907 ///
908 /// The supported color spaces for each format are listed in
909 /// [`SurfaceCapabilities::format_capabilities`].
910 /// [`SurfaceColorSpace::Auto`] (the default) is supported for every
911 /// format in [`SurfaceCapabilities::formats`]; any other value must be
912 /// present in the format's
913 /// [`color_spaces`](SurfaceFormatCapabilities::color_spaces) set.
914 pub color_space: SurfaceColorSpace,
915 /// Width of the swap chain. Must be the same size as the surface, and nonzero.
916 ///
917 /// If this is not the same size as the underlying surface (e.g. if it is
918 /// set once, and the window is later resized), the behaviour is defined
919 /// but platform-specific, and may change in the future (currently macOS
920 /// scales the surface, other platforms may do something else).
921 pub width: u32,
922 /// Height of the swap chain. Must be the same size as the surface, and nonzero.
923 ///
924 /// If this is not the same size as the underlying surface (e.g. if it is
925 /// set once, and the window is later resized), the behaviour is defined
926 /// but platform-specific, and may change in the future (currently macOS
927 /// scales the surface, other platforms may do something else).
928 pub height: u32,
929 /// Presentation mode of the swap chain. Fifo is the only mode guaranteed to be supported.
930 /// `FifoRelaxed`, `Immediate`, and `Mailbox` will crash if unsupported, while `AutoVsync` and
931 /// `AutoNoVsync` will gracefully do a designed sets of fallbacks if their primary modes are
932 /// unsupported.
933 pub present_mode: PresentMode,
934 /// Desired maximum number of monitor refreshes between a [`Surface::get_current_texture`] call and the
935 /// texture being presented to the screen. This is sometimes called "Frames in Flight".
936 ///
937 /// Defaults to `2` when created via [`Surface::get_default_config`] as this is a reasonable default.
938 ///
939 /// This is ultimately a hint to the backend implementation and will always be clamped
940 /// to the supported range.
941 ///
942 /// Typical values are `1` to `3`, but higher values are valid, though likely to be clamped.
943 /// * Choose `1` to minimize latency above all else. This only gives a single monitor refresh for all of
944 /// the CPU and GPU work to complete. ⚠️ As a result of these short swapchains, the CPU and GPU
945 /// cannot run in parallel, prioritizing latency over throughput. For applications like GUIs doing
946 /// a small amount of GPU work each frame that need low latency, this is a reasonable choice.
947 /// * Choose `2` for a balance between latency and throughput. The CPU and GPU both can each use
948 /// a full monitor refresh to do their computations. This is a reasonable default for most applications.
949 /// * Choose `3` or higher to maximize throughput, sacrificing latency when the CPU and GPU
950 /// are using less than a full monitor refresh each. For applications that use CPU-side pipelining
951 /// of frames this may be a reasonable choice. ⚠️ On 60hz displays the latency can be very noticeable.
952 ///
953 /// This maps to the backend in the following ways:
954 /// - Vulkan: Number of frames in the swapchain is `desired_maximum_frame_latency + 1`,
955 /// clamped to the supported range.
956 /// - DX12: Calls [`IDXGISwapChain2::SetMaximumFrameLatency(desired_maximum_frame_latency)`][SMFL].
957 /// - Metal: Sets the `maximumDrawableCount` of the underlying `CAMetalLayer` to
958 /// `desired_maximum_frame_latency + 1`, clamped to the supported range.
959 /// - OpenGL: Ignored
960 ///
961 /// It also has various subtle interactions with various present modes and APIs.
962 /// - DX12 + Mailbox: Limits framerate to `desired_maximum_frame_latency * Monitor Hz` fps.
963 /// - Vulkan/Metal + Mailbox: If this is set to `2`, limits framerate to `2 * Monitor Hz` fps. `3` or higher is unlimited.
964 ///
965 #[doc = link_to_wgpu_docs!(["`Surface::get_current_texture`"]: "struct.Surface.html#method.get_current_texture")]
966 #[doc = link_to_wgpu_docs!(["`Surface::get_default_config`"]: "struct.Surface.html#method.get_default_config")]
967 /// [SMFL]: https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_3/nf-dxgi1_3-idxgiswapchain2-setmaximumframelatency
968 pub desired_maximum_frame_latency: u32,
969 /// Specifies how the alpha channel of the textures should be handled during compositing.
970 pub alpha_mode: CompositeAlphaMode,
971 /// Specifies what view formats will be allowed when calling `Texture::create_view` on the texture returned by `Surface::get_current_texture`.
972 ///
973 /// View formats of the same format as the texture are always allowed.
974 ///
975 /// Note: currently, only the srgb-ness is allowed to change. (ex: `Rgba8Unorm` texture + `Rgba8UnormSrgb` view)
976 pub view_formats: V,
977}
978
979impl<V: Clone> SurfaceConfiguration<V> {
980 /// Map `view_formats` of the texture descriptor into another.
981 pub fn map_view_formats<'a, M>(
982 &'a self,
983 fun: impl FnOnce(&'a V) -> M,
984 ) -> SurfaceConfiguration<M> {
985 SurfaceConfiguration {
986 usage: self.usage,
987 format: self.format,
988 color_space: self.color_space,
989 width: self.width,
990 height: self.height,
991 present_mode: self.present_mode,
992 desired_maximum_frame_latency: self.desired_maximum_frame_latency,
993 alpha_mode: self.alpha_mode,
994 view_formats: fun(&self.view_formats),
995 }
996 }
997}
998
999/// Status of the received surface image.
1000#[repr(C)]
1001#[derive(Debug)]
1002pub enum SurfaceStatus {
1003 /// No issues.
1004 Good,
1005 /// The swap chain is operational, but it does no longer perfectly
1006 /// match the surface. A re-configuration is needed.
1007 Suboptimal,
1008 /// Unable to get the next frame, timed out.
1009 ///
1010 /// Try reconfiguring your surface.
1011 Timeout,
1012 /// The window is occluded (e.g. minimized or behind another window).
1013 ///
1014 /// Try again once the window is no longer occluded.
1015 Occluded,
1016 /// The surface under the swap chain has changed.
1017 ///
1018 /// Try reconfiguring your surface.
1019 Outdated,
1020 /// The surface under the swap chain is lost.
1021 Lost,
1022 /// `Surface::get_current_texture` has hit a validation error which was caught
1023 /// by a error scope.
1024 Validation,
1025}
1026
1027/// Nanosecond timestamp used by the presentation engine.
1028///
1029/// The specific clock depends on the window system integration (WSI) API used.
1030///
1031/// <table>
1032/// <tr>
1033/// <td>WSI</td>
1034/// <td>Clock</td>
1035/// </tr>
1036/// <tr>
1037/// <td>IDXGISwapchain</td>
1038/// <td><a href="https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter">QueryPerformanceCounter</a></td>
1039/// </tr>
1040/// <tr>
1041/// <td>IPresentationManager</td>
1042/// <td><a href="https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryinterrupttimeprecise">QueryInterruptTimePrecise</a></td>
1043/// </tr>
1044/// <tr>
1045/// <td>CAMetalLayer</td>
1046/// <td><a href="https://developer.apple.com/documentation/kernel/1462446-mach_absolute_time">mach_absolute_time</a></td>
1047/// </tr>
1048/// <tr>
1049/// <td>VK_GOOGLE_display_timing</td>
1050/// <td><a href="https://linux.die.net/man/3/clock_gettime">clock_gettime(CLOCK_MONOTONIC)</a></td>
1051/// </tr>
1052/// </table>
1053#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
1054pub struct PresentationTimestamp(
1055 /// Timestamp in nanoseconds.
1056 pub u128,
1057);
1058
1059impl PresentationTimestamp {
1060 /// A timestamp that is invalid due to the platform not having a timestamp system.
1061 pub const INVALID_TIMESTAMP: Self = Self(u128::MAX);
1062
1063 /// Returns true if this timestamp is the invalid timestamp.
1064 #[must_use]
1065 pub fn is_invalid(self) -> bool {
1066 self == Self::INVALID_TIMESTAMP
1067 }
1068}