use crate::{
cache::{OnceCache, WeakArcOnceCache},
device::physical::PhysicalDevice,
instance::{Instance, InstanceOwned, InstanceOwnedDebugWrapper},
macros::vulkan_bitflags_enum,
swapchain::SurfaceTransforms,
Validated, ValidationError, VulkanError, VulkanObject,
};
use std::{
hash::{Hash, Hasher},
mem::MaybeUninit,
ptr,
sync::Arc,
};
#[derive(Debug)]
pub struct Display {
physical_device: InstanceOwnedDebugWrapper<Arc<PhysicalDevice>>,
handle: ash::vk::DisplayKHR,
name: Option<String>,
physical_dimensions: [u32; 2],
physical_resolution: [u32; 2],
supported_transforms: SurfaceTransforms,
plane_reorder_possible: bool,
persistent_content: bool,
display_modes: WeakArcOnceCache<ash::vk::DisplayModeKHR, DisplayMode>,
}
impl Display {
#[inline]
pub fn from_handle(
physical_device: Arc<PhysicalDevice>,
handle: ash::vk::DisplayKHR,
properties: DisplayProperties,
) -> Arc<Self> {
let DisplayProperties {
name,
physical_dimensions,
physical_resolution,
supported_transforms,
plane_reorder_possible,
persistent_content,
} = properties;
Arc::new(Self {
physical_device: InstanceOwnedDebugWrapper(physical_device),
handle,
name,
physical_dimensions,
physical_resolution,
supported_transforms,
plane_reorder_possible,
persistent_content,
display_modes: WeakArcOnceCache::new(),
})
}
#[inline]
pub fn physical_device(&self) -> &Arc<PhysicalDevice> {
&self.physical_device
}
#[inline]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
#[inline]
pub fn physical_dimensions(&self) -> [u32; 2] {
self.physical_dimensions
}
#[inline]
pub fn physical_resolution(&self) -> [u32; 2] {
self.physical_resolution
}
#[inline]
pub fn supported_transforms(&self) -> SurfaceTransforms {
self.supported_transforms
}
#[inline]
pub fn plane_reorder_possible(&self) -> bool {
self.plane_reorder_possible
}
#[inline]
pub fn persistent_content(&self) -> bool {
self.persistent_content
}
pub fn display_mode_properties(self: &Arc<Self>) -> Result<Vec<Arc<DisplayMode>>, VulkanError> {
let fns = self.physical_device.instance().fns();
if self
.instance()
.enabled_extensions()
.khr_get_display_properties2
{
let properties_vk = unsafe {
loop {
let mut count = 0;
(fns.khr_get_display_properties2
.get_display_mode_properties2_khr)(
self.physical_device.handle(),
self.handle,
&mut count,
ptr::null_mut(),
)
.result()
.map_err(VulkanError::from)?;
let mut properties =
vec![ash::vk::DisplayModeProperties2KHR::default(); count as usize];
let result = (fns
.khr_get_display_properties2
.get_display_mode_properties2_khr)(
self.physical_device.handle(),
self.handle,
&mut count,
properties.as_mut_ptr(),
);
match result {
ash::vk::Result::SUCCESS => {
properties.set_len(count as usize);
break properties;
}
ash::vk::Result::INCOMPLETE => (),
err => return Err(VulkanError::from(err)),
}
}
};
Ok(properties_vk
.into_iter()
.map(|properties_vk| {
let properties_vk = &properties_vk.display_mode_properties;
self.display_modes
.get_or_insert(properties_vk.display_mode, |&handle| {
DisplayMode::from_handle(
self.clone(),
handle,
DisplayModeCreateInfo {
visible_region: [
properties_vk.parameters.visible_region.width,
properties_vk.parameters.visible_region.height,
],
refresh_rate: properties_vk.parameters.refresh_rate,
_ne: crate::NonExhaustive(()),
},
)
})
})
.collect())
} else {
let properties_vk = unsafe {
loop {
let mut count = 0;
(fns.khr_display.get_display_mode_properties_khr)(
self.physical_device.handle(),
self.handle,
&mut count,
ptr::null_mut(),
)
.result()
.map_err(VulkanError::from)?;
let mut properties = Vec::with_capacity(count as usize);
let result = (fns.khr_display.get_display_mode_properties_khr)(
self.physical_device.handle(),
self.handle,
&mut count,
properties.as_mut_ptr(),
);
match result {
ash::vk::Result::SUCCESS => {
properties.set_len(count as usize);
break properties;
}
ash::vk::Result::INCOMPLETE => (),
err => return Err(VulkanError::from(err)),
}
}
};
Ok(properties_vk
.into_iter()
.map(|properties_vk| {
self.display_modes
.get_or_insert(properties_vk.display_mode, |&handle| {
DisplayMode::from_handle(
self.clone(),
handle,
DisplayModeCreateInfo {
visible_region: [
properties_vk.parameters.visible_region.width,
properties_vk.parameters.visible_region.height,
],
refresh_rate: properties_vk.parameters.refresh_rate,
_ne: crate::NonExhaustive(()),
},
)
})
})
.collect())
}
}
}
unsafe impl VulkanObject for Display {
type Handle = ash::vk::DisplayKHR;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl InstanceOwned for Display {
#[inline]
fn instance(&self) -> &Arc<Instance> {
self.physical_device.instance()
}
}
impl PartialEq for Display {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.physical_device == other.physical_device && self.handle == other.handle
}
}
impl Eq for Display {}
impl Hash for Display {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.physical_device.hash(state);
self.handle.hash(state);
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct DisplayProperties {
pub name: Option<String>,
pub physical_dimensions: [u32; 2],
pub physical_resolution: [u32; 2],
pub supported_transforms: SurfaceTransforms,
pub plane_reorder_possible: bool,
pub persistent_content: bool,
}
#[derive(Debug)]
pub struct DisplayMode {
display: InstanceOwnedDebugWrapper<Arc<Display>>,
handle: ash::vk::DisplayModeKHR,
visible_region: [u32; 2],
refresh_rate: u32,
display_plane_capabilities: OnceCache<u32, DisplayPlaneCapabilities>,
}
impl DisplayMode {
#[inline]
pub fn new(
display: Arc<Display>,
create_info: DisplayModeCreateInfo,
) -> Result<Arc<Self>, Validated<VulkanError>> {
Self::validate_new(&display, &create_info)?;
unsafe { Ok(Self::new_unchecked(display, create_info)?) }
}
fn validate_new(
display: &Display,
create_info: &DisplayModeCreateInfo,
) -> Result<(), Box<ValidationError>> {
create_info
.validate(&display.physical_device)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
display: Arc<Display>,
create_info: DisplayModeCreateInfo,
) -> Result<Arc<Self>, VulkanError> {
let &DisplayModeCreateInfo {
visible_region,
refresh_rate,
_ne: _,
} = &create_info;
let physical_device = display.physical_device.clone();
let create_info_vk = ash::vk::DisplayModeCreateInfoKHR {
flags: ash::vk::DisplayModeCreateFlagsKHR::empty(),
parameters: ash::vk::DisplayModeParametersKHR {
visible_region: ash::vk::Extent2D {
width: visible_region[0],
height: visible_region[1],
},
refresh_rate,
},
..Default::default()
};
let handle = {
let fns = physical_device.instance().fns();
let mut output = MaybeUninit::uninit();
(fns.khr_display.create_display_mode_khr)(
physical_device.handle(),
display.handle,
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(display.display_modes.get_or_insert(handle, |&handle| {
Self::from_handle(display.clone(), handle, create_info)
}))
}
#[inline]
pub fn from_handle(
display: Arc<Display>,
handle: ash::vk::DisplayModeKHR,
create_info: DisplayModeCreateInfo,
) -> Arc<Self> {
let DisplayModeCreateInfo {
visible_region,
refresh_rate,
_ne: _,
} = create_info;
Arc::new(Self {
display: InstanceOwnedDebugWrapper(display),
handle,
visible_region,
refresh_rate,
display_plane_capabilities: OnceCache::new(),
})
}
#[inline]
pub fn display(&self) -> &Arc<Display> {
&self.display
}
#[inline]
pub fn visible_region(&self) -> [u32; 2] {
self.visible_region
}
#[inline]
pub fn refresh_rate(&self) -> u32 {
self.refresh_rate
}
#[inline]
pub fn display_plane_capabilities(
&self,
plane_index: u32,
) -> Result<DisplayPlaneCapabilities, Validated<VulkanError>> {
self.validate_display_plane_capabilities(plane_index)?;
unsafe { Ok(self.display_plane_capabilities_unchecked(plane_index)?) }
}
fn validate_display_plane_capabilities(
&self,
plane_index: u32,
) -> Result<(), Box<ValidationError>> {
let display_plane_properties_raw = unsafe {
self.display
.physical_device
.display_plane_properties_raw()
.map_err(|_err| {
Box::new(ValidationError {
problem: "`PhysicalDevice::display_plane_properties` \
returned an error"
.into(),
..Default::default()
})
})?
};
if plane_index as usize >= display_plane_properties_raw.len() {
return Err(Box::new(ValidationError {
problem: "`plane_index` is not less than the number of display planes on the \
physical device"
.into(),
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn display_plane_capabilities_unchecked(
&self,
plane_index: u32,
) -> Result<DisplayPlaneCapabilities, VulkanError> {
self.display_plane_capabilities
.get_or_try_insert(plane_index, |&plane_index| unsafe {
let fns = self.display.physical_device.instance().fns();
let mut capabilities_vk = ash::vk::DisplayPlaneCapabilities2KHR::default();
if self
.instance()
.enabled_extensions()
.khr_get_display_properties2
{
let info_vk = ash::vk::DisplayPlaneInfo2KHR {
mode: self.handle,
plane_index,
..Default::default()
};
(fns.khr_get_display_properties2
.get_display_plane_capabilities2_khr)(
self.display.physical_device.handle(),
&info_vk,
&mut capabilities_vk,
)
.result()
.map_err(VulkanError::from)?;
} else {
(fns.khr_display.get_display_plane_capabilities_khr)(
self.display.physical_device.handle(),
self.handle,
plane_index,
&mut capabilities_vk.capabilities,
)
.result()
.map_err(VulkanError::from)?;
}
Ok(DisplayPlaneCapabilities {
supported_alpha: capabilities_vk.capabilities.supported_alpha.into(),
min_src_position: [
capabilities_vk.capabilities.min_src_position.x as u32,
capabilities_vk.capabilities.min_src_position.y as u32,
],
max_src_position: [
capabilities_vk.capabilities.max_src_position.x as u32,
capabilities_vk.capabilities.max_src_position.y as u32,
],
min_src_extent: [
capabilities_vk.capabilities.min_src_extent.width,
capabilities_vk.capabilities.min_src_extent.height,
],
max_src_extent: [
capabilities_vk.capabilities.max_src_extent.width,
capabilities_vk.capabilities.max_src_extent.height,
],
min_dst_position: [
capabilities_vk.capabilities.min_dst_position.x as u32,
capabilities_vk.capabilities.min_dst_position.y as u32,
],
max_dst_position: [
capabilities_vk.capabilities.max_dst_position.x,
capabilities_vk.capabilities.max_dst_position.y,
],
min_dst_extent: [
capabilities_vk.capabilities.min_dst_extent.width,
capabilities_vk.capabilities.min_dst_extent.height,
],
max_dst_extent: [
capabilities_vk.capabilities.max_dst_extent.width,
capabilities_vk.capabilities.max_dst_extent.height,
],
})
})
}
}
unsafe impl VulkanObject for DisplayMode {
type Handle = ash::vk::DisplayModeKHR;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl InstanceOwned for DisplayMode {
#[inline]
fn instance(&self) -> &Arc<Instance> {
self.display.instance()
}
}
impl PartialEq for DisplayMode {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.display == other.display && self.handle == other.handle
}
}
impl Eq for DisplayMode {}
impl Hash for DisplayMode {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.display.hash(state);
self.handle.hash(state);
}
}
#[derive(Clone, Debug)]
pub struct DisplayModeCreateInfo {
pub visible_region: [u32; 2],
pub refresh_rate: u32,
pub _ne: crate::NonExhaustive,
}
impl Default for DisplayModeCreateInfo {
#[inline]
fn default() -> Self {
Self {
visible_region: [0; 2],
refresh_rate: 0,
_ne: crate::NonExhaustive(()),
}
}
}
impl DisplayModeCreateInfo {
pub(crate) fn validate(
&self,
_physical_device: &PhysicalDevice,
) -> Result<(), Box<ValidationError>> {
let &Self {
visible_region,
refresh_rate,
_ne: _,
} = self;
if visible_region[0] == 0 {
return Err(Box::new(ValidationError {
context: "visible_region[0]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkDisplayModeParametersKHR-width-01990"],
..Default::default()
}));
}
if visible_region[1] == 0 {
return Err(Box::new(ValidationError {
context: "visible_region[1]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkDisplayModeParametersKHR-height-01991"],
..Default::default()
}));
}
if refresh_rate == 0 {
return Err(Box::new(ValidationError {
context: "refresh_rate".into(),
problem: "is zero".into(),
vuids: &["VUID-VkDisplayModeParametersKHR-refreshRate-01992"],
..Default::default()
}));
}
Ok(())
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct DisplayPlaneProperties {
pub current_display: Option<Arc<Display>>,
pub current_stack_index: u32,
}
#[derive(Clone, Debug)]
pub(crate) struct DisplayPlanePropertiesRaw {
pub(crate) current_display: Option<ash::vk::DisplayKHR>,
pub(crate) current_stack_index: u32,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct DisplayPlaneCapabilities {
pub supported_alpha: DisplayPlaneAlphaFlags,
pub min_src_position: [u32; 2],
pub max_src_position: [u32; 2],
pub min_src_extent: [u32; 2],
pub max_src_extent: [u32; 2],
pub min_dst_position: [u32; 2],
pub max_dst_position: [i32; 2],
pub min_dst_extent: [u32; 2],
pub max_dst_extent: [u32; 2],
}
vulkan_bitflags_enum! {
#[non_exhaustive]
DisplayPlaneAlphaFlags,
DisplayPlaneAlpha,
= DisplayPlaneAlphaFlagsKHR(u32);
OPAQUE, Opaque = OPAQUE,
GLOBAL, Global = GLOBAL,
PER_PIXEL, PerPixel = PER_PIXEL,
PER_PIXEL_PREMULTIPLIED, PerPixelPremultiplied = PER_PIXEL_PREMULTIPLIED,
}