diff options
Diffstat (limited to 'third_party/rust/gfx-hal/src/window.rs')
-rw-r--r-- | third_party/rust/gfx-hal/src/window.rs | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/third_party/rust/gfx-hal/src/window.rs b/third_party/rust/gfx-hal/src/window.rs new file mode 100644 index 0000000000..b0bc6801e7 --- /dev/null +++ b/third_party/rust/gfx-hal/src/window.rs @@ -0,0 +1,563 @@ +//! 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<device::OutOfMemory> for CreationError { + fn from(error: device::OutOfMemory) -> Self { + CreationError::OutOfMemory(error) + } +} + +impl From<device::DeviceLost> for CreationError { + fn from(error: device::DeviceLost) -> Self { + CreationError::DeviceLost(error) + } +} + +impl From<device::SurfaceLost> for CreationError { + fn from(error: device::SurfaceLost) -> Self { + CreationError::SurfaceLost(error) + } +} + +impl From<device::WindowInUse> 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<image::Extent> 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<SwapImageIndex>, + + /// Current extent of the surface. + /// + /// `None` if the surface has no explicit size, depending on the swapchain extent. + pub current_extent: Option<Extent2D>, + + /// Range of supported extents. + /// + /// `current_extent` must be inside this range. + pub extents: RangeInclusive<Extent2D>, + + /// 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<B: Backend>: 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<Vec<Format>>; +} + +/// A surface trait that exposes the ability to present images on the +/// associtated swap chain. +pub trait PresentationSurface<B: Backend>: Surface<B> { + /// An opaque type wrapping the swapchain image. + type SwapchainImage: Borrow<B::Image> + Borrow<B::ImageView> + 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<Suboptimal>), 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 {} |