pub use self::{acquire_present::*, surface::*};
mod acquire_present;
mod surface;
use crate::{
device::{Device, DeviceOwned},
format::Format,
image::{Image, ImageCreateFlags, ImageFormatInfo, ImageTiling, ImageType, ImageUsage},
instance::InstanceOwnedDebugWrapper,
macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum},
sync::Sharing,
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{
fmt::Debug,
mem::MaybeUninit,
num::NonZeroU64,
ptr,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
},
time::Duration,
};
#[derive(Debug)]
pub struct Swapchain {
handle: ash::vk::SwapchainKHR,
device: InstanceOwnedDebugWrapper<Arc<Device>>,
surface: InstanceOwnedDebugWrapper<Arc<Surface>>,
id: NonZeroU64,
flags: SwapchainCreateFlags,
min_image_count: u32,
image_format: Format,
image_view_formats: Vec<Format>,
image_color_space: ColorSpace,
image_extent: [u32; 2],
image_array_layers: u32,
image_usage: ImageUsage,
image_sharing: Sharing<SmallVec<[u32; 4]>>,
pre_transform: SurfaceTransform,
composite_alpha: CompositeAlpha,
present_mode: PresentMode,
present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>,
clipped: bool,
scaling_behavior: Option<PresentScaling>,
present_gravity: Option<[PresentGravity; 2]>,
full_screen_exclusive: FullScreenExclusive,
win32_monitor: Option<Win32Monitor>,
prev_present_id: AtomicU64,
full_screen_exclusive_held: AtomicBool,
images: Vec<ImageEntry>,
is_retired: Mutex<bool>,
}
#[derive(Debug)]
struct ImageEntry {
handle: ash::vk::Image,
layout_initialized: AtomicBool,
}
impl Swapchain {
#[inline]
pub fn new(
device: Arc<Device>,
surface: Arc<Surface>,
create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Validated<VulkanError>> {
Self::validate_new_inner(&device, &surface, &create_info)?;
Ok(unsafe { Self::new_unchecked(device, surface, create_info) }?)
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn new_unchecked(
device: Arc<Device>,
surface: Arc<Surface>,
create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
let (handle, image_handles) =
unsafe { Self::new_inner_unchecked(&device, &surface, &create_info, None) }?;
unsafe { Self::from_handle(device, handle, image_handles, surface, create_info) }
}
pub fn recreate(
self: &Arc<Self>,
create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Validated<VulkanError>> {
Self::validate_new_inner(&self.device, &self.surface, &create_info)?;
{
let mut is_retired = self.is_retired.lock();
if *is_retired {
return Err(Box::new(ValidationError {
context: "self".into(),
problem: "has already been used to recreate a swapchain".into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933"],
..Default::default()
})
.into());
} else {
*is_retired = true;
}
}
Ok(unsafe { self.recreate_unchecked(create_info) }?)
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn recreate_unchecked(
self: &Arc<Self>,
create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
*self.is_retired.lock() = true;
let (handle, image_handles) = unsafe {
Self::new_inner_unchecked(&self.device, &self.surface, &create_info, Some(self))
}?;
let (swapchain, swapchain_images) = unsafe {
Self::from_handle(
self.device.clone(),
handle,
image_handles,
self.surface.clone(),
create_info,
)
}?;
if self.full_screen_exclusive == FullScreenExclusive::ApplicationControlled {
swapchain.full_screen_exclusive_held.store(
self.full_screen_exclusive_held.load(Ordering::Relaxed),
Ordering::Relaxed,
);
};
Ok((swapchain, swapchain_images))
}
fn validate_new_inner(
device: &Device,
surface: &Surface,
create_info: &SwapchainCreateInfo,
) -> Result<(), Box<ValidationError>> {
if !device.enabled_extensions().khr_swapchain {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"khr_swapchain",
)])]),
..Default::default()
}));
}
create_info
.validate(device)
.map_err(|err| err.add_context("create_info"))?;
let &SwapchainCreateInfo {
flags: _,
min_image_count,
image_format,
image_view_formats: _,
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing: _,
pre_transform,
composite_alpha,
present_mode,
ref present_modes,
clipped: _,
scaling_behavior,
present_gravity,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = create_info;
assert_eq!(device.instance(), surface.instance());
if !device.active_queue_family_indices().iter().any(|&index| {
unsafe {
device
.physical_device()
.surface_support_unchecked(index, surface)
}
.unwrap_or_default()
}) {
return Err(Box::new(ValidationError {
context: "surface".into(),
problem: "is not supported by the physical device".into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-surface-01270"],
..Default::default()
}));
}
let surface_capabilities = unsafe {
device.physical_device().surface_capabilities_unchecked(
surface,
SurfaceInfo {
present_mode: device
.enabled_extensions()
.ext_swapchain_maintenance1
.then_some(present_mode),
full_screen_exclusive,
win32_monitor: win32_monitor
.filter(|_| full_screen_exclusive != FullScreenExclusive::Default),
..Default::default()
},
)
}
.map_err(|_err| {
Box::new(ValidationError {
problem: "`PhysicalDevice::surface_capabilities` \
returned an error"
.into(),
..Default::default()
})
})?;
let surface_formats = unsafe {
device.physical_device().surface_formats_unchecked(
surface,
SurfaceInfo {
present_mode: device
.enabled_extensions()
.ext_swapchain_maintenance1
.then_some(present_mode),
full_screen_exclusive,
win32_monitor: win32_monitor.filter(|_| {
surface.api() == SurfaceApi::Win32
&& full_screen_exclusive == FullScreenExclusive::ApplicationControlled
}),
..Default::default()
},
)
}
.map_err(|_err| {
Box::new(ValidationError {
problem: "`PhysicalDevice::surface_formats` \
returned an error"
.into(),
..Default::default()
})
})?;
let surface_present_modes = unsafe {
device.physical_device().surface_present_modes_unchecked(
surface,
SurfaceInfo {
full_screen_exclusive,
win32_monitor: win32_monitor.filter(|_| {
surface.api() == SurfaceApi::Win32
&& full_screen_exclusive == FullScreenExclusive::ApplicationControlled
}),
..Default::default()
},
)
}
.map_err(|_err| {
Box::new(ValidationError {
problem: "`PhysicalDevice::surface_present_modes` \
returned an error"
.into(),
..Default::default()
})
})?;
if surface_capabilities
.max_image_count
.is_some_and(|c| min_image_count > c)
{
return Err(Box::new(ValidationError {
problem: "`create_info.min_image_count` is greater than the `max_image_count` \
value of the capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-minImageCount-01272"],
..Default::default()
}));
}
if min_image_count < surface_capabilities.min_image_count {
return Err(Box::new(ValidationError {
problem: "`create_info.min_image_count` is less than the `min_image_count` \
value of the capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-02839"],
..Default::default()
}));
}
if !surface_formats
.iter()
.any(|&fc| fc == (image_format, image_color_space))
{
return Err(Box::new(ValidationError {
problem: "the combination of `create_info.image_format` and \
`create_info.image_color_space` is not supported for `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01273"],
..Default::default()
}));
}
if image_array_layers > surface_capabilities.max_image_array_layers {
return Err(Box::new(ValidationError {
problem: "`create_info.image_array_layers` is greater than the \
`max_image_array_layers` value of the capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"],
..Default::default()
}));
}
if matches!(
present_mode,
PresentMode::Immediate
| PresentMode::Mailbox
| PresentMode::Fifo
| PresentMode::FifoRelaxed
) && !surface_capabilities
.supported_usage_flags
.contains(image_usage)
{
return Err(Box::new(ValidationError {
problem: "`create_info.present_mode` is `PresentMode::Immediate`, \
`PresentMode::Mailbox`, `PresentMode::Fifo` or `PresentMode::FifoRelaxed`, \
and `create_info.image_usage` contains flags that are not set in \
the `supported_usage_flags` value of the capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01427"],
..Default::default()
}));
}
if !surface_capabilities
.supported_transforms
.contains_enum(pre_transform)
{
return Err(Box::new(ValidationError {
problem: "`create_info.pre_transform` is not present in the \
`supported_transforms` value of the capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-preTransform-01279"],
..Default::default()
}));
}
if !surface_capabilities
.supported_composite_alpha
.contains_enum(composite_alpha)
{
return Err(Box::new(ValidationError {
problem: "`create_info.composite_alpha` is not present in the \
`supported_composite_alpha` value of the capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280"],
..Default::default()
}));
}
if !surface_present_modes.contains(&present_mode) {
return Err(Box::new(ValidationError {
problem: "`create_info.present_mode` is not supported for `surface`".into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01281"],
..Default::default()
}));
}
if present_modes.is_empty() {
if let Some(scaling_behavior) = scaling_behavior {
if !surface_capabilities
.supported_present_scaling
.contains_enum(scaling_behavior)
{
return Err(Box::new(ValidationError {
problem: "`create_info.scaling_behavior` is not present in the \
`supported_present_scaling` value of the \
capabilities of `surface`"
.into(),
vuids: &[
"VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07770",
],
..Default::default()
}));
}
}
if let Some(present_gravity) = present_gravity {
for (axis_index, (present_gravity, supported_present_gravity)) in present_gravity
.into_iter()
.zip(surface_capabilities.supported_present_gravity)
.enumerate()
{
if !supported_present_gravity.contains_enum(present_gravity) {
return Err(Box::new(ValidationError {
problem: format!(
"`create_info.present_gravity[{0}]` is not present in the \
`supported_present_gravity[{0}]` value of the \
capabilities of `surface`",
axis_index,
)
.into(),
vuids: &[
"VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07772",
"VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07774",
],
..Default::default()
}));
}
}
}
} else {
for (index, &present_mode) in present_modes.iter().enumerate() {
if !surface_present_modes.contains(&present_mode) {
return Err(Box::new(ValidationError {
problem: format!(
"`create_info.present_modes[{}]` is not supported for `surface`",
index,
)
.into(),
vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-None-07762"],
..Default::default()
}));
}
if !surface_capabilities
.compatible_present_modes
.contains(&present_mode)
{
return Err(Box::new(ValidationError {
problem: format!(
"`create_info.present_modes[{}]` is not present in the \
`compatible_present_modes` value of the \
capabilities of `surface`",
index,
)
.into(),
vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-07763"],
..Default::default()
}));
}
if scaling_behavior.is_some() || present_gravity.is_some() {
let surface_capabilities = unsafe {
device.physical_device().surface_capabilities_unchecked(
surface,
SurfaceInfo {
present_mode: Some(present_mode),
full_screen_exclusive,
win32_monitor,
..Default::default()
},
)
}
.map_err(|_err| {
Box::new(ValidationError {
problem: "`PhysicalDevice::surface_capabilities` \
returned an error"
.into(),
..Default::default()
})
})?;
if let Some(scaling_behavior) = scaling_behavior {
if !surface_capabilities
.supported_present_scaling
.contains_enum(scaling_behavior)
{
return Err(Box::new(ValidationError {
problem: format!(
"`create_info.scaling_behavior` is not present in the \
`supported_present_scaling` value of the \
capabilities of `surface` for \
`create_info.present_modes[{}]`",
index,
)
.into(),
vuids: &[
"VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07771",
],
..Default::default()
}));
}
}
if let Some(present_gravity) = present_gravity {
for (axis_index, (present_gravity, supported_present_gravity)) in
present_gravity
.into_iter()
.zip(surface_capabilities.supported_present_gravity)
.enumerate()
{
if !supported_present_gravity.contains_enum(present_gravity) {
return Err(Box::new(ValidationError {
problem: format!(
"`create_info.present_gravity[{0}]` is not present in the \
`supported_present_gravity[{0}]` value of the \
capabilities of `surface` for \
`create_info.present_modes[{1}]`",
axis_index, index,
)
.into(),
vuids: &[
"VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07773",
"VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07775",
],
..Default::default()
}));
}
}
}
}
}
}
if scaling_behavior.is_some() {
if let Some(min_scaled_image_extent) = surface_capabilities.min_scaled_image_extent {
if image_extent[0] < min_scaled_image_extent[0]
|| image_extent[1] < min_scaled_image_extent[1]
{
return Err(Box::new(ValidationError {
problem: "`scaling_behavior` is `Some`, and an element of \
`create_info.image_extent` is less than the corresponding element \
of the `min_scaled_image_extent` value of the \
capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"],
..Default::default()
}));
}
}
if let Some(max_scaled_image_extent) = surface_capabilities.max_scaled_image_extent {
if image_extent[0] > max_scaled_image_extent[0]
|| image_extent[1] > max_scaled_image_extent[1]
{
return Err(Box::new(ValidationError {
problem: "`scaling_behavior` is `Some`, and an element of \
`create_info.image_extent` is greater than the corresponding element \
of the `max_scaled_image_extent` value of the \
capabilities of `surface`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"],
..Default::default()
}));
}
}
} else {
}
Ok(())
}
unsafe fn new_inner_unchecked(
device: &Device,
surface: &Surface,
create_info: &SwapchainCreateInfo,
old_swapchain: Option<&Swapchain>,
) -> Result<(ash::vk::SwapchainKHR, Vec<ash::vk::Image>), VulkanError> {
let create_info_fields1_vk = create_info.to_vk_fields1();
let mut create_info_extensions_vk = create_info.to_vk_extensions(&create_info_fields1_vk);
let create_info_vk = create_info.to_vk(
surface.handle(),
old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle),
&mut create_info_extensions_vk,
);
let fns = device.fns();
let handle = {
let mut output = MaybeUninit::uninit();
unsafe {
(fns.khr_swapchain.create_swapchain_khr)(
device.handle(),
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
}
.result()
.map_err(VulkanError::from)?;
unsafe { output.assume_init() }
};
let image_handles = loop {
let mut count = 0;
unsafe {
(fns.khr_swapchain.get_swapchain_images_khr)(
device.handle(),
handle,
&mut count,
ptr::null_mut(),
)
}
.result()
.map_err(VulkanError::from)?;
let mut images = Vec::with_capacity(count as usize);
let result = unsafe {
(fns.khr_swapchain.get_swapchain_images_khr)(
device.handle(),
handle,
&mut count,
images.as_mut_ptr(),
)
};
match result {
ash::vk::Result::SUCCESS => {
unsafe { images.set_len(count as usize) };
break images;
}
ash::vk::Result::INCOMPLETE => (),
err => return Err(VulkanError::from(err)),
}
};
Ok((handle, image_handles))
}
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::SwapchainKHR,
image_handles: impl IntoIterator<Item = ash::vk::Image>,
surface: Arc<Surface>,
create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
let SwapchainCreateInfo {
flags,
min_image_count,
image_format,
image_view_formats,
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
present_modes,
clipped,
scaling_behavior,
present_gravity,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = create_info;
let swapchain = Arc::new(Swapchain {
handle,
device: InstanceOwnedDebugWrapper(device),
surface: InstanceOwnedDebugWrapper(surface),
id: Self::next_id(),
flags,
min_image_count,
image_format,
image_view_formats,
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
present_modes,
clipped,
scaling_behavior,
present_gravity,
full_screen_exclusive,
win32_monitor,
prev_present_id: Default::default(),
full_screen_exclusive_held: AtomicBool::new(false),
images: image_handles
.into_iter()
.map(|handle| ImageEntry {
handle,
layout_initialized: AtomicBool::new(false),
})
.collect(),
is_retired: Mutex::new(false),
});
let swapchain_images = swapchain
.images
.iter()
.enumerate()
.map(|(image_index, entry)| {
Ok(Arc::new(unsafe {
Image::from_swapchain(entry.handle, swapchain.clone(), image_index as u32)
}?))
})
.collect::<Result<_, VulkanError>>()?;
Ok((swapchain, swapchain_images))
}
#[inline]
pub fn create_info(&self) -> SwapchainCreateInfo {
SwapchainCreateInfo {
flags: self.flags,
min_image_count: self.min_image_count,
image_format: self.image_format,
image_view_formats: self.image_view_formats.clone(),
image_color_space: self.image_color_space,
image_extent: self.image_extent,
image_array_layers: self.image_array_layers,
image_usage: self.image_usage,
image_sharing: self.image_sharing.clone(),
pre_transform: self.pre_transform,
composite_alpha: self.composite_alpha,
present_mode: self.present_mode,
present_modes: self.present_modes.clone(),
clipped: self.clipped,
scaling_behavior: self.scaling_behavior,
present_gravity: self.present_gravity,
full_screen_exclusive: self.full_screen_exclusive,
win32_monitor: self.win32_monitor,
_ne: crate::NonExhaustive(()),
}
}
#[inline]
pub fn surface(&self) -> &Arc<Surface> {
&self.surface
}
#[inline]
pub fn flags(&self) -> SwapchainCreateFlags {
self.flags
}
#[inline]
pub fn index_of_image(&self, image: &Image) -> Option<u32> {
self.images
.iter()
.position(|entry| entry.handle == image.handle())
.map(|i| i as u32)
}
#[inline]
pub fn image_count(&self) -> u32 {
self.images.len() as u32
}
#[inline]
pub fn image_format(&self) -> Format {
self.image_format
}
#[inline]
pub fn image_view_formats(&self) -> &[Format] {
&self.image_view_formats
}
#[inline]
pub fn image_color_space(&self) -> ColorSpace {
self.image_color_space
}
#[inline]
pub fn image_extent(&self) -> [u32; 2] {
self.image_extent
}
#[inline]
pub fn image_array_layers(&self) -> u32 {
self.image_array_layers
}
#[inline]
pub fn image_usage(&self) -> ImageUsage {
self.image_usage
}
#[inline]
pub fn image_sharing(&self) -> &Sharing<SmallVec<[u32; 4]>> {
&self.image_sharing
}
#[inline]
pub fn pre_transform(&self) -> SurfaceTransform {
self.pre_transform
}
#[inline]
pub fn composite_alpha(&self) -> CompositeAlpha {
self.composite_alpha
}
#[inline]
pub fn present_mode(&self) -> PresentMode {
self.present_mode
}
#[inline]
pub fn present_modes(&self) -> &[PresentMode] {
&self.present_modes
}
#[inline]
pub fn clipped(&self) -> bool {
self.clipped
}
#[inline]
pub fn scaling_behavior(&self) -> Option<PresentScaling> {
self.scaling_behavior
}
#[inline]
pub fn present_gravity(&self) -> Option<[PresentGravity; 2]> {
self.present_gravity
}
#[inline]
pub fn full_screen_exclusive(&self) -> FullScreenExclusive {
self.full_screen_exclusive
}
#[inline]
pub unsafe fn acquire_next_image(
&self,
acquire_info: &AcquireNextImageInfo,
) -> Result<AcquiredImage, Validated<VulkanError>> {
let is_retired_lock = self.is_retired.lock();
self.validate_acquire_next_image(acquire_info, *is_retired_lock)?;
Ok(unsafe { self.acquire_next_image_unchecked(acquire_info) }?)
}
fn validate_acquire_next_image(
&self,
acquire_info: &AcquireNextImageInfo,
is_retired: bool,
) -> Result<(), Box<ValidationError>> {
acquire_info
.validate(&self.device)
.map_err(|err| err.add_context("acquire_info"))?;
if is_retired {
return Err(Box::new(ValidationError {
problem: "this swapchain is retired".into(),
vuids: &["VUID-VkAcquireNextImageInfoKHR-swapchain-01675"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn acquire_next_image_unchecked(
&self,
acquire_info: &AcquireNextImageInfo,
) -> Result<AcquiredImage, VulkanError> {
let acquire_info_vk = acquire_info.to_vk(self.handle(), self.device.device_mask());
let (image_index, is_suboptimal) = {
let fns = self.device.fns();
let mut output = MaybeUninit::uninit();
let result = if self.device.api_version() >= Version::V1_1
|| self.device.enabled_extensions().khr_device_group
{
unsafe {
(fns.khr_swapchain.acquire_next_image2_khr)(
self.device.handle(),
&acquire_info_vk,
output.as_mut_ptr(),
)
}
} else {
debug_assert!(acquire_info_vk.p_next.is_null());
unsafe {
(fns.khr_swapchain.acquire_next_image_khr)(
self.device.handle(),
acquire_info_vk.swapchain,
acquire_info_vk.timeout,
acquire_info_vk.semaphore,
acquire_info_vk.fence,
output.as_mut_ptr(),
)
}
};
match result {
ash::vk::Result::SUCCESS => (unsafe { output.assume_init() }, false),
ash::vk::Result::SUBOPTIMAL_KHR => (unsafe { output.assume_init() }, true),
ash::vk::Result::NOT_READY => return Err(VulkanError::NotReady),
ash::vk::Result::TIMEOUT => return Err(VulkanError::Timeout),
err => {
let err = VulkanError::from(err);
if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
self.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
return Err(err);
}
}
};
Ok(AcquiredImage {
image_index,
is_suboptimal,
})
}
#[inline]
pub fn wait_for_present(
&self,
present_id: NonZeroU64,
timeout: Option<Duration>,
) -> Result<bool, Validated<VulkanError>> {
let is_retired_lock = self.is_retired.lock();
self.validate_wait_for_present(present_id, timeout, *is_retired_lock)?;
Ok(unsafe { self.wait_for_present_unchecked(present_id, timeout) }?)
}
fn validate_wait_for_present(
&self,
_present_id: NonZeroU64,
timeout: Option<Duration>,
is_retired: bool,
) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_features().present_wait {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
"present_wait",
)])]),
vuids: &["VUID-vkWaitForPresentKHR-presentWait-06234"],
..Default::default()
}));
}
if is_retired {
return Err(Box::new(ValidationError {
problem: "this swapchain is retired".into(),
vuids: &["VUID-vkWaitForPresentKHR-swapchain-04997"],
..Default::default()
}));
}
if let Some(timeout) = timeout {
if timeout.as_nanos() >= u64::MAX as u128 {
return Err(Box::new(ValidationError {
context: "timeout".into(),
problem: "is not less than `u64::MAX` nanoseconds".into(),
..Default::default()
}));
}
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn wait_for_present_unchecked(
&self,
present_id: NonZeroU64,
timeout: Option<Duration>,
) -> Result<bool, VulkanError> {
let result = {
let fns = self.device.fns();
unsafe {
(fns.khr_present_wait.wait_for_present_khr)(
self.device.handle(),
self.handle,
present_id.get(),
timeout.map_or(u64::MAX, |duration| {
u64::try_from(duration.as_nanos()).unwrap()
}),
)
}
};
match result {
ash::vk::Result::SUCCESS => Ok(false),
ash::vk::Result::SUBOPTIMAL_KHR => Ok(true),
ash::vk::Result::TIMEOUT => Err(VulkanError::Timeout),
err => {
let err = VulkanError::from(err);
if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
self.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
Err(err)
}
}
}
#[inline]
pub fn acquire_full_screen_exclusive_mode(&self) -> Result<(), Validated<VulkanError>> {
self.validate_acquire_full_screen_exclusive_mode()?;
Ok(unsafe { self.acquire_full_screen_exclusive_mode_unchecked() }?)
}
fn validate_acquire_full_screen_exclusive_mode(&self) -> Result<(), Box<ValidationError>> {
if *self.is_retired.lock() {
return Err(Box::new(ValidationError {
problem: "this swapchain is retired".into(),
vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02674"],
..Default::default()
}));
}
if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
return Err(Box::new(ValidationError {
context: "self.full_screen_exclusive()".into(),
problem: "is not `FullScreenExclusive::ApplicationControlled`".into(),
vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02675"],
..Default::default()
}));
}
if self
.full_screen_exclusive_held
.swap(true, Ordering::Acquire)
{
return Err(Box::new(ValidationError {
problem: "this swapchain already holds full-screen exclusive access".into(),
vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02676"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn acquire_full_screen_exclusive_mode_unchecked(&self) -> Result<(), VulkanError> {
self.full_screen_exclusive_held
.store(true, Ordering::Relaxed);
let fns = self.device.fns();
unsafe {
(fns.ext_full_screen_exclusive
.acquire_full_screen_exclusive_mode_ext)(
self.device.handle(), self.handle
)
}
.result()
.map_err(VulkanError::from)?;
Ok(())
}
#[inline]
pub fn release_full_screen_exclusive_mode(&self) -> Result<(), Validated<VulkanError>> {
self.validate_release_full_screen_exclusive_mode()?;
Ok(unsafe { self.release_full_screen_exclusive_mode_unchecked() }?)
}
fn validate_release_full_screen_exclusive_mode(&self) -> Result<(), Box<ValidationError>> {
if *self.is_retired.lock() {
return Err(Box::new(ValidationError {
problem: "this swapchain is retired".into(),
vuids: &["VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02677"],
..Default::default()
}));
}
if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
return Err(Box::new(ValidationError {
context: "self.full_screen_exclusive()".into(),
problem: "is not `FullScreenExclusive::ApplicationControlled`".into(),
vuids: &["VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02678"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn release_full_screen_exclusive_mode_unchecked(&self) -> Result<(), VulkanError> {
self.full_screen_exclusive_held
.store(false, Ordering::Release);
let fns = self.device.fns();
unsafe {
(fns.ext_full_screen_exclusive
.release_full_screen_exclusive_mode_ext)(
self.device.handle(), self.handle
)
}
.result()
.map_err(VulkanError::from)?;
Ok(())
}
#[inline]
pub fn is_full_screen_exclusive(&self) -> bool {
if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
false
} else {
self.full_screen_exclusive_held.load(Ordering::SeqCst)
}
}
pub(crate) fn image_layout_initialized(&self, image_index: u32) {
let image_entry = self.images.get(image_index as usize);
if let Some(image_entry) = image_entry {
image_entry
.layout_initialized
.store(true, Ordering::Relaxed);
}
}
pub(crate) fn is_image_layout_initialized(&self, image_index: u32) -> bool {
let image_entry = self.images.get(image_index as usize);
if let Some(image_entry) = image_entry {
image_entry.layout_initialized.load(Ordering::Relaxed)
} else {
false
}
}
#[inline]
pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool {
&self.full_screen_exclusive_held
}
#[inline]
pub(crate) unsafe fn try_claim_present_id(&self, present_id: NonZeroU64) -> bool {
let present_id = u64::from(present_id);
self.prev_present_id.fetch_max(present_id, Ordering::SeqCst) < present_id
}
}
impl Drop for Swapchain {
#[inline]
fn drop(&mut self) {
let fns = self.device.fns();
unsafe {
(fns.khr_swapchain.destroy_swapchain_khr)(
self.device.handle(),
self.handle,
ptr::null(),
)
};
}
}
unsafe impl VulkanObject for Swapchain {
type Handle = ash::vk::SwapchainKHR;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for Swapchain {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl_id_counter!(Swapchain);
#[derive(Clone, Debug)]
pub struct SwapchainCreateInfo {
pub flags: SwapchainCreateFlags,
pub min_image_count: u32,
pub image_format: Format,
pub image_view_formats: Vec<Format>,
pub image_color_space: ColorSpace,
pub image_extent: [u32; 2],
pub image_array_layers: u32,
pub image_usage: ImageUsage,
pub image_sharing: Sharing<SmallVec<[u32; 4]>>,
pub pre_transform: SurfaceTransform,
pub composite_alpha: CompositeAlpha,
pub present_mode: PresentMode,
pub present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>,
pub clipped: bool,
pub scaling_behavior: Option<PresentScaling>,
pub present_gravity: Option<[PresentGravity; 2]>,
pub full_screen_exclusive: FullScreenExclusive,
pub win32_monitor: Option<Win32Monitor>,
pub _ne: crate::NonExhaustive,
}
impl Default for SwapchainCreateInfo {
#[inline]
fn default() -> Self {
Self {
flags: SwapchainCreateFlags::empty(),
min_image_count: 2,
image_format: Format::UNDEFINED,
image_view_formats: Vec::new(),
image_color_space: ColorSpace::SrgbNonLinear,
image_extent: [0, 0],
image_array_layers: 1,
image_usage: ImageUsage::empty(),
image_sharing: Sharing::Exclusive,
pre_transform: SurfaceTransform::Identity,
composite_alpha: CompositeAlpha::Opaque,
present_mode: PresentMode::Fifo,
present_modes: SmallVec::new(),
clipped: true,
scaling_behavior: None,
present_gravity: None,
full_screen_exclusive: FullScreenExclusive::Default,
win32_monitor: None,
_ne: crate::NonExhaustive(()),
}
}
}
impl SwapchainCreateInfo {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
flags,
min_image_count: _,
image_format,
ref image_view_formats,
image_color_space,
image_extent,
image_array_layers,
image_usage,
ref image_sharing,
pre_transform,
composite_alpha,
present_mode,
ref present_modes,
clipped: _,
scaling_behavior,
present_gravity,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = self;
flags.validate_device(device).map_err(|err| {
err.add_context("flags")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-flags-parameter"])
})?;
image_format.validate_device(device).map_err(|err| {
err.add_context("image_format")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter"])
})?;
image_color_space.validate_device(device).map_err(|err| {
err.add_context("image_color_space")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter"])
})?;
image_usage.validate_device(device).map_err(|err| {
err.add_context("image_usage")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter"])
})?;
if image_usage.is_empty() {
return Err(Box::new(ValidationError {
context: "image_usage".into(),
problem: "is empty".into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask"],
..Default::default()
}));
}
pre_transform.validate_device(device).map_err(|err| {
err.add_context("pre_transform")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-preTransform-parameter"])
})?;
composite_alpha.validate_device(device).map_err(|err| {
err.add_context("composite_alpha")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter"])
})?;
present_mode.validate_device(device).map_err(|err| {
err.add_context("present_mode")
.set_vuids(&["VUID-VkSwapchainCreateInfoKHR-presentMode-parameter"])
})?;
if image_extent.contains(&0) {
return Err(Box::new(ValidationError {
context: "image_extent".into(),
problem: "one or more elements are zero".into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageExtent-01689"],
..Default::default()
}));
}
if image_array_layers == 0 {
return Err(Box::new(ValidationError {
context: "image_array_layers".into(),
problem: "is zero".into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"],
..Default::default()
}));
}
match image_sharing {
Sharing::Exclusive => (),
Sharing::Concurrent(queue_family_indices) => {
if queue_family_indices.len() < 2 {
return Err(Box::new(ValidationError {
context: "image_sharing".into(),
problem: "is `Sharing::Concurrent`, and contains less than 2 \
queue family indices"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278"],
..Default::default()
}));
}
let queue_family_count =
device.physical_device().queue_family_properties().len() as u32;
for (index, &queue_family_index) in queue_family_indices.iter().enumerate() {
if queue_family_indices[..index].contains(&queue_family_index) {
return Err(Box::new(ValidationError {
context: "queue_family_indices".into(),
problem: format!(
"the queue family index in the list at index {} is contained in \
the list more than once",
index,
)
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"],
..Default::default()
}));
}
if queue_family_index >= queue_family_count {
return Err(Box::new(ValidationError {
context: format!("queue_family_indices[{}]", index).into(),
problem: "is not less than the number of queue families in the \
physical device"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"],
..Default::default()
}));
}
}
}
};
let image_format_properties = unsafe {
device
.physical_device()
.image_format_properties_unchecked(ImageFormatInfo {
format: image_format,
image_type: ImageType::Dim2d,
tiling: ImageTiling::Optimal,
usage: image_usage,
..Default::default()
})
}
.map_err(|_err| {
Box::new(ValidationError {
problem: "`PhysicalDevice::image_format_properties` \
returned an error"
.into(),
..Default::default()
})
})?;
if image_format_properties.is_none() {
return Err(Box::new(ValidationError {
problem: "the combination of `image_format` and `image_usage` is not supported \
for images by the physical device"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01778"],
..Default::default()
}));
}
if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT)
&& !image_view_formats.contains(&image_format)
{
return Err(Box::new(ValidationError {
problem: "`flags` contains `SwapchainCreateFlags::MUTABLE_FORMAT`, but \
`image_view_formats` does not contain `image_format`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-03168"],
..Default::default()
}));
}
if !image_view_formats.is_empty() {
if !(device.api_version() >= Version::V1_2
|| device.enabled_extensions().khr_image_format_list)
{
return Err(Box::new(ValidationError {
context: "image_view_formats".into(),
problem: "is not empty".into(),
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]),
]),
..Default::default()
}));
}
if !flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT)
&& image_view_formats.len() != 1
{
return Err(Box::new(ValidationError {
problem: "`flags` does not contain `SwapchainCreateFlags::MUTABLE_FORMAT`, \
but `image_view_formats` contains more than one element"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-04100"],
..Default::default()
}));
}
for (index, &image_view_format) in image_view_formats.iter().enumerate() {
image_view_format.validate_device(device).map_err(|err| {
err.add_context(format!("image_view_formats[{}]", index))
.set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"])
})?;
if image_view_format.compatibility() != image_format.compatibility() {
return Err(Box::new(ValidationError {
problem: format!(
"`image_view_formats[{}]` is not compatible with `image_format`",
index
)
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-04099"],
..Default::default()
}));
}
}
}
if !present_modes.is_empty() {
if !device.enabled_extensions().ext_swapchain_maintenance1 {
return Err(Box::new(ValidationError {
context: "present_modes".into(),
problem: "is not empty".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_swapchain_maintenance1",
)])]),
..Default::default()
}));
}
for (index, &present_mode) in present_modes.iter().enumerate() {
present_mode.validate_device(device).map_err(|err| {
err.add_context(format!("present_modes[{}]", index))
.set_vuids(&[
"VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-parameter",
])
})?;
}
if !present_modes.contains(&present_mode) {
return Err(Box::new(ValidationError {
problem: "`present_modes` is not empty, but does not contain `present_mode`"
.into(),
vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-presentMode-07764"],
..Default::default()
}));
}
}
if let Some(scaling_behavior) = scaling_behavior {
if !device.enabled_extensions().ext_swapchain_maintenance1 {
return Err(Box::new(ValidationError {
context: "scaling_behavior".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_swapchain_maintenance1",
)])]),
..Default::default()
}));
}
scaling_behavior.validate_device(device).map_err(|err| {
err.add_context("scaling_behavior").set_vuids(&[
"VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-parameter",
])
})?;
}
if let Some(present_gravity) = present_gravity {
if !device.enabled_extensions().ext_swapchain_maintenance1 {
return Err(Box::new(ValidationError {
context: "present_gravity".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_swapchain_maintenance1",
)])]),
..Default::default()
}));
}
for (axis_index, present_gravity) in present_gravity.into_iter().enumerate() {
present_gravity.validate_device(device).map_err(|err| {
err.add_context(format!("present_gravity[{}]", axis_index))
.set_vuids(&[
"VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-parameter",
"VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-parameter",
])
})?;
}
}
if full_screen_exclusive != FullScreenExclusive::Default {
if !device.enabled_extensions().ext_full_screen_exclusive {
return Err(Box::new(ValidationError {
context: "full_screen_exclusive".into(),
problem: "is not `FullScreenExclusive::Default`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_full_screen_exclusive",
)])]),
..Default::default()
}));
}
full_screen_exclusive
.validate_device(device)
.map_err(|err| {
err.add_context("full_screen_exclusive").set_vuids(&[
"VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter",
])
})?;
if win32_monitor.is_none() {
return Err(Box::new(ValidationError {
problem: "`full_screen_exclusive` is not `FullScreenExclusive::Default`, but \
`win32_monitor` is `None`"
.into(),
vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-02679"],
..Default::default()
}));
}
} else if win32_monitor.is_some() {
return Err(Box::new(ValidationError {
problem: "`full_screen_exclusive` is `FullScreenExclusive::Default`, but \
`win32_monitor` is `Some`"
.into(),
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk<'a>(
&'a self,
surface_vk: ash::vk::SurfaceKHR,
old_swapchain_vk: ash::vk::SwapchainKHR,
extensions_vk: &'a mut SwapchainCreateInfoExtensionsVk<'_>,
) -> ash::vk::SwapchainCreateInfoKHR<'a> {
let &Self {
flags,
min_image_count,
image_format,
image_view_formats: _,
image_color_space,
image_extent,
image_array_layers,
image_usage,
ref image_sharing,
pre_transform,
composite_alpha,
present_mode,
present_modes: _,
clipped,
scaling_behavior: _,
present_gravity: _,
full_screen_exclusive: _,
win32_monitor: _,
_ne: _,
} = self;
let (image_sharing_mode_vk, queue_family_indices_vk) = match image_sharing {
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, [].as_slice()),
Sharing::Concurrent(ids) => (ash::vk::SharingMode::CONCURRENT, ids.as_slice()),
};
let mut val_vk = ash::vk::SwapchainCreateInfoKHR::default()
.flags(flags.into())
.surface(surface_vk)
.min_image_count(min_image_count)
.image_format(image_format.into())
.image_color_space(image_color_space.into())
.image_extent(ash::vk::Extent2D {
width: image_extent[0],
height: image_extent[1],
})
.image_array_layers(image_array_layers)
.image_usage(image_usage.into())
.image_sharing_mode(image_sharing_mode_vk)
.queue_family_indices(queue_family_indices_vk)
.pre_transform(pre_transform.into())
.composite_alpha(composite_alpha.into())
.present_mode(present_mode.into())
.clipped(clipped)
.old_swapchain(old_swapchain_vk);
let SwapchainCreateInfoExtensionsVk {
full_screen_exclusive_vk,
full_screen_exclusive_win32_vk,
image_format_list_vk,
present_modes_vk,
present_scaling_vk,
} = extensions_vk;
if let Some(next) = full_screen_exclusive_vk {
val_vk = val_vk.push_next(next);
}
if let Some(next) = full_screen_exclusive_win32_vk {
val_vk = val_vk.push_next(next);
}
if let Some(next) = image_format_list_vk {
val_vk = val_vk.push_next(next);
}
if let Some(next) = present_modes_vk {
val_vk = val_vk.push_next(next);
}
if let Some(next) = present_scaling_vk {
val_vk = val_vk.push_next(next);
}
val_vk
}
pub(crate) fn to_vk_extensions<'a>(
&self,
fields1_vk: &'a SwapchainCreateInfoFields1Vk,
) -> SwapchainCreateInfoExtensionsVk<'a> {
let &Self {
flags: _,
min_image_count: _,
image_format: _,
image_view_formats: _,
image_color_space: _,
image_extent: _,
image_array_layers: _,
image_usage: _,
image_sharing: _,
pre_transform: _,
composite_alpha: _,
present_mode: _,
present_modes: _,
clipped: _,
scaling_behavior,
present_gravity,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = self;
let SwapchainCreateInfoFields1Vk {
present_modes_vk,
view_formats_vk,
} = fields1_vk;
let full_screen_exclusive_vk = (full_screen_exclusive != FullScreenExclusive::Default)
.then(|| {
ash::vk::SurfaceFullScreenExclusiveInfoEXT::default()
.full_screen_exclusive(full_screen_exclusive.into())
});
let full_screen_exclusive_win32_vk = win32_monitor.map(|win32_monitor| {
ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT::default().hmonitor(win32_monitor.0)
});
let image_format_list_vk = (!view_formats_vk.is_empty())
.then(|| ash::vk::ImageFormatListCreateInfo::default().view_formats(view_formats_vk));
let present_modes_vk = (!present_modes_vk.is_empty()).then(|| {
ash::vk::SwapchainPresentModesCreateInfoEXT::default().present_modes(present_modes_vk)
});
let present_scaling_vk =
(scaling_behavior.is_some() || present_gravity.is_some()).then(|| {
let [present_gravity_x, present_gravity_y] =
present_gravity.map_or_else(Default::default, |pg| pg.map(Into::into));
ash::vk::SwapchainPresentScalingCreateInfoEXT::default()
.scaling_behavior(scaling_behavior.map_or_else(Default::default, Into::into))
.present_gravity_x(present_gravity_x)
.present_gravity_y(present_gravity_y)
});
SwapchainCreateInfoExtensionsVk {
full_screen_exclusive_vk,
full_screen_exclusive_win32_vk,
image_format_list_vk,
present_modes_vk,
present_scaling_vk,
}
}
pub(crate) fn to_vk_fields1(&self) -> SwapchainCreateInfoFields1Vk {
let present_modes_vk = self.present_modes.iter().copied().map(Into::into).collect();
let view_formats_vk = self
.image_view_formats
.iter()
.copied()
.map(ash::vk::Format::from)
.collect();
SwapchainCreateInfoFields1Vk {
present_modes_vk,
view_formats_vk,
}
}
}
pub(crate) struct SwapchainCreateInfoExtensionsVk<'a> {
pub(crate) full_screen_exclusive_vk:
Option<ash::vk::SurfaceFullScreenExclusiveInfoEXT<'static>>,
pub(crate) full_screen_exclusive_win32_vk:
Option<ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT<'static>>,
pub(crate) image_format_list_vk: Option<ash::vk::ImageFormatListCreateInfo<'a>>,
pub(crate) present_modes_vk: Option<ash::vk::SwapchainPresentModesCreateInfoEXT<'a>>,
pub(crate) present_scaling_vk: Option<ash::vk::SwapchainPresentScalingCreateInfoEXT<'static>>,
}
pub(crate) struct SwapchainCreateInfoFields1Vk {
pub(crate) present_modes_vk: SmallVec<[ash::vk::PresentModeKHR; PresentMode::COUNT]>,
pub(crate) view_formats_vk: Vec<ash::vk::Format>,
}
vulkan_bitflags! {
#[non_exhaustive]
SwapchainCreateFlags = SwapchainCreateFlagsKHR(u32);
MUTABLE_FORMAT = MUTABLE_FORMAT
RequiresOneOf([
RequiresAllOf([DeviceExtension(khr_swapchain_mutable_format)]),
]),
}
impl From<SwapchainCreateFlags> for ImageCreateFlags {
#[inline]
fn from(flags: SwapchainCreateFlags) -> Self {
let mut result = ImageCreateFlags::empty();
if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT) {
result |= ImageCreateFlags::MUTABLE_FORMAT | ImageCreateFlags::EXTENDED_USAGE;
}
result
}
}
vulkan_bitflags_enum! {
#[non_exhaustive]
PresentScalingFlags,
PresentScaling,
= PresentScalingFlagsEXT(u32);
ONE_TO_ONE, OneToOne = ONE_TO_ONE,
ASPECT_RATIO_STRETCH, AspectRatioStretch = ASPECT_RATIO_STRETCH,
STRETCH, Stretch = STRETCH,
}
vulkan_bitflags_enum! {
#[non_exhaustive]
PresentGravityFlags,
PresentGravity,
= PresentGravityFlagsEXT(u32);
MIN, Min = MIN,
MAX, Max = MAX,
CENTERED, Centered = CENTERED,
}
vulkan_enum! {
#[non_exhaustive]
FullScreenExclusive = FullScreenExclusiveEXT(i32);
Default = DEFAULT,
Allowed = ALLOWED,
Disallowed = DISALLOWED,
ApplicationControlled = APPLICATION_CONTROLLED,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR);
impl Win32Monitor {
pub unsafe fn new(hmonitor: ash::vk::HMONITOR) -> Self {
Self(hmonitor)
}
}
unsafe impl Send for Win32Monitor {}
unsafe impl Sync for Win32Monitor {}