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 = loop {
let mut count = 0;
unsafe {
(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_vk =
vec![ash::vk::DisplayModeProperties2KHR::default(); count as usize];
let result = unsafe {
(fns.khr_get_display_properties2
.get_display_mode_properties2_khr)(
self.physical_device.handle(),
self.handle,
&mut count,
properties_vk.as_mut_ptr(),
)
};
match result {
ash::vk::Result::SUCCESS => {
unsafe { properties_vk.set_len(count as usize) };
break properties_vk;
}
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::from_vk_parameters(
&properties_vk.parameters,
),
)
})
})
.collect())
} else {
let properties_vk = loop {
let mut count = 0;
unsafe {
(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 = unsafe {
(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 => {
unsafe { 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::from_vk_parameters(
&properties_vk.parameters,
),
)
})
})
.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,
}
impl DisplayProperties {
pub(crate) fn to_mut_vk2() -> ash::vk::DisplayProperties2KHR<'static> {
ash::vk::DisplayProperties2KHR::default()
}
pub(crate) fn from_vk(val_vk: &ash::vk::DisplayPropertiesKHR<'_>) -> Self {
let display_name_vk = unsafe { val_vk.display_name_as_c_str() };
let &ash::vk::DisplayPropertiesKHR {
display: _,
display_name: _,
physical_dimensions,
physical_resolution,
supported_transforms,
plane_reorder_possible,
persistent_content,
..
} = val_vk;
DisplayProperties {
name: display_name_vk.map(|name| {
name.to_str()
.expect("non UTF-8 characters in display name")
.to_owned()
}),
physical_dimensions: [physical_dimensions.width, physical_dimensions.height],
physical_resolution: [physical_resolution.width, physical_resolution.height],
supported_transforms: supported_transforms.into(),
plane_reorder_possible: plane_reorder_possible != ash::vk::FALSE,
persistent_content: persistent_content != ash::vk::FALSE,
}
}
}
#[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)?;
Ok(unsafe { 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 physical_device = &display.physical_device;
let create_info_vk = create_info.to_vk();
let handle = {
let fns = physical_device.instance().fns();
let mut output = MaybeUninit::uninit();
unsafe {
(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)?;
unsafe { 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)?;
Ok(unsafe { 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| {
let fns = self.display.physical_device.instance().fns();
let mut capabilities_vk = DisplayPlaneCapabilities::to_mut_vk2();
if self
.instance()
.enabled_extensions()
.khr_get_display_properties2
{
let info_vk = ash::vk::DisplayPlaneInfo2KHR::default()
.mode(self.handle)
.plane_index(plane_index);
unsafe {
(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 {
unsafe {
(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::from_vk2(&capabilities_vk))
})
}
}
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(())
}
pub(crate) fn to_vk(&self) -> ash::vk::DisplayModeCreateInfoKHR<'static> {
let &Self {
visible_region,
refresh_rate,
_ne: _,
} = self;
ash::vk::DisplayModeCreateInfoKHR::default()
.flags(ash::vk::DisplayModeCreateFlagsKHR::empty())
.parameters(ash::vk::DisplayModeParametersKHR {
visible_region: ash::vk::Extent2D {
width: visible_region[0],
height: visible_region[1],
},
refresh_rate,
})
}
pub(crate) fn from_vk_parameters(val_vk: &ash::vk::DisplayModeParametersKHR) -> Self {
let &ash::vk::DisplayModeParametersKHR {
visible_region,
refresh_rate,
} = val_vk;
DisplayModeCreateInfo {
visible_region: [visible_region.width, visible_region.height],
refresh_rate,
_ne: crate::NonExhaustive(()),
}
}
}
#[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,
}
impl DisplayPlanePropertiesRaw {
pub(crate) fn to_mut_vk2() -> ash::vk::DisplayPlaneProperties2KHR<'static> {
ash::vk::DisplayPlaneProperties2KHR::default()
}
pub(crate) fn from_vk(val_vk: &ash::vk::DisplayPlanePropertiesKHR) -> Self {
let &ash::vk::DisplayPlanePropertiesKHR {
current_display,
current_stack_index,
} = val_vk;
DisplayPlanePropertiesRaw {
current_display: Some(current_display).filter(|&x| x != ash::vk::DisplayKHR::null()),
current_stack_index,
}
}
}
#[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],
}
impl DisplayPlaneCapabilities {
pub(crate) fn to_mut_vk2() -> ash::vk::DisplayPlaneCapabilities2KHR<'static> {
ash::vk::DisplayPlaneCapabilities2KHR::default()
}
pub(crate) fn from_vk2(val_vk: &ash::vk::DisplayPlaneCapabilities2KHR<'_>) -> Self {
let &ash::vk::DisplayPlaneCapabilities2KHR {
capabilities:
ash::vk::DisplayPlaneCapabilitiesKHR {
supported_alpha,
min_src_position,
max_src_position,
min_src_extent,
max_src_extent,
min_dst_position,
max_dst_position,
min_dst_extent,
max_dst_extent,
},
..
} = val_vk;
DisplayPlaneCapabilities {
supported_alpha: supported_alpha.into(),
min_src_position: [min_src_position.x as u32, min_src_position.y as u32],
max_src_position: [max_src_position.x as u32, max_src_position.y as u32],
min_src_extent: [min_src_extent.width, min_src_extent.height],
max_src_extent: [max_src_extent.width, max_src_extent.height],
min_dst_position: [min_dst_position.x as u32, min_dst_position.y as u32],
max_dst_position: [max_dst_position.x, max_dst_position.y],
min_dst_extent: [min_dst_extent.width, min_dst_extent.height],
max_dst_extent: [max_dst_extent.width, max_dst_extent.height],
}
}
}
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,
}