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