// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use crate::format::Format; use crate::image::ImageUsage; use std::iter::FromIterator; /// The capabilities of a surface when used by a physical device. /// /// You have to match these capabilities when you create a swapchain. #[derive(Clone, Debug)] pub struct Capabilities { /// Minimum number of images that must be present in the swapchain. pub min_image_count: u32, /// Maximum number of images that must be present in the swapchain, or `None` if there is no /// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as /// you may still get out of memory errors. pub max_image_count: Option, /// The current dimensions of the surface. `None` means that the surface's dimensions will /// depend on the dimensions of the swapchain that you are going to create. pub current_extent: Option<[u32; 2]>, /// Minimum width and height of a swapchain that uses this surface. pub min_image_extent: [u32; 2], /// Maximum width and height of a swapchain that uses this surface. pub max_image_extent: [u32; 2], /// Maximum number of image layers if you create an image array. The minimum is 1. pub max_image_array_layers: u32, /// List of transforms supported for the swapchain. pub supported_transforms: SupportedSurfaceTransforms, /// Current transform used by the surface. pub current_transform: SurfaceTransform, /// List of composite alpha modes supports for the swapchain. pub supported_composite_alpha: SupportedCompositeAlpha, /// List of image usages that are supported for images of the swapchain. Only /// the `color_attachment` usage is guaranteed to be supported. pub supported_usage_flags: ImageUsage, /// List of formats supported for the swapchain. pub supported_formats: Vec<(Format, ColorSpace)>, // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/207 /// List of present modes that are supported. `Fifo` is always guaranteed to be supported. pub present_modes: SupportedPresentModes, } /// The way presenting a swapchain is accomplished. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(i32)] pub enum PresentMode { /// Immediately shows the image to the user. May result in visible tearing. Immediate = ash::vk::PresentModeKHR::IMMEDIATE.as_raw(), /// The action of presenting an image puts it in wait. When the next vertical blanking period /// happens, the waiting image is effectively shown to the user. If an image is presented while /// another one is waiting, it is replaced. Mailbox = ash::vk::PresentModeKHR::MAILBOX.as_raw(), /// The action of presenting an image adds it to a queue of images. At each vertical blanking /// period, the queue is popped and an image is presented. /// /// Guaranteed to be always supported. /// /// This is the equivalent of OpenGL's `SwapInterval` with a value of 1. Fifo = ash::vk::PresentModeKHR::FIFO.as_raw(), /// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking /// period then it is equivalent to `Immediate`. /// /// This is the equivalent of OpenGL's `SwapInterval` with a value of -1. Relaxed = ash::vk::PresentModeKHR::FIFO_RELAXED.as_raw(), // TODO: These can't be enabled yet because they have to be used with shared present surfaces // which vulkano doesnt support yet. //SharedDemand = ash::vk::PresentModeKHR::SHARED_DEMAND_REFRESH, //SharedContinuous = ash::vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH, } impl From for ash::vk::PresentModeKHR { #[inline] fn from(val: PresentMode) -> Self { Self::from_raw(val as i32) } } /// List of `PresentMode`s that are supported. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SupportedPresentModes { pub immediate: bool, pub mailbox: bool, pub fifo: bool, pub relaxed: bool, pub shared_demand: bool, pub shared_continuous: bool, } impl FromIterator for SupportedPresentModes { fn from_iter(iter: T) -> Self where T: IntoIterator, { let mut result = SupportedPresentModes::none(); for e in iter { match e { ash::vk::PresentModeKHR::IMMEDIATE => result.immediate = true, ash::vk::PresentModeKHR::MAILBOX => result.mailbox = true, ash::vk::PresentModeKHR::FIFO => result.fifo = true, ash::vk::PresentModeKHR::FIFO_RELAXED => result.relaxed = true, ash::vk::PresentModeKHR::SHARED_DEMAND_REFRESH => result.shared_demand = true, ash::vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH => { result.shared_continuous = true } _ => {} } } result } } impl SupportedPresentModes { /// Builds a `SupportedPresentModes` with all fields set to false. #[inline] pub fn none() -> SupportedPresentModes { SupportedPresentModes { immediate: false, mailbox: false, fifo: false, relaxed: false, shared_demand: false, shared_continuous: false, } } /// Returns true if the given present mode is in this list of supported modes. #[inline] pub fn supports(&self, mode: PresentMode) -> bool { match mode { PresentMode::Immediate => self.immediate, PresentMode::Mailbox => self.mailbox, PresentMode::Fifo => self.fifo, PresentMode::Relaxed => self.relaxed, } } /// Returns an iterator to the list of supported present modes. #[inline] pub fn iter(&self) -> SupportedPresentModesIter { SupportedPresentModesIter(self.clone()) } } /// Enumeration of the `PresentMode`s that are supported. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SupportedPresentModesIter(SupportedPresentModes); impl Iterator for SupportedPresentModesIter { type Item = PresentMode; #[inline] fn next(&mut self) -> Option { if self.0.immediate { self.0.immediate = false; return Some(PresentMode::Immediate); } if self.0.mailbox { self.0.mailbox = false; return Some(PresentMode::Mailbox); } if self.0.fifo { self.0.fifo = false; return Some(PresentMode::Fifo); } if self.0.relaxed { self.0.relaxed = false; return Some(PresentMode::Relaxed); } None } } /// A transformation to apply to the image before showing it on the screen. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] pub enum SurfaceTransform { /// Don't transform the image. Identity = ash::vk::SurfaceTransformFlagsKHR::IDENTITY.as_raw(), /// Rotate 90 degrees. Rotate90 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_90.as_raw(), /// Rotate 180 degrees. Rotate180 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_180.as_raw(), /// Rotate 270 degrees. Rotate270 = ash::vk::SurfaceTransformFlagsKHR::ROTATE_270.as_raw(), /// Mirror the image horizontally. HorizontalMirror = ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR.as_raw(), /// Mirror the image horizontally and rotate 90 degrees. HorizontalMirrorRotate90 = ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_90.as_raw(), /// Mirror the image horizontally and rotate 180 degrees. HorizontalMirrorRotate180 = ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_180.as_raw(), /// Mirror the image horizontally and rotate 270 degrees. HorizontalMirrorRotate270 = ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_270.as_raw(), /// Let the operating system or driver implementation choose. Inherit = ash::vk::SurfaceTransformFlagsKHR::INHERIT.as_raw(), } impl From for ash::vk::SurfaceTransformFlagsKHR { #[inline] fn from(val: SurfaceTransform) -> Self { Self::from_raw(val as u32) } } /// How the alpha values of the pixels of the window are treated. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] pub enum CompositeAlpha { /// The alpha channel of the image is ignored. All the pixels are considered as if they have a /// value of 1.0. Opaque = ash::vk::CompositeAlphaFlagsKHR::OPAQUE.as_raw(), /// The alpha channel of the image is respected. The color channels are expected to have /// already been multiplied by the alpha value. PreMultiplied = ash::vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED.as_raw(), /// The alpha channel of the image is respected. The color channels will be multiplied by the /// alpha value by the compositor before being added to what is behind. PostMultiplied = ash::vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED.as_raw(), /// Let the operating system or driver implementation choose. Inherit = ash::vk::CompositeAlphaFlagsKHR::INHERIT.as_raw(), } impl From for ash::vk::CompositeAlphaFlagsKHR { #[inline] fn from(val: CompositeAlpha) -> Self { Self::from_raw(val as u32) } } /// List of supported composite alpha modes. /// /// See the docs of `CompositeAlpha`. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[allow(missing_docs)] pub struct SupportedCompositeAlpha { pub opaque: bool, pub pre_multiplied: bool, pub post_multiplied: bool, pub inherit: bool, } impl From for SupportedCompositeAlpha { #[inline] fn from(val: ash::vk::CompositeAlphaFlagsKHR) -> SupportedCompositeAlpha { let mut result = SupportedCompositeAlpha::none(); if !(val & ash::vk::CompositeAlphaFlagsKHR::OPAQUE).is_empty() { result.opaque = true; } if !(val & ash::vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED).is_empty() { result.pre_multiplied = true; } if !(val & ash::vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED).is_empty() { result.post_multiplied = true; } if !(val & ash::vk::CompositeAlphaFlagsKHR::INHERIT).is_empty() { result.inherit = true; } result } } impl SupportedCompositeAlpha { /// Builds a `SupportedCompositeAlpha` with all fields set to false. #[inline] pub fn none() -> SupportedCompositeAlpha { SupportedCompositeAlpha { opaque: false, pre_multiplied: false, post_multiplied: false, inherit: false, } } /// Returns true if the given `CompositeAlpha` is in this list. #[inline] pub fn supports(&self, value: CompositeAlpha) -> bool { match value { CompositeAlpha::Opaque => self.opaque, CompositeAlpha::PreMultiplied => self.pre_multiplied, CompositeAlpha::PostMultiplied => self.post_multiplied, CompositeAlpha::Inherit => self.inherit, } } /// Returns an iterator to the list of supported composite alpha. #[inline] pub fn iter(&self) -> SupportedCompositeAlphaIter { SupportedCompositeAlphaIter(self.clone()) } } /// Enumeration of the `CompositeAlpha` that are supported. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SupportedCompositeAlphaIter(SupportedCompositeAlpha); impl Iterator for SupportedCompositeAlphaIter { type Item = CompositeAlpha; #[inline] fn next(&mut self) -> Option { if self.0.opaque { self.0.opaque = false; return Some(CompositeAlpha::Opaque); } if self.0.pre_multiplied { self.0.pre_multiplied = false; return Some(CompositeAlpha::PreMultiplied); } if self.0.post_multiplied { self.0.post_multiplied = false; return Some(CompositeAlpha::PostMultiplied); } if self.0.inherit { self.0.inherit = false; return Some(CompositeAlpha::Inherit); } None } } /// List of supported composite alpha modes. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SupportedSurfaceTransforms { pub identity: bool, pub rotate90: bool, pub rotate180: bool, pub rotate270: bool, pub horizontal_mirror: bool, pub horizontal_mirror_rotate90: bool, pub horizontal_mirror_rotate180: bool, pub horizontal_mirror_rotate270: bool, pub inherit: bool, } impl From for SupportedSurfaceTransforms { fn from(val: ash::vk::SurfaceTransformFlagsKHR) -> Self { macro_rules! v { ($val:expr, $out:ident, $e:expr, $f:ident) => { if !($val & $e).is_empty() { $out.$f = true; } }; } let mut result = SupportedSurfaceTransforms::none(); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::IDENTITY, identity ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::ROTATE_90, rotate90 ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::ROTATE_180, rotate180 ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::ROTATE_270, rotate270 ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR, horizontal_mirror ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_90, horizontal_mirror_rotate90 ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_180, horizontal_mirror_rotate180 ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::HORIZONTAL_MIRROR_ROTATE_270, horizontal_mirror_rotate270 ); v!( val, result, ash::vk::SurfaceTransformFlagsKHR::INHERIT, inherit ); result } } impl SupportedSurfaceTransforms { /// Builds a `SupportedSurfaceTransforms` with all fields set to false. #[inline] pub fn none() -> SupportedSurfaceTransforms { SupportedSurfaceTransforms { identity: false, rotate90: false, rotate180: false, rotate270: false, horizontal_mirror: false, horizontal_mirror_rotate90: false, horizontal_mirror_rotate180: false, horizontal_mirror_rotate270: false, inherit: false, } } /// Returns true if the given `SurfaceTransform` is in this list. #[inline] pub fn supports(&self, value: SurfaceTransform) -> bool { match value { SurfaceTransform::Identity => self.identity, SurfaceTransform::Rotate90 => self.rotate90, SurfaceTransform::Rotate180 => self.rotate180, SurfaceTransform::Rotate270 => self.rotate270, SurfaceTransform::HorizontalMirror => self.horizontal_mirror, SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90, SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180, SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270, SurfaceTransform::Inherit => self.inherit, } } /// Returns an iterator to the list of supported composite alpha. #[inline] pub fn iter(&self) -> SupportedSurfaceTransformsIter { SupportedSurfaceTransformsIter(self.clone()) } } /// Enumeration of the `SurfaceTransform` that are supported. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SupportedSurfaceTransformsIter(SupportedSurfaceTransforms); impl Iterator for SupportedSurfaceTransformsIter { type Item = SurfaceTransform; #[inline] fn next(&mut self) -> Option { if self.0.identity { self.0.identity = false; return Some(SurfaceTransform::Identity); } if self.0.rotate90 { self.0.rotate90 = false; return Some(SurfaceTransform::Rotate90); } if self.0.rotate180 { self.0.rotate180 = false; return Some(SurfaceTransform::Rotate180); } if self.0.rotate270 { self.0.rotate270 = false; return Some(SurfaceTransform::Rotate270); } if self.0.horizontal_mirror { self.0.horizontal_mirror = false; return Some(SurfaceTransform::HorizontalMirror); } if self.0.horizontal_mirror_rotate90 { self.0.horizontal_mirror_rotate90 = false; return Some(SurfaceTransform::HorizontalMirrorRotate90); } if self.0.horizontal_mirror_rotate180 { self.0.horizontal_mirror_rotate180 = false; return Some(SurfaceTransform::HorizontalMirrorRotate180); } if self.0.horizontal_mirror_rotate270 { self.0.horizontal_mirror_rotate270 = false; return Some(SurfaceTransform::HorizontalMirrorRotate270); } if self.0.inherit { self.0.inherit = false; return Some(SurfaceTransform::Inherit); } None } } impl Default for SurfaceTransform { #[inline] fn default() -> SurfaceTransform { SurfaceTransform::Identity } } /// How the presentation engine should interpret the data. /// /// # A quick lesson about color spaces /// /// ## What is a color space? /// /// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the /// past, computers would simply send to the monitor the intensity of each of the three components. /// /// This proved to be problematic, because depending on the brand of the monitor the colors would /// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a /// bit more orange than on others. /// /// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB, /// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have /// a precise absolute meaning in terms of color, that is the same across all systems and monitors. /// /// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the /// > representation of the data, but not how it is interpreted. You can think of this a bit like /// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which /// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other /// > words it is the way the value should be interpreted. /// /// The most commonly used color space today is sRGB. Most monitors today use this color space, /// and most images files are encoded in this color space. /// /// ## Pixel formats and linear vs non-linear /// /// In Vulkan all images have a specific format in which the data is stored. The data of an image /// consists of pixels in RGB but contains no information about the color space (or lack thereof) /// of these pixels. You are free to store them in whatever color space you want. /// /// But one big practical problem with color spaces is that they are sometimes not linear, and in /// particular the popular sRGB color space is not linear. In a non-linear color space, a value of /// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This /// is problematic, because operations such as taking the average of two colors or calculating the /// lighting of a texture with a dot product are mathematically incorrect and will produce /// incorrect colors. /// /// > **Note**: If the texture format has an alpha component, it is not affected by the color space /// > and always behaves linearly. /// /// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are /// expected to contain RGB data in the sRGB color space. When you sample an image with such a /// format from a shader, the implementation will automatically turn the pixel values into a linear /// color space that is suitable for linear operations (such as additions or multiplications). /// When you write to a framebuffer attachment with such a format, the implementation will /// automatically perform the opposite conversion. These conversions are most of the time performed /// by the hardware and incur no additional cost. /// /// ## Color space of the swapchain /// /// The color space that you specify when you create a swapchain is how the implementation will /// interpret the raw data inside of the image. /// /// > **Note**: The implementation can choose to send the data in the swapchain image directly to /// > the monitor, but it can also choose to write it in an intermediary buffer that is then read /// > by the operating system or windowing system. Therefore the color space that the /// > implementation supports is not necessarily the same as the one supported by the monitor. /// /// It is *your* job to ensure that the data in the swapchain image is in the color space /// that is specified here, otherwise colors will be incorrect. /// The implementation will never perform any additional automatic conversion after the colors have /// been written to the swapchain image. /// /// # How do I handle this correctly? /// /// The easiest way to handle color spaces in a cross-platform program is: /// /// - Always request the `SrgbNonLinear` color space when creating the swapchain. /// - Make sure that all your image files use the sRGB color space, and load them in images whose /// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations /// or to store non-color data. /// - Swapchain images should have a format with the `Srgb` suffix. /// /// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the /// > the implementation or not. See . /// /// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles /// > talking about gamma correction and suggestion to put your colors to the power 2.2 for /// > example. These are all hacks and you should use the sRGB pixel formats instead. /// /// If you follow these three rules, then everything should render the same way on all platforms. /// /// Additionally you can try detect whether the implementation supports any additional color space /// and perform a manual conversion to that color space from inside your shader. /// #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(i32)] pub enum ColorSpace { SrgbNonLinear = ash::vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw(), DisplayP3NonLinear = ash::vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT.as_raw(), ExtendedSrgbLinear = ash::vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT.as_raw(), ExtendedSrgbNonLinear = ash::vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT.as_raw(), DciP3Linear = ash::vk::ColorSpaceKHR::DCI_P3_LINEAR_EXT.as_raw(), DciP3NonLinear = ash::vk::ColorSpaceKHR::DCI_P3_NONLINEAR_EXT.as_raw(), Bt709Linear = ash::vk::ColorSpaceKHR::BT709_LINEAR_EXT.as_raw(), Bt709NonLinear = ash::vk::ColorSpaceKHR::BT709_NONLINEAR_EXT.as_raw(), Bt2020Linear = ash::vk::ColorSpaceKHR::BT2020_LINEAR_EXT.as_raw(), Hdr10St2084 = ash::vk::ColorSpaceKHR::HDR10_ST2084_EXT.as_raw(), DolbyVision = ash::vk::ColorSpaceKHR::DOLBYVISION_EXT.as_raw(), Hdr10Hlg = ash::vk::ColorSpaceKHR::HDR10_HLG_EXT.as_raw(), AdobeRgbLinear = ash::vk::ColorSpaceKHR::ADOBERGB_LINEAR_EXT.as_raw(), AdobeRgbNonLinear = ash::vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT.as_raw(), PassThrough = ash::vk::ColorSpaceKHR::PASS_THROUGH_EXT.as_raw(), DisplayNative = ash::vk::ColorSpaceKHR::DISPLAY_NATIVE_AMD.as_raw(), } impl From for ash::vk::ColorSpaceKHR { #[inline] fn from(val: ColorSpace) -> Self { Self::from_raw(val as i32) } } impl From for ColorSpace { #[inline] fn from(val: ash::vk::ColorSpaceKHR) -> Self { match val { ash::vk::ColorSpaceKHR::SRGB_NONLINEAR => ColorSpace::SrgbNonLinear, ash::vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear, ash::vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT => ColorSpace::ExtendedSrgbLinear, ash::vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT => { ColorSpace::ExtendedSrgbNonLinear } ash::vk::ColorSpaceKHR::DCI_P3_LINEAR_EXT => ColorSpace::DciP3Linear, ash::vk::ColorSpaceKHR::DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear, ash::vk::ColorSpaceKHR::BT709_LINEAR_EXT => ColorSpace::Bt709Linear, ash::vk::ColorSpaceKHR::BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear, ash::vk::ColorSpaceKHR::BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear, ash::vk::ColorSpaceKHR::HDR10_ST2084_EXT => ColorSpace::Hdr10St2084, ash::vk::ColorSpaceKHR::DOLBYVISION_EXT => ColorSpace::DolbyVision, ash::vk::ColorSpaceKHR::HDR10_HLG_EXT => ColorSpace::Hdr10Hlg, ash::vk::ColorSpaceKHR::ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear, ash::vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear, ash::vk::ColorSpaceKHR::PASS_THROUGH_EXT => ColorSpace::PassThrough, ash::vk::ColorSpaceKHR::DISPLAY_NATIVE_AMD => ColorSpace::DisplayNative, _ => panic!("Wrong value for color space enum {:?}", val), } } }