wgpu_types/
surface.rs

1use alloc::{vec, vec::Vec};
2
3use crate::{link_to_wgpu_docs, link_to_wgpu_item, TextureFormat, TextureUsages};
4
5#[cfg(any(feature = "serde", test))]
6use serde::{Deserialize, Serialize};
7
8/// Timing and queueing with which frames are actually displayed to the user.
9///
10/// Use this as part of a [`SurfaceConfiguration`] to control the behavior of
11/// [`SurfaceTexture::present()`].
12///
13/// Some modes are only supported by some backends.
14/// You can use one of the `Auto*` modes, [`Fifo`](Self::Fifo),
15/// or choose one of the supported modes from [`SurfaceCapabilities::present_modes`].
16///
17#[doc = link_to_wgpu_docs!(["presented"]: "struct.SurfaceTexture.html#method.present")]
18#[doc = link_to_wgpu_docs!(["`SurfaceTexture::present()`"]: "struct.SurfaceTexture.html#method.present")]
19#[repr(C)]
20#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub enum PresentMode {
23    /// Chooses the first supported mode out of:
24    ///
25    /// 1. [`FifoRelaxed`](Self::FifoRelaxed)
26    /// 2. [`Fifo`](Self::Fifo)
27    ///
28    /// Because of the fallback behavior, this is supported everywhere.
29    AutoVsync = 0,
30
31    /// Chooses the first supported mode out of:
32    ///
33    /// 1. [`Immediate`](Self::Immediate)
34    /// 2. [`Mailbox`](Self::Mailbox)
35    /// 3. [`Fifo`](Self::Fifo)
36    ///
37    /// Because of the fallback behavior, this is supported everywhere.
38    AutoNoVsync = 1,
39
40    /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames
41    /// long. Every vertical blanking period, the presentation engine will pop a frame
42    /// off the queue to display. If there is no frame to display, it will present the same
43    /// frame again until the next vblank.
44    ///
45    /// When a present command is executed on the GPU, the presented image is added on the queue.
46    ///
47    /// Calls to [`Surface::get_current_texture()`] will block until there is a spot in the queue.
48    ///
49    /// * **Tearing:** No tearing will be observed.
50    /// * **Supported on**: All platforms.
51    /// * **Also known as**: "Vsync On"
52    ///
53    /// This is the [default](Self::default) value for `PresentMode`.
54    /// If you don't know what mode to choose, choose this mode.
55    ///
56    #[doc = link_to_wgpu_docs!(["`Surface::get_current_texture()`"]: "struct.Surface.html#method.get_current_texture")]
57    #[default]
58    Fifo = 2,
59
60    /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames
61    /// long. Every vertical blanking period, the presentation engine will pop a frame
62    /// off the queue to display. If there is no frame to display, it will present the
63    /// same frame until there is a frame in the queue. The moment there is a frame in the
64    /// queue, it will immediately pop the frame off the queue.
65    ///
66    /// When a present command is executed on the GPU, the presented image is added on the queue.
67    ///
68    /// Calls to [`Surface::get_current_texture()`] will block until there is a spot in the queue.
69    ///
70    /// * **Tearing**:
71    ///   Tearing will be observed if frames last more than one vblank as the front buffer.
72    /// * **Supported on**: AMD on Vulkan.
73    /// * **Also known as**: "Adaptive Vsync"
74    ///
75    #[doc = link_to_wgpu_docs!(["`Surface::get_current_texture()`"]: "struct.Surface.html#method.get_current_texture")]
76    FifoRelaxed = 3,
77
78    /// Presentation frames are not queued at all. The moment a present command
79    /// is executed on the GPU, the presented image is swapped onto the front buffer
80    /// immediately.
81    ///
82    /// * **Tearing**: Tearing can be observed.
83    /// * **Supported on**: Most platforms except older DX12 and Wayland.
84    /// * **Also known as**: "Vsync Off"
85    Immediate = 4,
86
87    /// Presentation frames are kept in a single-frame queue. Every vertical blanking period,
88    /// the presentation engine will pop a frame from the queue. If there is no frame to display,
89    /// it will present the same frame again until the next vblank.
90    ///
91    /// When a present command is executed on the GPU, the frame will be put into the queue.
92    /// If there was already a frame in the queue, the new frame will _replace_ the old frame
93    /// on the queue.
94    ///
95    /// * **Tearing**: No tearing will be observed.
96    /// * **Supported on**: DX12 on Windows 10, NVidia on Vulkan and Wayland on Vulkan.
97    /// * **Also known as**: "Fast Vsync"
98    Mailbox = 5,
99}
100
101/// Specifies how the alpha channel of the textures should be handled during
102/// compositing.
103#[repr(C)]
104#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
105#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
106#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
107pub enum CompositeAlphaMode {
108    /// Chooses either `Opaque` or `Inherit` automatically,depending on the
109    /// `alpha_mode` that the current surface can support.
110    #[default]
111    Auto = 0,
112    /// The alpha channel, if it exists, of the textures is ignored in the
113    /// compositing process. Instead, the textures is treated as if it has a
114    /// constant alpha of 1.0.
115    Opaque = 1,
116    /// The alpha channel, if it exists, of the textures is respected in the
117    /// compositing process. The non-alpha channels of the textures are
118    /// expected to already be multiplied by the alpha channel by the
119    /// application.
120    PreMultiplied = 2,
121    /// The alpha channel, if it exists, of the textures is respected in the
122    /// compositing process. The non-alpha channels of the textures are not
123    /// expected to already be multiplied by the alpha channel by the
124    /// application; instead, the compositor will multiply the non-alpha
125    /// channels of the texture by the alpha channel during compositing.
126    PostMultiplied = 3,
127    /// The alpha channel, if it exists, of the textures is unknown for processing
128    /// during compositing. Instead, the application is responsible for setting
129    /// the composite alpha blending mode using native WSI command. If not set,
130    /// then a platform-specific default will be used.
131    Inherit = 4,
132}
133
134/// Defines the capabilities of a given surface and adapter.
135#[derive(Debug)]
136pub struct SurfaceCapabilities {
137    /// List of supported formats to use with the given adapter. The first format in the vector is preferred.
138    ///
139    /// Returns an empty vector if the surface is incompatible with the adapter.
140    pub formats: Vec<TextureFormat>,
141    /// List of supported presentation modes to use with the given adapter.
142    ///
143    /// Returns an empty vector if the surface is incompatible with the adapter.
144    pub present_modes: Vec<PresentMode>,
145    /// List of supported alpha modes to use with the given adapter.
146    ///
147    /// Will return at least one element, [`CompositeAlphaMode::Opaque`] or [`CompositeAlphaMode::Inherit`].
148    pub alpha_modes: Vec<CompositeAlphaMode>,
149    /// Bitflag of supported texture usages for the surface to use with the given adapter.
150    ///
151    /// The usage [`TextureUsages::RENDER_ATTACHMENT`] is guaranteed.
152    pub usages: TextureUsages,
153}
154
155impl Default for SurfaceCapabilities {
156    fn default() -> Self {
157        Self {
158            formats: Vec::new(),
159            present_modes: Vec::new(),
160            alpha_modes: vec![CompositeAlphaMode::Opaque],
161            usages: TextureUsages::RENDER_ATTACHMENT,
162        }
163    }
164}
165
166/// Configures a [`Surface`] for presentation.
167///
168#[doc = link_to_wgpu_item!(struct Surface)]
169#[repr(C)]
170#[derive(Clone, Debug, PartialEq, Eq, Hash)]
171#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
172pub struct SurfaceConfiguration<V> {
173    /// The usage of the swap chain. The only usage guaranteed to be supported is [`TextureUsages::RENDER_ATTACHMENT`].
174    pub usage: TextureUsages,
175    /// The texture format of the swap chain. The only formats that are guaranteed are
176    /// [`TextureFormat::Bgra8Unorm`] and [`TextureFormat::Bgra8UnormSrgb`].
177    pub format: TextureFormat,
178    /// Width of the swap chain. Must be the same size as the surface, and nonzero.
179    ///
180    /// If this is not the same size as the underlying surface (e.g. if it is
181    /// set once, and the window is later resized), the behaviour is defined
182    /// but platform-specific, and may change in the future (currently macOS
183    /// scales the surface, other platforms may do something else).
184    pub width: u32,
185    /// Height of the swap chain. Must be the same size as the surface, and nonzero.
186    ///
187    /// If this is not the same size as the underlying surface (e.g. if it is
188    /// set once, and the window is later resized), the behaviour is defined
189    /// but platform-specific, and may change in the future (currently macOS
190    /// scales the surface, other platforms may do something else).
191    pub height: u32,
192    /// Presentation mode of the swap chain. Fifo is the only mode guaranteed to be supported.
193    /// `FifoRelaxed`, `Immediate`, and `Mailbox` will crash if unsupported, while `AutoVsync` and
194    /// `AutoNoVsync` will gracefully do a designed sets of fallbacks if their primary modes are
195    /// unsupported.
196    pub present_mode: PresentMode,
197    /// Desired maximum number of monitor refreshes between a [`Surface::get_current_texture`] call and the
198    /// texture being presented to the screen. This is sometimes called "Frames in Flight".
199    ///
200    /// Defaults to `2` when created via [`Surface::get_default_config`] as this is a reasonable default.
201    ///
202    /// This is ultimately a hint to the backend implementation and will always be clamped
203    /// to the supported range.
204    ///
205    /// Typical values are `1` to `3`, but higher values are valid, though likely to be clamped.
206    /// * Choose `1` to minimize latency above all else. This only gives a single monitor refresh for all of
207    ///   the CPU and GPU work to complete. ⚠️ As a result of these short swapchains, the CPU and GPU
208    ///   cannot run in parallel, prioritizing latency over throughput. For applications like GUIs doing
209    ///   a small amount of GPU work each frame that need low latency, this is a reasonable choice.
210    /// * Choose `2` for a balance between latency and throughput. The CPU and GPU both can each use
211    ///   a full monitor refresh to do their computations. This is a reasonable default for most applications.
212    /// * Choose `3` or higher to maximize throughput, sacrificing latency when the the CPU and GPU
213    ///   are using less than a full monitor refresh each. For applications that use CPU-side pipelining
214    ///   of frames this may be a reasonable choice. ⚠️ On 60hz displays the latency can be very noticeable.
215    ///
216    /// This maps to the backend in the following ways:
217    /// - Vulkan: Number of frames in the swapchain is `desired_maximum_frame_latency + 1`,
218    ///   clamped to the supported range.
219    /// - DX12: Calls [`IDXGISwapChain2::SetMaximumFrameLatency(desired_maximum_frame_latency)`][SMFL].
220    /// - Metal: Sets the `maximumDrawableCount` of the underlying `CAMetalLayer` to
221    ///   `desired_maximum_frame_latency + 1`, clamped to the supported range.
222    /// - OpenGL: Ignored
223    ///
224    /// It also has various subtle interactions with various present modes and APIs.
225    /// - DX12 + Mailbox: Limits framerate to `desired_maximum_frame_latency * Monitor Hz` fps.
226    /// - Vulkan/Metal + Mailbox: If this is set to `2`, limits framerate to `2 * Monitor Hz` fps. `3` or higher is unlimited.
227    ///
228    #[doc = link_to_wgpu_docs!(["`Surface::get_current_texture`"]: "struct.Surface.html#method.get_current_texture")]
229    #[doc = link_to_wgpu_docs!(["`Surface::get_default_config`"]: "struct.Surface.html#method.get_default_config")]
230    /// [SMFL]: https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_3/nf-dxgi1_3-idxgiswapchain2-setmaximumframelatency
231    pub desired_maximum_frame_latency: u32,
232    /// Specifies how the alpha channel of the textures should be handled during compositing.
233    pub alpha_mode: CompositeAlphaMode,
234    /// Specifies what view formats will be allowed when calling `Texture::create_view` on the texture returned by `Surface::get_current_texture`.
235    ///
236    /// View formats of the same format as the texture are always allowed.
237    ///
238    /// Note: currently, only the srgb-ness is allowed to change. (ex: `Rgba8Unorm` texture + `Rgba8UnormSrgb` view)
239    pub view_formats: V,
240}
241
242impl<V: Clone> SurfaceConfiguration<V> {
243    /// Map `view_formats` of the texture descriptor into another.
244    pub fn map_view_formats<M>(&self, fun: impl FnOnce(V) -> M) -> SurfaceConfiguration<M> {
245        SurfaceConfiguration {
246            usage: self.usage,
247            format: self.format,
248            width: self.width,
249            height: self.height,
250            present_mode: self.present_mode,
251            desired_maximum_frame_latency: self.desired_maximum_frame_latency,
252            alpha_mode: self.alpha_mode,
253            view_formats: fun(self.view_formats.clone()),
254        }
255    }
256}
257
258/// Status of the received surface image.
259#[repr(C)]
260#[derive(Debug)]
261pub enum SurfaceStatus {
262    /// No issues.
263    Good,
264    /// The swap chain is operational, but it does no longer perfectly
265    /// match the surface. A re-configuration is needed.
266    Suboptimal,
267    /// Unable to get the next frame, timed out.
268    Timeout,
269    /// The surface under the swap chain has changed.
270    Outdated,
271    /// The surface under the swap chain is lost.
272    Lost,
273    /// The surface status is not known since `Surface::get_current_texture` previously failed.
274    Unknown,
275}
276
277/// Nanosecond timestamp used by the presentation engine.
278///
279/// The specific clock depends on the window system integration (WSI) API used.
280///
281/// <table>
282/// <tr>
283///     <td>WSI</td>
284///     <td>Clock</td>
285/// </tr>
286/// <tr>
287///     <td>IDXGISwapchain</td>
288///     <td><a href="https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter">QueryPerformanceCounter</a></td>
289/// </tr>
290/// <tr>
291///     <td>IPresentationManager</td>
292///     <td><a href="https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryinterrupttimeprecise">QueryInterruptTimePrecise</a></td>
293/// </tr>
294/// <tr>
295///     <td>CAMetalLayer</td>
296///     <td><a href="https://developer.apple.com/documentation/kernel/1462446-mach_absolute_time">mach_absolute_time</a></td>
297/// </tr>
298/// <tr>
299///     <td>VK_GOOGLE_display_timing</td>
300///     <td><a href="https://linux.die.net/man/3/clock_gettime">clock_gettime(CLOCK_MONOTONIC)</a></td>
301/// </tr>
302/// </table>
303#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
304pub struct PresentationTimestamp(
305    /// Timestamp in nanoseconds.
306    pub u128,
307);
308
309impl PresentationTimestamp {
310    /// A timestamp that is invalid due to the platform not having a timestamp system.
311    pub const INVALID_TIMESTAMP: Self = Self(u128::MAX);
312
313    /// Returns true if this timestamp is the invalid timestamp.
314    #[must_use]
315    pub fn is_invalid(self) -> bool {
316        self == Self::INVALID_TIMESTAMP
317    }
318}