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}