use alloc::{vec, vec::Vec};
use crate::{link_to_wgpu_docs, link_to_wgpu_item, TextureFormat, TextureUsages};
#[cfg(any(feature = "serde", test))]
use serde::{Deserialize, Serialize};
#[doc = link_to_wgpu_docs!(["presented"]: "struct.SurfaceTexture.html#method.present")]
#[doc = link_to_wgpu_docs!(["`SurfaceTexture::present()`"]: "struct.SurfaceTexture.html#method.present")]
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PresentMode {
AutoVsync = 0,
AutoNoVsync = 1,
#[doc = link_to_wgpu_docs!(["`Surface::get_current_texture()`"]: "struct.Surface.html#method.get_current_texture")]
#[default]
Fifo = 2,
#[doc = link_to_wgpu_docs!(["`Surface::get_current_texture()`"]: "struct.Surface.html#method.get_current_texture")]
FifoRelaxed = 3,
Immediate = 4,
Mailbox = 5,
}
#[repr(C)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
pub enum CompositeAlphaMode {
#[default]
Auto = 0,
Opaque = 1,
PreMultiplied = 2,
PostMultiplied = 3,
Inherit = 4,
}
#[doc = include_str!("color_gamuts.svg")]
#[doc = include_str!("sdr_hdr_range.svg")]
#[doc = link_to_wgpu_docs!(["color space and HDR primer"]: "index.html#surface-color-spaces-and-hdr-output")]
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SurfaceColorSpace {
#[default]
Auto = 0,
Srgb = 1,
ExtendedSrgbLinear = 2,
DisplayP3 = 3,
Bt2100Pq = 4,
Bt2100Hlg = 5,
ExtendedSrgb = 6,
ExtendedDisplayP3 = 7,
}
impl SurfaceColorSpace {
#[must_use]
pub const fn to_color_spaces(self) -> Option<SurfaceColorSpaces> {
match self {
Self::Auto => None,
Self::Srgb => Some(SurfaceColorSpaces::SRGB),
Self::ExtendedSrgbLinear => Some(SurfaceColorSpaces::EXTENDED_SRGB_LINEAR),
Self::DisplayP3 => Some(SurfaceColorSpaces::DISPLAY_P3),
Self::Bt2100Pq => Some(SurfaceColorSpaces::BT2100_PQ),
Self::Bt2100Hlg => Some(SurfaceColorSpaces::BT2100_HLG),
Self::ExtendedSrgb => Some(SurfaceColorSpaces::EXTENDED_SRGB),
Self::ExtendedDisplayP3 => Some(SurfaceColorSpaces::EXTENDED_DISPLAY_P3),
}
}
#[must_use]
pub const fn is_hdr(self) -> bool {
match self {
Self::ExtendedSrgbLinear
| Self::ExtendedSrgb
| Self::ExtendedDisplayP3
| Self::Bt2100Pq
| Self::Bt2100Hlg => true,
Self::Auto | Self::Srgb | Self::DisplayP3 => false,
}
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SurfaceColorSpaces: u32 {
const SRGB = 1 << 0;
const EXTENDED_SRGB_LINEAR = 1 << 1;
const DISPLAY_P3 = 1 << 2;
const BT2100_PQ = 1 << 3;
const BT2100_HLG = 1 << 4;
const EXTENDED_SRGB = 1 << 5;
const EXTENDED_DISPLAY_P3 = 1 << 6;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SurfaceFormatCapabilities {
pub format: TextureFormat,
pub color_spaces: SurfaceColorSpaces,
}
#[derive(Debug)]
pub struct SurfaceCapabilities {
pub formats: Vec<TextureFormat>,
pub format_capabilities: Vec<SurfaceFormatCapabilities>,
pub present_modes: Vec<PresentMode>,
pub alpha_modes: Vec<CompositeAlphaMode>,
pub usages: TextureUsages,
}
impl SurfaceCapabilities {
#[must_use]
pub fn color_spaces(&self, format: TextureFormat) -> SurfaceColorSpaces {
self.format_capabilities
.iter()
.filter(|fc| fc.format == format)
.fold(SurfaceColorSpaces::empty(), |acc, fc| acc | fc.color_spaces)
}
}
impl Default for SurfaceCapabilities {
fn default() -> Self {
Self {
formats: Vec::new(),
format_capabilities: Vec::new(),
present_modes: Vec::new(),
alpha_modes: vec![CompositeAlphaMode::Opaque],
usages: TextureUsages::RENDER_ATTACHMENT,
}
}
}
#[doc = link_to_wgpu_item!(struct Surface)]
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DisplayHdrInfo {
pub luminance: Option<DisplayLuminance>,
pub headroom: Option<DisplayHeadroom>,
pub chromaticity: Option<DisplayChromaticity>,
pub coarse: Option<DisplayCoarseRange>,
pub bits_per_color: Option<u8>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DisplayLuminance {
pub max_nits: Option<f32>,
pub max_full_frame_nits: Option<f32>,
pub min_nits: Option<f32>,
pub sdr_white_nits: Option<f32>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DisplayHeadroom {
pub current: Option<f32>,
pub potential: Option<f32>,
pub reference: Option<f32>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DisplayChromaticity {
pub red: Option<[f32; 2]>,
pub green: Option<[f32; 2]>,
pub blue: Option<[f32; 2]>,
pub white: Option<[f32; 2]>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DisplayCoarseRange {
pub high_dynamic_range: Option<bool>,
pub gamut: Option<DisplayGamut>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum DisplayGamut {
Srgb,
DisplayP3,
Rec2020,
}
impl DisplayHdrInfo {
#[must_use]
pub fn tone_map_headroom(&self) -> Option<f32> {
if let Some(h) = self
.headroom
.and_then(|h| h.current)
.filter(|h| h.is_finite())
{
return Some(h);
}
if self.coarse.and_then(|c| c.high_dynamic_range) == Some(false) {
return Some(1.0);
}
if let Some((max, sdr)) = self
.luminance
.and_then(|l| l.max_nits.zip(l.sdr_white_nits))
.filter(|&(max, sdr)| sdr > 0.0 && max.is_finite() && sdr.is_finite())
{
return Some(max / sdr);
}
None
}
}
#[cfg(test)]
mod display_hdr_info_tests {
use super::*;
#[test]
fn default_is_unknown() {
assert_eq!(DisplayHdrInfo::default().tone_map_headroom(), None);
}
#[test]
fn apple_headroom_is_used_directly() {
let info = DisplayHdrInfo {
headroom: Some(DisplayHeadroom {
current: Some(3.0),
potential: Some(5.0),
reference: None,
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), Some(3.0));
}
#[test]
fn apple_uses_current_not_potential() {
let info = DisplayHdrInfo {
headroom: Some(DisplayHeadroom {
current: Some(1.0),
potential: Some(16.0),
reference: None,
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), Some(1.0));
}
#[test]
fn windows_nits_derive_headroom_only_with_sdr_white() {
let info = DisplayHdrInfo {
luminance: Some(DisplayLuminance {
max_nits: Some(800.0),
sdr_white_nits: Some(200.0),
..Default::default()
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), Some(4.0));
let info = DisplayHdrInfo {
luminance: Some(DisplayLuminance {
max_nits: Some(800.0),
sdr_white_nits: None,
..Default::default()
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), None);
}
#[test]
fn sdr_display_collapses_to_unity() {
let info = DisplayHdrInfo {
coarse: Some(DisplayCoarseRange {
high_dynamic_range: Some(false),
gamut: Some(DisplayGamut::Srgb),
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), Some(1.0));
}
#[test]
fn sdr_display_overrides_panel_nits() {
let info = DisplayHdrInfo {
luminance: Some(DisplayLuminance {
max_nits: Some(270.0),
sdr_white_nits: Some(80.0),
..Default::default()
}),
coarse: Some(DisplayCoarseRange {
high_dynamic_range: Some(false),
gamut: Some(DisplayGamut::DisplayP3),
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), Some(1.0));
}
#[test]
fn coarse_hdr_capable_alone_derives_nothing() {
let info = DisplayHdrInfo {
coarse: Some(DisplayCoarseRange {
high_dynamic_range: Some(true),
gamut: Some(DisplayGamut::Rec2020),
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), None);
}
#[test]
fn non_finite_current_falls_through_to_nits() {
let info = DisplayHdrInfo {
headroom: Some(DisplayHeadroom {
current: Some(f32::INFINITY),
..Default::default()
}),
luminance: Some(DisplayLuminance {
max_nits: Some(1000.0),
sdr_white_nits: Some(100.0),
..Default::default()
}),
..Default::default()
};
assert_eq!(info.tone_map_headroom(), Some(10.0));
}
}
#[doc = link_to_wgpu_item!(struct Surface)]
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SurfaceConfiguration<V> {
pub usage: TextureUsages,
pub format: TextureFormat,
pub color_space: SurfaceColorSpace,
pub width: u32,
pub height: u32,
pub present_mode: PresentMode,
#[doc = link_to_wgpu_docs!(["`Surface::get_current_texture`"]: "struct.Surface.html#method.get_current_texture")]
#[doc = link_to_wgpu_docs!(["`Surface::get_default_config`"]: "struct.Surface.html#method.get_default_config")]
pub desired_maximum_frame_latency: u32,
pub alpha_mode: CompositeAlphaMode,
pub view_formats: V,
}
impl<V: Clone> SurfaceConfiguration<V> {
pub fn map_view_formats<'a, M>(
&'a self,
fun: impl FnOnce(&'a V) -> M,
) -> SurfaceConfiguration<M> {
SurfaceConfiguration {
usage: self.usage,
format: self.format,
color_space: self.color_space,
width: self.width,
height: self.height,
present_mode: self.present_mode,
desired_maximum_frame_latency: self.desired_maximum_frame_latency,
alpha_mode: self.alpha_mode,
view_formats: fun(&self.view_formats),
}
}
}
#[repr(C)]
#[derive(Debug)]
pub enum SurfaceStatus {
Good,
Suboptimal,
Timeout,
Occluded,
Outdated,
Lost,
Validation,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PresentationTimestamp(
pub u128,
);
impl PresentationTimestamp {
pub const INVALID_TIMESTAMP: Self = Self(u128::MAX);
#[must_use]
pub fn is_invalid(self) -> bool {
self == Self::INVALID_TIMESTAMP
}
}