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}