use super::{
ColorSpace, CompositeAlpha, PresentMode, SupportedCompositeAlpha, SupportedSurfaceTransforms,
Surface, SurfaceTransform, SwapchainPresentInfo,
};
use crate::{
buffer::sys::Buffer,
device::{Device, DeviceOwned, Queue},
format::Format,
image::{
sys::Image, ImageFormatInfo, ImageLayout, ImageTiling, ImageType, ImageUsage,
SwapchainImage,
},
macros::vulkan_enum,
swapchain::{PresentInfo, SurfaceApi, SurfaceInfo, SurfaceSwapchainLock},
sync::{
AccessCheckError, AccessError, AccessFlags, Fence, FenceError, FlushError, GpuFuture,
PipelineStages, Semaphore, SemaphoreError, Sharing, SubmitAnyBuilder,
},
DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
};
use parking_lot::Mutex;
use smallvec::{smallvec, SmallVec};
use std::{
error::Error,
fmt::{Debug, Display, Error as FmtError, Formatter},
mem::MaybeUninit,
num::NonZeroU64,
ops::Range,
ptr,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
},
time::Duration,
};
pub struct Swapchain {
handle: ash::vk::SwapchainKHR,
device: Arc<Device>,
surface: Arc<Surface>,
id: NonZeroU64,
min_image_count: u32,
image_format: 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,
clipped: bool,
full_screen_exclusive: FullScreenExclusive,
win32_monitor: Option<Win32Monitor>,
prev_present_id: AtomicU64,
full_screen_exclusive_held: AtomicBool,
images: Vec<ImageEntry>,
retired: Mutex<bool>,
}
#[derive(Debug)]
struct ImageEntry {
handle: ash::vk::Image,
layout_initialized: AtomicBool,
}
impl Swapchain {
pub fn new(
device: Arc<Device>,
surface: Arc<Surface>,
mut create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), SwapchainCreationError> {
Self::validate(&device, &surface, &mut create_info)?;
if surface.flag().swap(true, Ordering::AcqRel) {
return Err(SwapchainCreationError::SurfaceInUse);
}
let (handle, image_handles) =
unsafe { Self::create(&device, &surface, &create_info, None)? };
let SwapchainCreateInfo {
min_image_count,
image_format,
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = create_info;
let swapchain = Arc::new(Swapchain {
handle,
device,
surface,
id: Self::next_id(),
min_image_count,
image_format: image_format.unwrap(),
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped,
full_screen_exclusive,
win32_monitor,
prev_present_id: Default::default(),
full_screen_exclusive_held: AtomicBool::new(false),
images: image_handles
.iter()
.map(|&handle| ImageEntry {
handle,
layout_initialized: AtomicBool::new(false),
})
.collect(),
retired: Mutex::new(false),
});
let swapchain_images = image_handles
.into_iter()
.enumerate()
.map(|(image_index, handle)| unsafe {
SwapchainImage::from_handle(handle, swapchain.clone(), image_index as u32)
})
.collect::<Result<_, _>>()?;
Ok((swapchain, swapchain_images))
}
pub fn recreate(
self: &Arc<Self>,
mut create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), SwapchainCreationError> {
Self::validate(&self.device, &self.surface, &mut create_info)?;
{
let mut retired = self.retired.lock();
if *retired {
return Err(SwapchainCreationError::SwapchainAlreadyRetired);
} else {
*retired = true;
}
}
let (handle, image_handles) =
unsafe { Self::create(&self.device, &self.surface, &create_info, Some(self))? };
let full_screen_exclusive_held =
if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
false
} else {
self.full_screen_exclusive_held.load(Ordering::SeqCst)
};
let SwapchainCreateInfo {
min_image_count,
image_format,
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = create_info;
let swapchain = Arc::new(Swapchain {
handle,
device: self.device.clone(),
surface: self.surface.clone(),
id: Self::next_id(),
min_image_count,
image_format: image_format.unwrap(),
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped,
full_screen_exclusive,
win32_monitor,
prev_present_id: Default::default(),
full_screen_exclusive_held: AtomicBool::new(full_screen_exclusive_held),
images: image_handles
.iter()
.map(|&handle| ImageEntry {
handle,
layout_initialized: AtomicBool::new(false),
})
.collect(),
retired: Mutex::new(false),
});
let swapchain_images = image_handles
.into_iter()
.enumerate()
.map(|(image_index, handle)| unsafe {
SwapchainImage::from_handle(handle, swapchain.clone(), image_index as u32)
})
.collect::<Result<_, _>>()?;
Ok((swapchain, swapchain_images))
}
fn validate(
device: &Device,
surface: &Surface,
create_info: &mut SwapchainCreateInfo,
) -> Result<(), SwapchainCreationError> {
let &mut SwapchainCreateInfo {
min_image_count,
ref mut image_format,
image_color_space,
ref mut image_extent,
image_array_layers,
image_usage,
ref mut image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped: _,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = create_info;
if !device.enabled_extensions().khr_swapchain {
return Err(SwapchainCreationError::RequirementNotMet {
required_for: "`Swapchain`",
requires_one_of: RequiresOneOf {
device_extensions: &["khr_swapchain"],
..Default::default()
},
});
}
assert_eq!(device.instance(), surface.instance());
image_color_space.validate_device(device)?;
image_usage.validate_device(device)?;
assert!(!image_usage.is_empty());
pre_transform.validate_device(device)?;
composite_alpha.validate_device(device)?;
present_mode.validate_device(device)?;
if full_screen_exclusive != FullScreenExclusive::Default {
if !device.enabled_extensions().ext_full_screen_exclusive {
return Err(SwapchainCreationError::RequirementNotMet {
required_for:
"`create_info.full_screen_exclusive` is not `FullScreenExclusive::Default`",
requires_one_of: RequiresOneOf {
device_extensions: &["ext_full_screen_exclusive"],
..Default::default()
},
});
}
full_screen_exclusive.validate_device(device)?;
}
if surface.api() == SurfaceApi::Win32
&& full_screen_exclusive == FullScreenExclusive::ApplicationControlled
{
if win32_monitor.is_none() {
return Err(SwapchainCreationError::Win32MonitorInvalid);
}
} else {
if win32_monitor.is_some() {
return Err(SwapchainCreationError::Win32MonitorInvalid);
}
}
if !device
.active_queue_family_indices()
.iter()
.copied()
.any(|index| unsafe {
device
.physical_device()
.surface_support_unchecked(index, surface)
.unwrap_or_default()
})
{
return Err(SwapchainCreationError::SurfaceNotSupported);
}
*image_format = Some({
let surface_formats = unsafe {
device.physical_device().surface_formats_unchecked(
surface,
SurfaceInfo {
full_screen_exclusive,
win32_monitor,
..Default::default()
},
)?
};
if let Some(format) = image_format {
format.validate_device(device)?;
if !surface_formats
.into_iter()
.any(|(f, c)| f == *format && c == image_color_space)
{
return Err(SwapchainCreationError::FormatColorSpaceNotSupported);
}
*format
} else {
surface_formats
.into_iter()
.find_map(|(f, c)| {
(c == image_color_space
&& [Format::R8G8B8A8_UNORM, Format::B8G8R8A8_UNORM].contains(&f))
.then_some(f)
})
.ok_or(SwapchainCreationError::FormatColorSpaceNotSupported)?
}
});
let surface_capabilities = unsafe {
device.physical_device().surface_capabilities_unchecked(
surface,
SurfaceInfo {
full_screen_exclusive,
win32_monitor,
..Default::default()
},
)?
};
if min_image_count < surface_capabilities.min_image_count
|| surface_capabilities
.max_image_count
.map_or(false, |c| min_image_count > c)
{
return Err(SwapchainCreationError::MinImageCountNotSupported {
provided: min_image_count,
min_supported: surface_capabilities.min_image_count,
max_supported: surface_capabilities.max_image_count,
});
}
if image_extent[0] == 0 || image_extent[1] == 0 {
*image_extent = surface_capabilities.current_extent.unwrap();
}
if image_extent[0] < surface_capabilities.min_image_extent[0]
|| image_extent[1] < surface_capabilities.min_image_extent[1]
|| image_extent[0] > surface_capabilities.max_image_extent[0]
|| image_extent[1] > surface_capabilities.max_image_extent[1]
{
return Err(SwapchainCreationError::ImageExtentNotSupported {
provided: *image_extent,
min_supported: surface_capabilities.min_image_extent,
max_supported: surface_capabilities.max_image_extent,
});
}
if image_extent.contains(&0) {
return Err(SwapchainCreationError::ImageExtentZeroLengthDimensions);
}
if image_array_layers == 0
|| image_array_layers > surface_capabilities.max_image_array_layers
{
return Err(SwapchainCreationError::ImageArrayLayersNotSupported {
provided: image_array_layers,
max_supported: surface_capabilities.max_image_array_layers,
});
}
if (ash::vk::ImageUsageFlags::from(image_usage)
& ash::vk::ImageUsageFlags::from(surface_capabilities.supported_usage_flags))
!= ash::vk::ImageUsageFlags::from(image_usage)
{
return Err(SwapchainCreationError::ImageUsageNotSupported {
provided: image_usage,
supported: surface_capabilities.supported_usage_flags,
});
}
match image_sharing {
Sharing::Exclusive => (),
Sharing::Concurrent(queue_family_indices) => {
queue_family_indices.sort_unstable();
queue_family_indices.dedup();
assert!(queue_family_indices.len() >= 2);
for &queue_family_index in queue_family_indices.iter() {
if queue_family_index
>= device.physical_device().queue_family_properties().len() as u32
{
return Err(
SwapchainCreationError::ImageSharingQueueFamilyIndexOutOfRange {
queue_family_index,
queue_family_count: device
.physical_device()
.queue_family_properties()
.len()
as u32,
},
);
}
}
}
};
if !surface_capabilities
.supported_transforms
.supports(pre_transform)
{
return Err(SwapchainCreationError::PreTransformNotSupported {
provided: pre_transform,
supported: surface_capabilities.supported_transforms,
});
}
if !surface_capabilities
.supported_composite_alpha
.supports(composite_alpha)
{
return Err(SwapchainCreationError::CompositeAlphaNotSupported {
provided: composite_alpha,
supported: surface_capabilities.supported_composite_alpha,
});
}
if !unsafe {
device
.physical_device()
.surface_present_modes_unchecked(surface)?
}
.any(|mode| mode == present_mode)
{
return Err(SwapchainCreationError::PresentModeNotSupported);
}
if unsafe {
device
.physical_device()
.image_format_properties_unchecked(ImageFormatInfo {
format: *image_format,
image_type: ImageType::Dim2d,
tiling: ImageTiling::Optimal,
usage: image_usage,
..Default::default()
})?
}
.is_none()
{
return Err(SwapchainCreationError::ImageFormatPropertiesNotSupported);
}
Ok(())
}
unsafe fn create(
device: &Device,
surface: &Surface,
create_info: &SwapchainCreateInfo,
old_swapchain: Option<&Swapchain>,
) -> Result<(ash::vk::SwapchainKHR, Vec<ash::vk::Image>), SwapchainCreationError> {
let &SwapchainCreateInfo {
min_image_count,
image_format,
image_color_space,
image_extent,
image_array_layers,
image_usage,
ref image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped,
full_screen_exclusive,
win32_monitor,
_ne: _,
} = create_info;
let (image_sharing_mode, queue_family_index_count, p_queue_family_indices) =
match image_sharing {
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()),
Sharing::Concurrent(ref ids) => (
ash::vk::SharingMode::CONCURRENT,
ids.len() as u32,
ids.as_ptr(),
),
};
let mut info_vk = ash::vk::SwapchainCreateInfoKHR {
flags: ash::vk::SwapchainCreateFlagsKHR::empty(),
surface: surface.handle(),
min_image_count,
image_format: image_format.unwrap().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_usage: image_usage.into(),
image_sharing_mode,
queue_family_index_count,
p_queue_family_indices,
pre_transform: pre_transform.into(),
composite_alpha: composite_alpha.into(),
present_mode: present_mode.into(),
clipped: clipped as ash::vk::Bool32,
old_swapchain: old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle),
..Default::default()
};
let mut surface_full_screen_exclusive_info_vk = None;
let mut surface_full_screen_exclusive_win32_info_vk = None;
if full_screen_exclusive != FullScreenExclusive::Default {
let next = surface_full_screen_exclusive_info_vk.insert(
ash::vk::SurfaceFullScreenExclusiveInfoEXT {
full_screen_exclusive: full_screen_exclusive.into(),
..Default::default()
},
);
next.p_next = info_vk.p_next as *mut _;
info_vk.p_next = next as *const _ as *const _;
}
if let Some(Win32Monitor(hmonitor)) = win32_monitor {
let next = surface_full_screen_exclusive_win32_info_vk.insert(
ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT {
hmonitor,
..Default::default()
},
);
next.p_next = info_vk.p_next as *mut _;
info_vk.p_next = next as *const _ as *const _;
}
let fns = device.fns();
let handle = {
let mut output = MaybeUninit::uninit();
(fns.khr_swapchain.create_swapchain_khr)(
device.handle(),
&info_vk,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
let image_handles = loop {
let mut count = 0;
(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 = (fns.khr_swapchain.get_swapchain_images_khr)(
device.handle(),
handle,
&mut count,
images.as_mut_ptr(),
);
match result {
ash::vk::Result::SUCCESS => {
images.set_len(count as usize);
break images;
}
ash::vk::Result::INCOMPLETE => (),
err => return Err(VulkanError::from(err).into()),
}
};
Ok((handle, image_handles))
}
#[inline]
pub fn create_info(&self) -> SwapchainCreateInfo {
SwapchainCreateInfo {
min_image_count: self.min_image_count,
image_format: Some(self.image_format),
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,
clipped: self.clipped,
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 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_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(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
}
#[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 clipped(&self) -> bool {
self.clipped
}
#[inline]
pub fn full_screen_exclusive(&self) -> FullScreenExclusive {
self.full_screen_exclusive
}
#[inline]
pub fn acquire_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> {
if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
return Err(FullScreenExclusiveError::NotApplicationControlled);
}
if self.full_screen_exclusive_held.swap(true, Ordering::SeqCst) {
return Err(FullScreenExclusiveError::DoubleAcquire);
}
unsafe {
let fns = self.device.fns();
(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(&self) -> Result<(), FullScreenExclusiveError> {
if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
return Err(FullScreenExclusiveError::NotApplicationControlled);
}
if !self
.full_screen_exclusive_held
.swap(false, Ordering::SeqCst)
{
return Err(FullScreenExclusiveError::DoubleRelease);
}
unsafe {
let fns = self.device.fns();
(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
}
}
}
impl Drop for Swapchain {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
(fns.khr_swapchain.destroy_swapchain_khr)(
self.device.handle(),
self.handle,
ptr::null(),
);
self.surface.flag().store(false, Ordering::Release);
}
}
}
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
}
}
crate::impl_id_counter!(Swapchain);
impl Debug for Swapchain {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
let Self {
handle,
device,
surface,
id: _,
min_image_count,
image_format,
image_color_space,
image_extent,
image_array_layers,
image_usage,
image_sharing,
pre_transform,
composite_alpha,
present_mode,
clipped,
full_screen_exclusive,
win32_monitor,
prev_present_id,
full_screen_exclusive_held,
images,
retired,
} = self;
f.debug_struct("Swapchain")
.field("handle", &handle)
.field("device", &device.handle())
.field("surface", &surface.handle())
.field("min_image_count", &min_image_count)
.field("image_format", &image_format)
.field("image_color_space", &image_color_space)
.field("image_extent", &image_extent)
.field("image_array_layers", &image_array_layers)
.field("image_usage", &image_usage)
.field("image_sharing", &image_sharing)
.field("pre_transform", &pre_transform)
.field("composite_alpha", &composite_alpha)
.field("present_mode", &present_mode)
.field("clipped", &clipped)
.field("full_screen_exclusive", &full_screen_exclusive)
.field("win32_monitor", &win32_monitor)
.field("prev_present_id", &prev_present_id)
.field("full_screen_exclusive_held", &full_screen_exclusive_held)
.field("images", &images)
.field("retired", &retired)
.finish()
}
}
#[derive(Clone, Debug)]
pub struct SwapchainCreateInfo {
pub min_image_count: u32,
pub image_format: Option<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 clipped: bool,
pub full_screen_exclusive: FullScreenExclusive,
pub win32_monitor: Option<Win32Monitor>,
pub _ne: crate::NonExhaustive,
}
impl Default for SwapchainCreateInfo {
#[inline]
fn default() -> Self {
Self {
min_image_count: 2,
image_format: None,
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,
clipped: true,
full_screen_exclusive: FullScreenExclusive::Default,
win32_monitor: None,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SwapchainCreationError {
OomError(OomError),
DeviceLost,
SurfaceLost,
SurfaceInUse,
NativeWindowInUse,
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
CompositeAlphaNotSupported {
provided: CompositeAlpha,
supported: SupportedCompositeAlpha,
},
FormatColorSpaceNotSupported,
ImageArrayLayersNotSupported { provided: u32, max_supported: u32 },
ImageExtentNotSupported {
provided: [u32; 2],
min_supported: [u32; 2],
max_supported: [u32; 2],
},
ImageExtentZeroLengthDimensions,
ImageFormatPropertiesNotSupported,
ImageSharingQueueFamilyIndexOutOfRange {
queue_family_index: u32,
queue_family_count: u32,
},
ImageUsageNotSupported {
provided: ImageUsage,
supported: ImageUsage,
},
MinImageCountNotSupported {
provided: u32,
min_supported: u32,
max_supported: Option<u32>,
},
PresentModeNotSupported,
PreTransformNotSupported {
provided: SurfaceTransform,
supported: SupportedSurfaceTransforms,
},
SurfaceNotSupported,
SwapchainAlreadyRetired,
Win32MonitorInvalid,
}
impl Error for SwapchainCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for SwapchainCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::DeviceLost => write!(f, "the device was lost"),
Self::SurfaceLost => write!(f, "the surface was lost"),
Self::SurfaceInUse => {
write!(f, "the surface is already used by another swapchain")
}
Self::NativeWindowInUse => {
write!(f, "the window is already in use by another API")
}
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::CompositeAlphaNotSupported { .. } => write!(
f,
"the provided `composite_alpha` is not supported by the surface for this device",
),
Self::FormatColorSpaceNotSupported => write!(
f,
"the provided `format` and `color_space` are not supported by the surface for this \
device",
),
Self::ImageArrayLayersNotSupported {
provided,
max_supported,
} => write!(
f,
"the provided `image_array_layers` ({}) is greater than what is supported ({}) by \
the surface for this device",
provided, max_supported,
),
Self::ImageExtentNotSupported {
provided,
min_supported,
max_supported,
} => write!(
f,
"the provided `image_extent` ({:?}) is not within the range (min: {:?}, max: {:?}) \
supported by the surface for this device",
provided, min_supported, max_supported,
),
Self::ImageExtentZeroLengthDimensions => write!(
f,
"the provided `image_extent` contained at least one dimension of zero length",
),
Self::ImageFormatPropertiesNotSupported => write!(
f,
"the provided image parameters are not supported as queried from \
`image_format_properties`",
),
Self::ImageSharingQueueFamilyIndexOutOfRange {
queue_family_index,
queue_family_count: _,
} => write!(
f,
"the provided `image_sharing` was set to `Concurrent`, but one of the specified \
queue family indices ({}) was out of range",
queue_family_index,
),
Self::ImageUsageNotSupported { .. } => write!(
f,
"the provided `image_usage` has fields set that are not supported by the surface \
for this device",
),
Self::MinImageCountNotSupported {
provided,
min_supported,
max_supported,
} => write!(
f,
"the provided `min_image_count` ({}) is not within the range (min: {}, max: {:?}) \
supported by the surface for this device",
provided, min_supported, max_supported,
),
Self::PresentModeNotSupported => write!(
f,
"the provided `present_mode` is not supported by the surface for this device",
),
Self::PreTransformNotSupported { .. } => write!(
f,
"the provided `pre_transform` is not supported by the surface for this device",
),
Self::SurfaceNotSupported => write!(
f,
"the provided `surface` is not supported by any of the device's queue families",
),
Self::SwapchainAlreadyRetired => {
write!(f, "the swapchain has already been used to create a new one")
}
Self::Win32MonitorInvalid => write!(
f,
"the `win32_monitor` value was `Some` when it must be `None` or vice-versa",
),
}
}
}
impl From<VulkanError> for SwapchainCreationError {
fn from(err: VulkanError) -> SwapchainCreationError {
match err {
err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)),
err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
VulkanError::DeviceLost => Self::DeviceLost,
VulkanError::SurfaceLost => Self::SurfaceLost,
VulkanError::NativeWindowInUse => Self::NativeWindowInUse,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for SwapchainCreationError {
fn from(err: OomError) -> SwapchainCreationError {
Self::OomError(err)
}
}
impl From<RequirementNotMet> for SwapchainCreationError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
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<T>(hmonitor: *const T) -> Self {
Self(hmonitor as _)
}
}
unsafe impl Send for Win32Monitor {}
unsafe impl Sync for Win32Monitor {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FullScreenExclusiveError {
OomError(OomError),
InitializationFailed,
SurfaceLost,
DoubleAcquire,
DoubleRelease,
NotApplicationControlled,
}
impl Error for FullScreenExclusiveError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
FullScreenExclusiveError::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for FullScreenExclusiveError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"{}",
match self {
FullScreenExclusiveError::OomError(_) => "not enough memory",
FullScreenExclusiveError::SurfaceLost => {
"the surface of this swapchain is no longer valid"
}
FullScreenExclusiveError::InitializationFailed => {
"operation could not be completed for driver specific reasons"
}
FullScreenExclusiveError::DoubleAcquire =>
"full-screen exclusivity is already acquired",
FullScreenExclusiveError::DoubleRelease =>
"full-screen exclusivity is not acquired",
FullScreenExclusiveError::NotApplicationControlled => {
"the swapchain is not in full-screen exclusive application controlled mode"
}
}
)
}
}
impl From<VulkanError> for FullScreenExclusiveError {
fn from(err: VulkanError) -> FullScreenExclusiveError {
match err {
err @ VulkanError::OutOfHostMemory => {
FullScreenExclusiveError::OomError(OomError::from(err))
}
err @ VulkanError::OutOfDeviceMemory => {
FullScreenExclusiveError::OomError(OomError::from(err))
}
VulkanError::SurfaceLost => FullScreenExclusiveError::SurfaceLost,
VulkanError::InitializationFailed => FullScreenExclusiveError::InitializationFailed,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for FullScreenExclusiveError {
fn from(err: OomError) -> FullScreenExclusiveError {
FullScreenExclusiveError::OomError(err)
}
}
pub fn acquire_next_image(
swapchain: Arc<Swapchain>,
timeout: Option<Duration>,
) -> Result<(u32, bool, SwapchainAcquireFuture), AcquireError> {
let semaphore = Arc::new(Semaphore::from_pool(swapchain.device.clone())?);
let fence = Fence::from_pool(swapchain.device.clone())?;
let AcquiredImage {
image_index,
suboptimal,
} = {
let retired = swapchain.retired.lock();
if *retired {
return Err(AcquireError::OutOfDate);
}
let acquire_result =
unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) };
if let &Err(AcquireError::FullScreenExclusiveModeLost) = &acquire_result {
swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
acquire_result?
};
Ok((
image_index,
suboptimal,
SwapchainAcquireFuture {
swapchain,
semaphore: Some(semaphore),
fence: Some(fence),
image_index,
finished: AtomicBool::new(false),
},
))
}
pub fn present<F>(
before: F,
queue: Arc<Queue>,
swapchain_info: SwapchainPresentInfo,
) -> PresentFuture<F>
where
F: GpuFuture,
{
assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count());
PresentFuture {
previous: before,
queue,
swapchain_info,
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
}
pub fn wait_for_present(
swapchain: Arc<Swapchain>,
present_id: u64,
timeout: Option<Duration>,
) -> Result<bool, PresentWaitError> {
let retired = swapchain.retired.lock();
if *retired {
return Err(PresentWaitError::OutOfDate);
}
if present_id == 0 {
return Err(PresentWaitError::PresentIdZero);
}
if !swapchain.device.enabled_features().present_wait {
return Err(PresentWaitError::RequirementNotMet {
required_for: "`wait_for_present`",
requires_one_of: RequiresOneOf {
features: &["present_wait"],
..Default::default()
},
});
}
let timeout_ns = timeout.map(|dur| dur.as_nanos() as u64).unwrap_or(0);
let result = unsafe {
(swapchain.device.fns().khr_present_wait.wait_for_present_khr)(
swapchain.device.handle(),
swapchain.handle,
present_id,
timeout_ns,
)
};
match result {
ash::vk::Result::SUCCESS => Ok(false),
ash::vk::Result::SUBOPTIMAL_KHR => Ok(true),
ash::vk::Result::TIMEOUT => Err(PresentWaitError::Timeout),
err => {
let err = VulkanError::from(err).into();
if let PresentWaitError::FullScreenExclusiveModeLost = &err {
swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
Err(err)
}
}
}
#[must_use]
pub struct SwapchainAcquireFuture {
swapchain: Arc<Swapchain>,
image_index: u32,
semaphore: Option<Arc<Semaphore>>,
fence: Option<Fence>,
finished: AtomicBool,
}
impl SwapchainAcquireFuture {
pub fn image_index(&self) -> u32 {
self.image_index
}
pub fn swapchain(&self) -> &Arc<Swapchain> {
&self.swapchain
}
}
unsafe impl GpuFuture for SwapchainAcquireFuture {
fn cleanup_finished(&mut self) {}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
if let Some(ref semaphore) = self.semaphore {
let sem = smallvec![semaphore.clone()];
Ok(SubmitAnyBuilder::SemaphoresWait(sem))
} else {
Ok(SubmitAnyBuilder::Empty)
}
}
fn flush(&self) -> Result<(), FlushError> {
Ok(())
}
unsafe fn signal_finished(&self) {
self.finished.store(true, Ordering::SeqCst);
}
fn queue_change_allowed(&self) -> bool {
true
}
fn queue(&self) -> Option<Arc<Queue>> {
None
}
fn check_buffer_access(
&self,
_buffer: &Buffer,
_range: Range<DeviceSize>,
_exclusive: bool,
_queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
Err(AccessCheckError::Unknown)
}
fn check_image_access(
&self,
image: &Image,
_range: Range<DeviceSize>,
_exclusive: bool,
expected_layout: ImageLayout,
_queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
if self.swapchain.index_of_image(image) != Some(self.image_index) {
return Err(AccessCheckError::Unknown);
}
if !self.swapchain.images[self.image_index as usize]
.layout_initialized
.load(Ordering::Relaxed)
&& expected_layout != ImageLayout::Undefined
{
return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized {
requested: expected_layout,
}));
}
if expected_layout != ImageLayout::Undefined && expected_layout != ImageLayout::PresentSrc {
return Err(AccessCheckError::Denied(
AccessError::UnexpectedImageLayout {
allowed: ImageLayout::PresentSrc,
requested: expected_layout,
},
));
}
Ok(None)
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
before: bool,
) -> Result<(), AccessCheckError> {
if before {
Ok(())
} else {
if swapchain == self.swapchain.as_ref() && image_index == self.image_index {
Ok(())
} else {
Err(AccessCheckError::Unknown)
}
}
}
}
impl Drop for SwapchainAcquireFuture {
fn drop(&mut self) {
if let Some(ref fence) = self.fence {
fence.wait(None).unwrap(); self.semaphore = None;
}
}
}
unsafe impl DeviceOwned for SwapchainAcquireFuture {
fn device(&self) -> &Arc<Device> {
&self.swapchain.device
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum AcquireError {
OomError(OomError),
DeviceLost,
Timeout,
SurfaceLost,
FullScreenExclusiveModeLost,
OutOfDate,
FenceError(FenceError),
SemaphoreError(SemaphoreError),
}
impl Error for AcquireError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
AcquireError::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for AcquireError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"{}",
match self {
AcquireError::OomError(_) => "not enough memory",
AcquireError::DeviceLost => "the connection to the device has been lost",
AcquireError::Timeout => "no image is available for acquiring yet",
AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid",
AcquireError::OutOfDate => "the swapchain needs to be recreated",
AcquireError::FullScreenExclusiveModeLost => {
"the swapchain no longer has full-screen exclusivity"
}
AcquireError::FenceError(_) => "error creating fence",
AcquireError::SemaphoreError(_) => "error creating semaphore",
}
)
}
}
impl From<FenceError> for AcquireError {
fn from(err: FenceError) -> Self {
AcquireError::FenceError(err)
}
}
impl From<SemaphoreError> for AcquireError {
fn from(err: SemaphoreError) -> Self {
AcquireError::SemaphoreError(err)
}
}
impl From<OomError> for AcquireError {
fn from(err: OomError) -> AcquireError {
AcquireError::OomError(err)
}
}
impl From<VulkanError> for AcquireError {
fn from(err: VulkanError) -> AcquireError {
match err {
err @ VulkanError::OutOfHostMemory => AcquireError::OomError(OomError::from(err)),
err @ VulkanError::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)),
VulkanError::DeviceLost => AcquireError::DeviceLost,
VulkanError::SurfaceLost => AcquireError::SurfaceLost,
VulkanError::OutOfDate => AcquireError::OutOfDate,
VulkanError::FullScreenExclusiveModeLost => AcquireError::FullScreenExclusiveModeLost,
_ => panic!("unexpected error: {:?}", err),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PresentWaitError {
OomError(OomError),
DeviceLost,
OutOfDate,
SurfaceLost,
FullScreenExclusiveModeLost,
Timeout,
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
PresentIdZero,
}
impl Error for PresentWaitError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for PresentWaitError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(e) => write!(f, "{}", e),
Self::DeviceLost => write!(f, "the connection to the device has been lost"),
Self::Timeout => write!(f, "no image is available for acquiring yet"),
Self::SurfaceLost => write!(f, "the surface of this swapchain is no longer valid"),
Self::OutOfDate => write!(f, "the swapchain needs to be recreated"),
Self::FullScreenExclusiveModeLost => {
write!(f, "the swapchain no longer has full-screen exclusivity")
}
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::PresentIdZero => write!(f, "present id of zero is invalid"),
}
}
}
impl From<OomError> for PresentWaitError {
fn from(err: OomError) -> PresentWaitError {
Self::OomError(err)
}
}
impl From<RequirementNotMet> for PresentWaitError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
impl From<VulkanError> for PresentWaitError {
fn from(err: VulkanError) -> PresentWaitError {
match err {
err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)),
err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
VulkanError::DeviceLost => Self::DeviceLost,
VulkanError::SurfaceLost => Self::SurfaceLost,
VulkanError::OutOfDate => Self::OutOfDate,
VulkanError::FullScreenExclusiveModeLost => Self::FullScreenExclusiveModeLost,
_ => panic!("unexpected error: {:?}", err),
}
}
}
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
pub struct PresentFuture<P>
where
P: GpuFuture,
{
previous: P,
queue: Arc<Queue>,
swapchain_info: SwapchainPresentInfo,
flushed: AtomicBool,
finished: AtomicBool,
}
impl<P> PresentFuture<P>
where
P: GpuFuture,
{
pub fn image_id(&self) -> u32 {
self.swapchain_info.image_index
}
pub fn swapchain(&self) -> &Arc<Swapchain> {
&self.swapchain_info.swapchain
}
}
unsafe impl<P> GpuFuture for PresentFuture<P>
where
P: GpuFuture,
{
fn cleanup_finished(&mut self) {
self.previous.cleanup_finished();
}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
if self.flushed.load(Ordering::SeqCst) {
return Ok(SubmitAnyBuilder::Empty);
}
let mut swapchain_info = self.swapchain_info.clone();
debug_assert!((swapchain_info.image_index as u32) < swapchain_info.swapchain.image_count());
let device = swapchain_info.swapchain.device();
if !device.enabled_features().present_id {
swapchain_info.present_id = None;
}
if device.enabled_extensions().khr_incremental_present {
for rectangle in &swapchain_info.present_regions {
assert!(rectangle.is_compatible_with(swapchain_info.swapchain.as_ref()));
}
} else {
swapchain_info.present_regions = Default::default();
}
let _queue = self.previous.queue();
Ok(match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
}),
SubmitAnyBuilder::SemaphoresWait(semaphores) => {
SubmitAnyBuilder::QueuePresent(PresentInfo {
wait_semaphores: semaphores.into_iter().collect(),
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::CommandBuffer(_, _) => {
self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::BindSparse(_, _) => {
self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::QueuePresent(mut present_info) => {
present_info
.swapchain_infos
.push(self.swapchain_info.clone());
SubmitAnyBuilder::QueuePresent(present_info)
}
})
}
fn flush(&self) -> Result<(), FlushError> {
unsafe {
let build_submission_result = self.build_submission();
self.flushed.store(true, Ordering::SeqCst);
match build_submission_result? {
SubmitAnyBuilder::Empty => Ok(()),
SubmitAnyBuilder::QueuePresent(present_info) => {
for swapchain_info in &present_info.swapchain_infos {
if swapchain_info.present_id.map_or(false, |present_id| {
!swapchain_info.swapchain.try_claim_present_id(present_id)
}) {
return Err(FlushError::PresentIdLessThanOrEqual);
}
}
match self.previous.check_swapchain_image_acquired(
&self.swapchain_info.swapchain,
self.swapchain_info.image_index,
true,
) {
Ok(_) => (),
Err(AccessCheckError::Unknown) => {
return Err(AccessError::SwapchainImageNotAcquired.into())
}
Err(AccessCheckError::Denied(e)) => return Err(e.into()),
}
Ok(self
.queue
.with(|mut q| q.present_unchecked(present_info))?
.map(|r| r.map(|_| ()))
.fold(Ok(()), Result::and)?)
}
_ => unreachable!(),
}
}
}
unsafe fn signal_finished(&self) {
self.flushed.store(true, Ordering::SeqCst);
self.finished.store(true, Ordering::SeqCst);
self.previous.signal_finished();
}
fn queue_change_allowed(&self) -> bool {
false
}
fn queue(&self) -> Option<Arc<Queue>> {
debug_assert!(match self.previous.queue() {
None => true,
Some(q) => q == self.queue,
});
Some(self.queue.clone())
}
fn check_buffer_access(
&self,
buffer: &Buffer,
range: Range<DeviceSize>,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.previous
.check_buffer_access(buffer, range, exclusive, queue)
}
fn check_image_access(
&self,
image: &Image,
range: Range<DeviceSize>,
exclusive: bool,
expected_layout: ImageLayout,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
if self.swapchain_info.swapchain.index_of_image(image)
== Some(self.swapchain_info.image_index)
{
Err(AccessCheckError::Unknown)
} else {
self.previous
.check_image_access(image, range, exclusive, expected_layout, queue)
}
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
before: bool,
) -> Result<(), AccessCheckError> {
if before {
self.previous
.check_swapchain_image_acquired(swapchain, image_index, false)
} else if swapchain == self.swapchain_info.swapchain.as_ref()
&& image_index == self.swapchain_info.image_index
{
Err(AccessError::SwapchainImageNotAcquired.into())
} else {
self.previous
.check_swapchain_image_acquired(swapchain, image_index, false)
}
}
}
unsafe impl<P> DeviceOwned for PresentFuture<P>
where
P: GpuFuture,
{
fn device(&self) -> &Arc<Device> {
self.queue.device()
}
}
impl<P> Drop for PresentFuture<P>
where
P: GpuFuture,
{
fn drop(&mut self) {
unsafe {
if !*self.flushed.get_mut() {
self.flush().ok();
}
if !*self.finished.get_mut() {
self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap();
self.previous.signal_finished();
}
}
}
}
pub struct AcquiredImage {
pub image_index: u32,
pub suboptimal: bool,
}
pub unsafe fn acquire_next_image_raw(
swapchain: &Swapchain,
timeout: Option<Duration>,
semaphore: Option<&Semaphore>,
fence: Option<&Fence>,
) -> Result<AcquiredImage, AcquireError> {
let fns = swapchain.device.fns();
let timeout_ns = if let Some(timeout) = timeout {
timeout
.as_secs()
.saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
} else {
u64::MAX
};
let mut out = MaybeUninit::uninit();
let result = (fns.khr_swapchain.acquire_next_image_khr)(
swapchain.device.handle(),
swapchain.handle,
timeout_ns,
semaphore
.map(|s| s.handle())
.unwrap_or(ash::vk::Semaphore::null()),
fence.map(|f| f.handle()).unwrap_or(ash::vk::Fence::null()),
out.as_mut_ptr(),
);
let suboptimal = match result {
ash::vk::Result::SUCCESS => false,
ash::vk::Result::SUBOPTIMAL_KHR => true,
ash::vk::Result::NOT_READY => return Err(AcquireError::Timeout),
ash::vk::Result::TIMEOUT => return Err(AcquireError::Timeout),
err => return Err(VulkanError::from(err).into()),
};
if let Some(semaphore) = semaphore {
let mut state = semaphore.state();
state.swapchain_acquire();
}
if let Some(fence) = fence {
let mut state = fence.state();
state.import_swapchain_acquire();
}
Ok(AcquiredImage {
image_index: out.assume_init(),
suboptimal,
})
}