gfx_hal/window.rs
1//! Windowing system interoperability.
2//!
3//! Screen presentation (fullscreen or window) of images requires two objects:
4//!
5//! * [Surface][Surface] is an abstraction of a native screen or window, for graphics use.
6//! It hosts a chain of multiple images, which can be presented on a surface ("swapchain").
7//!
8//! ## Window
9//!
10//! `gfx-hal` does not provide any methods for creating a native window or screen.
11//! This is handled exeternally, either by managing your own window or by using a
12//! library such as [winit](https://github.com/rust-windowing/winit), and providing
13//! the [raw window handle](https://github.com/rust-windowing/raw-window-handle).
14//!
15//! ## Surface
16//!
17//! Once you have a window handle, you need to [create a surface][crate::Instance::create_surface]
18//! compatible with the [instance][crate::Instance] of the graphics API you currently use.
19//!
20//! ## PresentationSurface
21//!
22//! A surface has an implicit swapchain in it.
23//!
24//! The most interesting part of a swapchain are the contained presentable images/backbuffers.
25//! Presentable images are specialized images, which can be presented on the screen. They are
26//! 2D color images with optionally associated depth-stencil images.
27//!
28//! The common steps for presentation of a frame are acquisition and presentation:
29//!
30//! ```no_run
31//! # extern crate gfx_backend_empty as empty;
32//! # extern crate gfx_hal;
33//! # fn main() {
34//! # use gfx_hal::prelude::*;
35//!
36//! # let mut surface: empty::Surface = return;
37//! # let device: empty::Device = return;
38//! # let mut present_queue: empty::Queue = return;
39//! # unsafe {
40//! let mut render_semaphore = device.create_semaphore().unwrap();
41//!
42//! let (frame, suboptimal) = surface.acquire_image(!0).unwrap();
43//! // render the scene..
44//! // `render_semaphore` will be signalled once rendering has been finished
45//! present_queue.present(&mut surface, frame, Some(&mut render_semaphore));
46//! # }}
47//! ```
48//!
49//! Queues need to synchronize with the presentation engine, usually done via signalling a semaphore
50//! once a frame is available for rendering and waiting on a separate semaphore until scene rendering
51//! has finished.
52//!
53//! ### Recreation
54//!
55//! DOC TODO
56
57use crate::{device, format::Format, image, Backend};
58
59use std::{
60 any::Any,
61 borrow::Borrow,
62 cmp::{max, min},
63 fmt,
64 ops::RangeInclusive,
65};
66
67/// Default image usage for the swapchain.
68pub const DEFAULT_USAGE: image::Usage = image::Usage::COLOR_ATTACHMENT;
69/// Default image count for the swapchain.
70pub const DEFAULT_IMAGE_COUNT: SwapImageIndex = 3;
71
72/// Error occurred caused surface to be lost.
73#[derive(Clone, Debug, PartialEq, thiserror::Error)]
74#[error("Surface lost")]
75pub struct SurfaceLost;
76
77/// Error occurred during swapchain configuration.
78#[derive(Clone, Debug, PartialEq, thiserror::Error)]
79pub enum SwapchainError {
80 /// Out of either host or device memory.
81 #[error(transparent)]
82 OutOfMemory(#[from] device::OutOfMemory),
83 /// Device is lost
84 #[error(transparent)]
85 DeviceLost(#[from] device::DeviceLost),
86 /// Surface is lost
87 #[error(transparent)]
88 SurfaceLost(#[from] SurfaceLost),
89 /// Window in use
90 #[error("Window is in use")]
91 WindowInUse,
92 /// Accecssing the underlying NSView from wrong thread https://github.com/gfx-rs/gfx/issues/3704
93 #[error("Accecssing NSView from wrong thread")]
94 WrongThread,
95 /// Unknown error.
96 #[error("Swapchain can't be created for an unknown reason")]
97 Unknown,
98}
99
100/// An extent describes the size of a rectangle, such as
101/// a window or texture. It is not used for referring to a
102/// sub-rectangle; for that see `command::Rect`.
103#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub struct Extent2D {
106 /// Width
107 pub width: image::Size,
108 /// Height
109 pub height: image::Size,
110}
111
112impl From<image::Extent> for Extent2D {
113 fn from(ex: image::Extent) -> Self {
114 Extent2D {
115 width: ex.width,
116 height: ex.height,
117 }
118 }
119}
120
121impl From<(image::Size, image::Size)> for Extent2D {
122 fn from(tuple: (image::Size, image::Size)) -> Self {
123 Extent2D {
124 width: tuple.0,
125 height: tuple.1,
126 }
127 }
128}
129
130impl From<Extent2D> for (image::Size, image::Size) {
131 fn from(extent: Extent2D) -> Self {
132 (extent.width, extent.height)
133 }
134}
135
136impl Extent2D {
137 /// Convert into a regular image extent.
138 pub fn to_extent(&self) -> image::Extent {
139 image::Extent {
140 width: self.width,
141 height: self.height,
142 depth: 1,
143 }
144 }
145}
146
147/// An offset describes the position of a rectangle, such as
148/// a window or texture.
149#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
150#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
151pub struct Offset2D {
152 /// X
153 pub x: image::TexelCoordinate,
154 /// Y
155 pub y: image::TexelCoordinate,
156}
157
158impl From<(image::TexelCoordinate, image::TexelCoordinate)> for Offset2D {
159 fn from(tuple: (image::TexelCoordinate, image::TexelCoordinate)) -> Self {
160 Offset2D {
161 x: tuple.0,
162 y: tuple.1,
163 }
164 }
165}
166
167/// Describes information about what a `Surface`'s properties are.
168/// Fetch this with [Surface::capabilities].
169#[derive(Debug, Clone)]
170#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
171pub struct SurfaceCapabilities {
172 /// Number of presentable images supported by the adapter for a swapchain
173 /// created from this surface.
174 ///
175 /// - `image_count.start` must be at least 1.
176 /// - `image_count.end` must be larger or equal to `image_count.start`.
177 pub image_count: RangeInclusive<SwapImageIndex>,
178
179 /// Current extent of the surface.
180 ///
181 /// `None` if the surface has no explicit size, depending on the swapchain extent.
182 pub current_extent: Option<Extent2D>,
183
184 /// Range of supported extents.
185 ///
186 /// `current_extent` must be inside this range.
187 pub extents: RangeInclusive<Extent2D>,
188
189 /// Maximum number of layers supported for presentable images.
190 ///
191 /// Must be at least 1.
192 pub max_image_layers: image::Layer,
193
194 /// Supported image usage flags.
195 pub usage: image::Usage,
196
197 /// A bitmask of supported presentation modes.
198 pub present_modes: PresentMode,
199
200 /// A bitmask of supported alpha composition modes.
201 pub composite_alpha_modes: CompositeAlphaMode,
202}
203
204impl SurfaceCapabilities {
205 fn clamped_extent(&self, default_extent: Extent2D) -> Extent2D {
206 match self.current_extent {
207 Some(current) => current,
208 None => {
209 let (min_width, max_width) = (self.extents.start().width, self.extents.end().width);
210 let (min_height, max_height) =
211 (self.extents.start().height, self.extents.end().height);
212
213 // clamp the default_extent to within the allowed surface sizes
214 let width = min(max_width, max(default_extent.width, min_width));
215 let height = min(max_height, max(default_extent.height, min_height));
216
217 Extent2D { width, height }
218 }
219 }
220 }
221}
222
223/// A `Surface` abstracts the surface of a native window.
224pub trait Surface<B: Backend>: fmt::Debug + Any + Send + Sync {
225 /// Check if the queue family supports presentation to this surface.
226 fn supports_queue_family(&self, family: &B::QueueFamily) -> bool;
227
228 /// Query surface capabilities for this physical device.
229 ///
230 /// Use this function for configuring swapchain creation.
231 fn capabilities(&self, physical_device: &B::PhysicalDevice) -> SurfaceCapabilities;
232
233 /// Query surface formats for this physical device.
234 ///
235 /// This function may be slow. It's typically used during the initialization only.
236 ///
237 /// Note: technically the surface support formats may change at the point
238 /// where an application needs to recreate the swapchain, e.g. when the window
239 /// is moved to a different monitor.
240 ///
241 /// If `None` is returned then the surface has no preferred format and the
242 /// application may use any desired format.
243 fn supported_formats(&self, physical_device: &B::PhysicalDevice) -> Option<Vec<Format>>;
244}
245
246/// A surface trait that exposes the ability to present images on the
247/// associtated swap chain.
248pub trait PresentationSurface<B: Backend>: Surface<B> {
249 /// An opaque type wrapping the swapchain image.
250 type SwapchainImage: Borrow<B::Image> + Borrow<B::ImageView> + fmt::Debug + Send + Sync;
251
252 /// Set up the swapchain associated with the surface to have the given format.
253 unsafe fn configure_swapchain(
254 &mut self,
255 device: &B::Device,
256 config: SwapchainConfig,
257 ) -> Result<(), SwapchainError>;
258
259 /// Remove the associated swapchain from this surface.
260 ///
261 /// This has to be done before the surface is dropped.
262 unsafe fn unconfigure_swapchain(&mut self, device: &B::Device);
263
264 /// Acquire a new swapchain image for rendering.
265 ///
266 /// May fail according to one of the reasons indicated in `AcquireError` enum.
267 ///
268 /// # Synchronization
269 ///
270 /// The acquired image is available to render. No synchronization is required.
271 unsafe fn acquire_image(
272 &mut self,
273 timeout_ns: u64,
274 ) -> Result<(Self::SwapchainImage, Option<Suboptimal>), AcquireError>;
275}
276
277/// Index of an image in the swapchain.
278///
279/// The swapchain is a series of one or more images, usually
280/// with one being drawn on while the other is displayed by
281/// the GPU (aka double-buffering). A `SwapImageIndex` refers
282/// to a particular image in the swapchain.
283pub type SwapImageIndex = u32;
284
285bitflags!(
286 /// Specifies the mode regulating how a swapchain presents frames.
287 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
288 pub struct PresentMode: u32 {
289 /// Don't ever wait for v-sync.
290 const IMMEDIATE = 0x1;
291 /// Wait for v-sync, overwrite the last rendered frame.
292 const MAILBOX = 0x2;
293 /// Present frames in the same order they are rendered.
294 const FIFO = 0x4;
295 /// Don't wait for the next v-sync if we just missed it.
296 const RELAXED = 0x8;
297 }
298);
299
300bitflags!(
301 /// Specifies how the alpha channel of the images should be handled during
302 /// compositing.
303 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
304 pub struct CompositeAlphaMode: u32 {
305 /// The alpha channel, if it exists, of the images is ignored in the
306 /// compositing process. Instead, the image is treated as if it has a
307 /// constant alpha of 1.0.
308 const OPAQUE = 0x1;
309 /// The alpha channel, if it exists, of the images is respected in the
310 /// compositing process. The non-alpha channels of the image are
311 /// expected to already be multiplied by the alpha channel by the
312 /// application.
313 const PREMULTIPLIED = 0x2;
314 /// The alpha channel, if it exists, of the images is respected in the
315 /// compositing process. The non-alpha channels of the image are not
316 /// expected to already be multiplied by the alpha channel by the
317 /// application; instead, the compositor will multiply the non-alpha
318 /// channels of the image by the alpha channel during compositing.
319 const POSTMULTIPLIED = 0x4;
320 /// The way in which the presentation engine treats the alpha channel in
321 /// the images is unknown to gfx-hal. Instead, the application is
322 /// responsible for setting the composite alpha blending mode using
323 /// native window system commands. If the application does not set the
324 /// blending mode using native window system commands, then a
325 /// platform-specific default will be used.
326 const INHERIT = 0x8;
327 }
328);
329
330/// Contains all the data necessary to create a new `Swapchain`:
331/// color, depth, and number of images.
332///
333/// # Examples
334///
335/// This type implements the builder pattern, method calls can be
336/// easily chained.
337///
338/// ```no_run
339/// # extern crate gfx_hal;
340/// # fn main() {
341/// # use gfx_hal::window::SwapchainConfig;
342/// # use gfx_hal::format::Format;
343/// let config = SwapchainConfig::new(100, 100, Format::Bgra8Unorm, 2);
344/// # }
345/// ```
346#[derive(Debug, Clone)]
347pub struct SwapchainConfig {
348 /// Presentation mode.
349 pub present_mode: PresentMode,
350 /// Alpha composition mode.
351 pub composite_alpha_mode: CompositeAlphaMode,
352 /// Format of the backbuffer images.
353 pub format: Format,
354 /// Requested image extent. Must be in
355 /// `SurfaceCapabilities::extents` range.
356 pub extent: Extent2D,
357 /// Number of images in the swapchain. Must be in
358 /// `SurfaceCapabilities::image_count` range.
359 pub image_count: SwapImageIndex,
360 /// Number of image layers. Must be lower or equal to
361 /// `SurfaceCapabilities::max_image_layers`.
362 pub image_layers: image::Layer,
363 /// Image usage of the backbuffer images.
364 pub image_usage: image::Usage,
365}
366
367impl SwapchainConfig {
368 /// Create a new default configuration (color images only).
369 pub fn new(width: u32, height: u32, format: Format, image_count: SwapImageIndex) -> Self {
370 SwapchainConfig {
371 present_mode: PresentMode::FIFO,
372 composite_alpha_mode: CompositeAlphaMode::OPAQUE,
373 format,
374 extent: Extent2D { width, height },
375 image_count,
376 image_layers: 1,
377 image_usage: DEFAULT_USAGE,
378 }
379 }
380
381 /// Return the framebuffer attachment corresponding to the swapchain image views.
382 pub fn framebuffer_attachment(&self) -> image::FramebufferAttachment {
383 image::FramebufferAttachment {
384 usage: self.image_usage,
385 view_caps: image::ViewCapabilities::empty(),
386 format: self.format,
387 }
388 }
389
390 /// Create a swapchain configuration based on the capabilities
391 /// returned from a physical device query. If the surface does not
392 /// specify a current size, default_extent is clamped and used instead.
393 ///
394 /// The default values are taken from `DEFAULT_USAGE` and `DEFAULT_IMAGE_COUNT`.
395 pub fn from_caps(caps: &SurfaceCapabilities, format: Format, default_extent: Extent2D) -> Self {
396 let composite_alpha_mode = if caps
397 .composite_alpha_modes
398 .contains(CompositeAlphaMode::INHERIT)
399 {
400 CompositeAlphaMode::INHERIT
401 } else if caps
402 .composite_alpha_modes
403 .contains(CompositeAlphaMode::OPAQUE)
404 {
405 CompositeAlphaMode::OPAQUE
406 } else {
407 panic!("neither INHERIT or OPAQUE CompositeAlphaMode(s) are supported")
408 };
409 let present_mode = if caps.present_modes.contains(PresentMode::MAILBOX) {
410 PresentMode::MAILBOX
411 } else if caps.present_modes.contains(PresentMode::FIFO) {
412 PresentMode::FIFO
413 } else {
414 panic!("FIFO PresentMode is not supported")
415 };
416
417 SwapchainConfig {
418 present_mode,
419 composite_alpha_mode,
420 format,
421 extent: caps.clamped_extent(default_extent),
422 image_count: DEFAULT_IMAGE_COUNT
423 .max(*caps.image_count.start())
424 .min(*caps.image_count.end()),
425 image_layers: 1,
426 image_usage: DEFAULT_USAGE,
427 }
428 }
429
430 /// Specify the presentation mode.
431 pub fn with_present_mode(mut self, mode: PresentMode) -> Self {
432 self.present_mode = mode;
433 self
434 }
435
436 /// Specify the presentation mode.
437 pub fn with_composite_alpha_mode(mut self, mode: CompositeAlphaMode) -> Self {
438 self.composite_alpha_mode = mode;
439 self
440 }
441
442 /// Specify the usage of backbuffer images.
443 pub fn with_image_usage(mut self, usage: image::Usage) -> Self {
444 self.image_usage = usage;
445 self
446 }
447
448 /// Specify the count of backbuffer image.
449 pub fn with_image_count(mut self, count: SwapImageIndex) -> Self {
450 self.image_count = count;
451 self
452 }
453
454 // TODO: depth-only, stencil-only, swapchain size, present modes, etc.
455}
456
457/// Marker value returned if the swapchain no longer matches the surface properties exactly,
458/// but can still be used to present to the surface successfully.
459#[derive(Debug)]
460pub struct Suboptimal;
461
462/// Error occurred caused surface to be lost.
463#[derive(Clone, Debug, PartialEq, thiserror::Error)]
464#[error("Swapchain is out of date and needs to be re-created")]
465pub struct OutOfDate;
466
467/// Error on acquiring the next image from a swapchain.
468#[derive(Clone, Debug, PartialEq, thiserror::Error)]
469pub enum AcquireError {
470 /// Out of either host or device memory.
471 #[error(transparent)]
472 OutOfMemory(#[from] device::OutOfMemory),
473 /// No image was ready and no timeout was specified.
474 #[error("No image ready (timeout: {timeout:})")]
475 NotReady {
476 /// Time has ran out.
477 timeout: bool,
478 },
479 /// The swapchain is no longer in sync with the surface, needs to be re-created.
480 #[error(transparent)]
481 OutOfDate(#[from] OutOfDate),
482 /// The surface was lost, and the swapchain is no longer usable.
483 #[error(transparent)]
484 SurfaceLost(#[from] SurfaceLost),
485 /// Device is lost
486 #[error(transparent)]
487 DeviceLost(#[from] device::DeviceLost),
488}
489
490/// Error on acquiring the next image from a swapchain.
491#[derive(Clone, Debug, PartialEq, thiserror::Error)]
492pub enum PresentError {
493 /// Out of either host or device memory.
494 #[error(transparent)]
495 OutOfMemory(#[from] device::OutOfMemory),
496 /// The swapchain is no longer in sync with the surface, needs to be re-created.
497 #[error(transparent)]
498 OutOfDate(#[from] OutOfDate),
499 /// The surface was lost, and the swapchain is no longer usable.
500 #[error(transparent)]
501 SurfaceLost(#[from] SurfaceLost),
502 /// Device is lost
503 #[error(transparent)]
504 DeviceLost(#[from] device::DeviceLost),
505}
506
507/// Error occurred during surface creation.
508#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
509pub enum InitError {
510 /// Window handle is not supported by the backend.
511 #[error("Specified window handle is unsupported")]
512 UnsupportedWindowHandle,
513}