//! Windowing system interoperability. //! //! Screen presentation (fullscreen or window) of images requires two objects: //! //! * [Surface][Surface] is an abstraction of a native screen or window, for graphics use. //! * [Swapchain][Swapchain] is a chain of multiple images, which can be presented on //! a surface. //! //! ## Window //! //! `gfx-hal` does not provide any methods for creating a native window or screen. //! This is handled exeternally, either by managing your own window or by using a //! library such as [winit](https://github.com/rust-windowing/winit), and providing //! the [raw window handle](https://github.com/rust-windowing/raw-window-handle). //! //! ## Surface //! //! Once you have a window handle, you need to [create a surface][crate::Instance::create_surface] //! compatible with the [instance][crate::Instance] of the graphics API you currently use. //! //! ## PresentationSurface //! //! A surface has an implicit swapchain in it. //! //! The most interesting part of a swapchain are the contained presentable images/backbuffers. //! Presentable images are specialized images, which can be presented on the screen. They are //! 2D color images with optionally associated depth-stencil images. //! //! The common steps for presentation of a frame are acquisition and presentation: //! //! ```no_run //! # extern crate gfx_backend_empty as empty; //! # extern crate gfx_hal; //! # fn main() { //! # use gfx_hal::prelude::*; //! //! # let mut surface: empty::Surface = return; //! # let device: empty::Device = return; //! # let mut present_queue: empty::CommandQueue = return; //! # unsafe { //! let render_semaphore = device.create_semaphore().unwrap(); //! //! let (frame, suboptimal) = surface.acquire_image(!0).unwrap(); //! // render the scene.. //! // `render_semaphore` will be signalled once rendering has been finished //! present_queue.present(&mut surface, frame, Some(&render_semaphore)); //! # }} //! ``` //! //! Queues need to synchronize with the presentation engine, usually done via signalling a semaphore //! once a frame is available for rendering and waiting on a separate semaphore until scene rendering //! has finished. //! //! ### Recreation //! //! DOC TODO use crate::{device, format::Format, image, Backend}; use std::{ any::Any, borrow::Borrow, cmp::{max, min}, fmt, ops::RangeInclusive, }; /// Default image usage for the swapchain. pub const DEFAULT_USAGE: image::Usage = image::Usage::COLOR_ATTACHMENT; /// Default image count for the swapchain. pub const DEFAULT_IMAGE_COUNT: SwapImageIndex = 3; /// Error occurred during swapchain creation. #[derive(Clone, Debug, PartialEq)] pub enum CreationError { /// Out of either host or device memory. OutOfMemory(device::OutOfMemory), /// Device is lost DeviceLost(device::DeviceLost), /// Surface is lost SurfaceLost(device::SurfaceLost), /// Window in use WindowInUse(device::WindowInUse), } impl From for CreationError { fn from(error: device::OutOfMemory) -> Self { CreationError::OutOfMemory(error) } } impl From for CreationError { fn from(error: device::DeviceLost) -> Self { CreationError::DeviceLost(error) } } impl From for CreationError { fn from(error: device::SurfaceLost) -> Self { CreationError::SurfaceLost(error) } } impl From for CreationError { fn from(error: device::WindowInUse) -> Self { CreationError::WindowInUse(error) } } impl std::fmt::Display for CreationError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { CreationError::OutOfMemory(err) => { write!(fmt, "Failed to create or configure swapchain: {}", err) } CreationError::DeviceLost(err) => { write!(fmt, "Failed to create or configure swapchain: {}", err) } CreationError::SurfaceLost(err) => { write!(fmt, "Failed to create or configure swapchain: {}", err) } CreationError::WindowInUse(err) => { write!(fmt, "Failed to create or configure swapchain: {}", err) } } } } impl std::error::Error for CreationError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { CreationError::OutOfMemory(err) => Some(err), CreationError::DeviceLost(err) => Some(err), CreationError::SurfaceLost(err) => Some(err), CreationError::WindowInUse(err) => Some(err), } } } /// An extent describes the size of a rectangle, such as /// a window or texture. It is not used for referring to a /// sub-rectangle; for that see `command::Rect`. #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Extent2D { /// Width pub width: image::Size, /// Height pub height: image::Size, } impl From for Extent2D { fn from(ex: image::Extent) -> Self { Extent2D { width: ex.width, height: ex.height, } } } impl Extent2D { /// Convert into a regular image extent. pub fn to_extent(&self) -> image::Extent { image::Extent { width: self.width, height: self.height, depth: 1, } } } /// Describes information about what a `Surface`'s properties are. /// Fetch this with [Surface::capabilities]. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SurfaceCapabilities { /// Number of presentable images supported by the adapter for a swapchain /// created from this surface. /// /// - `image_count.start` must be at least 1. /// - `image_count.end` must be larger or equal to `image_count.start`. pub image_count: RangeInclusive, /// Current extent of the surface. /// /// `None` if the surface has no explicit size, depending on the swapchain extent. pub current_extent: Option, /// Range of supported extents. /// /// `current_extent` must be inside this range. pub extents: RangeInclusive, /// Maximum number of layers supported for presentable images. /// /// Must be at least 1. pub max_image_layers: image::Layer, /// Supported image usage flags. pub usage: image::Usage, /// A bitmask of supported presentation modes. pub present_modes: PresentMode, /// A bitmask of supported alpha composition modes. pub composite_alpha_modes: CompositeAlphaMode, } impl SurfaceCapabilities { fn clamped_extent(&self, default_extent: Extent2D) -> Extent2D { match self.current_extent { Some(current) => current, None => { let (min_width, max_width) = (self.extents.start().width, self.extents.end().width); let (min_height, max_height) = (self.extents.start().height, self.extents.end().height); // clamp the default_extent to within the allowed surface sizes let width = min(max_width, max(default_extent.width, min_width)); let height = min(max_height, max(default_extent.height, min_height)); Extent2D { width, height } } } } } /// A `Surface` abstracts the surface of a native window. pub trait Surface: fmt::Debug + Any + Send + Sync { /// Check if the queue family supports presentation to this surface. fn supports_queue_family(&self, family: &B::QueueFamily) -> bool; /// Query surface capabilities for this physical device. /// /// Use this function for configuring swapchain creation. fn capabilities(&self, physical_device: &B::PhysicalDevice) -> SurfaceCapabilities; /// Query surface formats for this physical device. /// /// This function may be slow. It's typically used during the initialization only. /// /// Note: technically the surface support formats may change at the point /// where an application needs to recreate the swapchain, e.g. when the window /// is moved to a different monitor. /// /// If `None` is returned then the surface has no preferred format and the /// application may use any desired format. fn supported_formats(&self, physical_device: &B::PhysicalDevice) -> Option>; } /// A surface trait that exposes the ability to present images on the /// associtated swap chain. pub trait PresentationSurface: Surface { /// An opaque type wrapping the swapchain image. type SwapchainImage: Borrow + Borrow + fmt::Debug + Send + Sync; /// Set up the swapchain associated with the surface to have the given format. unsafe fn configure_swapchain( &mut self, device: &B::Device, config: SwapchainConfig, ) -> Result<(), CreationError>; /// Remove the associated swapchain from this surface. /// /// This has to be done before the surface is dropped. unsafe fn unconfigure_swapchain(&mut self, device: &B::Device); /// Acquire a new swapchain image for rendering. /// /// May fail according to one of the reasons indicated in `AcquireError` enum. /// /// # Synchronization /// /// The acquired image is available to render. No synchronization is required. unsafe fn acquire_image( &mut self, timeout_ns: u64, ) -> Result<(Self::SwapchainImage, Option), AcquireError>; } /// Index of an image in the swapchain. /// /// The swapchain is a series of one or more images, usually /// with one being drawn on while the other is displayed by /// the GPU (aka double-buffering). A `SwapImageIndex` refers /// to a particular image in the swapchain. pub type SwapImageIndex = u32; bitflags!( /// Specifies the mode regulating how a swapchain presents frames. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PresentMode: u32 { /// Don't ever wait for v-sync. const IMMEDIATE = 0x1; /// Wait for v-sync, overwrite the last rendered frame. const MAILBOX = 0x2; /// Present frames in the same order they are rendered. const FIFO = 0x4; /// Don't wait for the next v-sync if we just missed it. const RELAXED = 0x8; } ); bitflags!( /// Specifies how the alpha channel of the images should be handled during /// compositing. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct CompositeAlphaMode: u32 { /// The alpha channel, if it exists, of the images is ignored in the /// compositing process. Instead, the image is treated as if it has a /// constant alpha of 1.0. const OPAQUE = 0x1; /// The alpha channel, if it exists, of the images is respected in the /// compositing process. The non-alpha channels of the image are /// expected to already be multiplied by the alpha channel by the /// application. const PREMULTIPLIED = 0x2; /// The alpha channel, if it exists, of the images is respected in the /// compositing process. The non-alpha channels of the image are not /// expected to already be multiplied by the alpha channel by the /// application; instead, the compositor will multiply the non-alpha /// channels of the image by the alpha channel during compositing. const POSTMULTIPLIED = 0x4; /// The way in which the presentation engine treats the alpha channel in /// the images is unknown to gfx-hal. Instead, the application is /// responsible for setting the composite alpha blending mode using /// native window system commands. If the application does not set the /// blending mode using native window system commands, then a /// platform-specific default will be used. const INHERIT = 0x8; } ); /// Contains all the data necessary to create a new `Swapchain`: /// color, depth, and number of images. /// /// # Examples /// /// This type implements the builder pattern, method calls can be /// easily chained. /// /// ```no_run /// # extern crate gfx_hal; /// # fn main() { /// # use gfx_hal::window::SwapchainConfig; /// # use gfx_hal::format::Format; /// let config = SwapchainConfig::new(100, 100, Format::Bgra8Unorm, 2); /// # } /// ``` #[derive(Debug, Clone)] pub struct SwapchainConfig { /// Presentation mode. pub present_mode: PresentMode, /// Alpha composition mode. pub composite_alpha_mode: CompositeAlphaMode, /// Format of the backbuffer images. pub format: Format, /// Requested image extent. Must be in /// `SurfaceCapabilities::extents` range. pub extent: Extent2D, /// Number of images in the swapchain. Must be in /// `SurfaceCapabilities::image_count` range. pub image_count: SwapImageIndex, /// Number of image layers. Must be lower or equal to /// `SurfaceCapabilities::max_image_layers`. pub image_layers: image::Layer, /// Image usage of the backbuffer images. pub image_usage: image::Usage, } impl SwapchainConfig { /// Create a new default configuration (color images only). pub fn new(width: u32, height: u32, format: Format, image_count: SwapImageIndex) -> Self { SwapchainConfig { present_mode: PresentMode::FIFO, composite_alpha_mode: CompositeAlphaMode::OPAQUE, format, extent: Extent2D { width, height }, image_count, image_layers: 1, image_usage: DEFAULT_USAGE, } } /// Create a swapchain configuration based on the capabilities /// returned from a physical device query. If the surface does not /// specify a current size, default_extent is clamped and used instead. /// /// The default values are taken from `DEFAULT_USAGE` and `DEFAULT_IMAGE_COUNT`. pub fn from_caps(caps: &SurfaceCapabilities, format: Format, default_extent: Extent2D) -> Self { let composite_alpha_mode = if caps .composite_alpha_modes .contains(CompositeAlphaMode::INHERIT) { CompositeAlphaMode::INHERIT } else if caps .composite_alpha_modes .contains(CompositeAlphaMode::OPAQUE) { CompositeAlphaMode::OPAQUE } else { panic!("neither INHERIT or OPAQUE CompositeAlphaMode(s) are supported") }; let present_mode = if caps.present_modes.contains(PresentMode::MAILBOX) { PresentMode::MAILBOX } else if caps.present_modes.contains(PresentMode::FIFO) { PresentMode::FIFO } else { panic!("FIFO PresentMode is not supported") }; SwapchainConfig { present_mode, composite_alpha_mode, format, extent: caps.clamped_extent(default_extent), image_count: DEFAULT_IMAGE_COUNT .max(*caps.image_count.start()) .min(*caps.image_count.end()), image_layers: 1, image_usage: DEFAULT_USAGE, } } /// Specify the presentation mode. pub fn with_present_mode(mut self, mode: PresentMode) -> Self { self.present_mode = mode; self } /// Specify the presentation mode. pub fn with_composite_alpha_mode(mut self, mode: CompositeAlphaMode) -> Self { self.composite_alpha_mode = mode; self } /// Specify the usage of backbuffer images. pub fn with_image_usage(mut self, usage: image::Usage) -> Self { self.image_usage = usage; self } /// Specify the count of backbuffer image. pub fn with_image_count(mut self, count: SwapImageIndex) -> Self { self.image_count = count; self } // TODO: depth-only, stencil-only, swapchain size, present modes, etc. } /// Marker value returned if the swapchain no longer matches the surface properties exactly, /// but can still be used to present to the surface successfully. #[derive(Debug)] pub struct Suboptimal; /// Error on acquiring the next image from a swapchain. #[derive(Clone, Debug, PartialEq)] pub enum AcquireError { /// Out of either host or device memory. OutOfMemory(device::OutOfMemory), /// No image was ready and no timeout was specified. NotReady, /// No image was ready after the specified timeout expired. Timeout, /// The swapchain is no longer in sync with the surface, needs to be re-created. OutOfDate, /// The surface was lost, and the swapchain is no longer usable. SurfaceLost(device::SurfaceLost), /// Device is lost DeviceLost(device::DeviceLost), } impl std::fmt::Display for AcquireError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AcquireError::OutOfMemory(err) => write!(fmt, "Failed to acqure image: {}", err), AcquireError::NotReady => write!( fmt, "Failed to acqure image: No image ready (timeout wasn't specified)" ), AcquireError::Timeout => { write!(fmt, "Failed to acqure image: No image ready (timeout)") } AcquireError::OutOfDate => write!( fmt, "Failed to acqure image: Swapchain is out of date and needs to be re-created" ), AcquireError::SurfaceLost(err) => write!(fmt, "Failed to acqure image: {}", err), AcquireError::DeviceLost(err) => write!(fmt, "Failed to acqure image: {}", err), } } } impl std::error::Error for AcquireError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { AcquireError::OutOfMemory(err) => Some(err), AcquireError::SurfaceLost(err) => Some(err), AcquireError::DeviceLost(err) => Some(err), _ => None, } } } /// Error on acquiring the next image from a swapchain. #[derive(Clone, Debug, PartialEq)] pub enum PresentError { /// Out of either host or device memory. OutOfMemory(device::OutOfMemory), /// The swapchain is no longer in sync with the surface, needs to be re-created. OutOfDate, /// The surface was lost, and the swapchain is no longer usable. SurfaceLost(device::SurfaceLost), /// Device is lost DeviceLost(device::DeviceLost), } impl std::fmt::Display for PresentError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { PresentError::OutOfMemory(err) => write!(fmt, "Failed to present image: {}", err), PresentError::OutOfDate => write!( fmt, "Failed to present image: Swapchain is out of date and needs to be re-created" ), PresentError::SurfaceLost(err) => write!(fmt, "Failed to present image: {}", err), PresentError::DeviceLost(err) => write!(fmt, "Failed to present image: {}", err), } } } impl std::error::Error for PresentError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { PresentError::OutOfMemory(err) => Some(err), PresentError::SurfaceLost(err) => Some(err), PresentError::DeviceLost(err) => Some(err), _ => None, } } } /// Error occurred during surface creation. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum InitError { /// Window handle is not supported by the backend. UnsupportedWindowHandle, } impl std::fmt::Display for InitError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { InitError::UnsupportedWindowHandle => write!( fmt, "Failed to create surface: Specified window handle is unsupported" ), } } } impl std::error::Error for InitError {}