blob: 400b2b9d8fe35483e4018208c0f2dae920bcd0ed [file] [log] [blame]
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use super::{
ColorSpace, CompositeAlpha, CompositeAlphas, PresentMode, Surface, SurfaceTransform,
SurfaceTransforms, SwapchainPresentInfo,
};
use crate::{
buffer::Buffer,
device::{Device, DeviceOwned, Queue},
format::Format,
image::{
sys::Image, ImageFormatInfo, ImageLayout, ImageTiling, ImageType, ImageUsage,
SwapchainImage,
},
macros::{impl_id_counter, vulkan_enum},
swapchain::{PresentInfo, SurfaceApi, SurfaceInfo, SurfaceSwapchainLock},
sync::{
fence::{Fence, FenceError},
future::{AccessCheckError, AccessError, FlushError, GpuFuture, SubmitAnyBuilder},
semaphore::{Semaphore, SemaphoreError},
Sharing,
},
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,
},
thread,
time::Duration,
};
/// Contains the swapping system and the images that can be shown on a surface.
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,
// Whether full-screen exclusive is currently held.
full_screen_exclusive_held: AtomicBool,
// The images of this swapchain.
images: Vec<ImageEntry>,
// If true, that means we have tried to use this swapchain to recreate a new swapchain. The
// current swapchain can no longer be used for anything except presenting already-acquired
// images.
//
// We use a `Mutex` instead of an `AtomicBool` because we want to keep that locked while
// we acquire the image.
retired: Mutex<bool>,
}
#[derive(Debug)]
struct ImageEntry {
handle: ash::vk::Image,
layout_initialized: AtomicBool,
}
impl Swapchain {
/// Creates a new `Swapchain`.
///
/// This function returns the swapchain plus a list of the images that belong to the
/// swapchain. The order in which the images are returned is important for the
/// `acquire_next_image` and `present` functions.
///
/// # Panics
///
/// - Panics if the device and the surface don't belong to the same instance.
/// - Panics if `create_info.usage` is empty.
///
// TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win?
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)?;
// Checking that the surface doesn't already have a swapchain.
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))
}
/// Creates a new swapchain from this one.
///
/// Use this when a swapchain has become invalidated, such as due to window resizes.
///
/// # Panics
///
/// - Panics if `create_info.usage` is empty.
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();
// The swapchain has already been used to create a new one.
if *retired {
return Err(SwapchainCreationError::SwapchainAlreadyRetired);
} else {
// According to the documentation of VkSwapchainCreateInfoKHR:
//
// > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE,
// > any images not acquired by the application may be freed by the implementation,
// > which may occur even if creation of the new swapchain fails.
//
// Therefore, we set retired to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails.
*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::new`",
requires_one_of: RequiresOneOf {
device_extensions: &["khr_swapchain"],
..Default::default()
},
});
}
assert_eq!(device.instance(), surface.instance());
// VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter
image_color_space.validate_device(device)?;
// VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter
image_usage.validate_device(device)?;
// VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask
assert!(!image_usage.is_empty());
// VUID-VkSwapchainCreateInfoKHR-preTransform-parameter
pre_transform.validate_device(device)?;
// VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter
composite_alpha.validate_device(device)?;
// VUID-VkSwapchainCreateInfoKHR-presentMode-parameter
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()
},
});
}
// VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter
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);
}
}
// VUID-VkSwapchainCreateInfoKHR-surface-01270
if !device
.active_queue_family_indices()
.iter()
.copied()
.any(|index| unsafe {
// Use unchecked, because all validation has been done above.
device
.physical_device()
.surface_support_unchecked(index, surface)
.unwrap_or_default()
})
{
return Err(SwapchainCreationError::SurfaceNotSupported);
}
*image_format = Some({
// Use unchecked, because all validation has been done above.
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 {
// VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter
format.validate_device(device)?;
// VUID-VkSwapchainCreateInfoKHR-imageFormat-01273
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)?
}
});
// Use unchecked, because all validation has been done above.
let surface_capabilities = unsafe {
device.physical_device().surface_capabilities_unchecked(
surface,
SurfaceInfo {
full_screen_exclusive,
win32_monitor,
..Default::default()
},
)?
};
// VUID-VkSwapchainCreateInfoKHR-minImageCount-01272
// VUID-VkSwapchainCreateInfoKHR-presentMode-02839
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();
}
// VUID-VkSwapchainCreateInfoKHR-imageExtent-01274
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,
});
}
// VUID-VkSwapchainCreateInfoKHR-imageExtent-01689
// On some platforms, dimensions of zero-length can occur by minimizing the surface.
if image_extent.contains(&0) {
return Err(SwapchainCreationError::ImageExtentZeroLengthDimensions);
}
// VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275
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,
});
}
// VUID-VkSwapchainCreateInfoKHR-presentMode-01427
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) => {
// VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278
// VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428
queue_family_indices.sort_unstable();
queue_family_indices.dedup();
assert!(queue_family_indices.len() >= 2);
for &queue_family_index in queue_family_indices.iter() {
// VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428
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,
},
);
}
}
}
};
// VUID-VkSwapchainCreateInfoKHR-preTransform-01279
if !surface_capabilities
.supported_transforms
.contains_enum(pre_transform)
{
return Err(SwapchainCreationError::PreTransformNotSupported {
provided: pre_transform,
supported: surface_capabilities.supported_transforms,
});
}
// VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280
if !surface_capabilities
.supported_composite_alpha
.contains_enum(composite_alpha)
{
return Err(SwapchainCreationError::CompositeAlphaNotSupported {
provided: composite_alpha,
supported: surface_capabilities.supported_composite_alpha,
});
}
// VUID-VkSwapchainCreateInfoKHR-presentMode-01281
// Use unchecked, because all validation has been done above.
if !unsafe {
device
.physical_device()
.surface_present_modes_unchecked(surface)?
}
.any(|mode| mode == present_mode)
{
return Err(SwapchainCreationError::PresentModeNotSupported);
}
// VUID-VkSwapchainCreateInfoKHR-imageFormat-01778
// Use unchecked, because all validation has been done above.
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))
}
/// Returns the creation parameters of the swapchain.
#[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(()),
}
}
/// Returns the saved Surface, from the Swapchain creation.
#[inline]
pub fn surface(&self) -> &Arc<Surface> {
&self.surface
}
/// If `image` is one of the images of this swapchain, returns its index within the swapchain.
#[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)
}
/// Returns the number of images of the swapchain.
#[inline]
pub fn image_count(&self) -> u32 {
self.images.len() as u32
}
/// Returns the format of the images of the swapchain.
#[inline]
pub fn image_format(&self) -> Format {
self.image_format
}
/// Returns the color space of the images of the swapchain.
#[inline]
pub fn image_color_space(&self) -> ColorSpace {
self.image_color_space
}
/// Returns the extent of the images of the swapchain.
#[inline]
pub fn image_extent(&self) -> [u32; 2] {
self.image_extent
}
/// Returns the number of array layers of the images of the swapchain.
#[inline]
pub fn image_array_layers(&self) -> u32 {
self.image_array_layers
}
/// Returns the usage of the images of the swapchain.
#[inline]
pub fn image_usage(&self) -> ImageUsage {
self.image_usage
}
/// Returns the sharing of the images of the swapchain.
#[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
}
/// Returns the pre-transform that was passed when creating the swapchain.
#[inline]
pub fn pre_transform(&self) -> SurfaceTransform {
self.pre_transform
}
/// Returns the alpha mode that was passed when creating the swapchain.
#[inline]
pub fn composite_alpha(&self) -> CompositeAlpha {
self.composite_alpha
}
/// Returns the present mode that was passed when creating the swapchain.
#[inline]
pub fn present_mode(&self) -> PresentMode {
self.present_mode
}
/// Returns the value of `clipped` that was passed when creating the swapchain.
#[inline]
pub fn clipped(&self) -> bool {
self.clipped
}
/// Returns the value of 'full_screen_exclusive` that was passed when creating the swapchain.
#[inline]
pub fn full_screen_exclusive(&self) -> FullScreenExclusive {
self.full_screen_exclusive
}
/// Acquires full-screen exclusivity.
///
/// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`],
/// and must not already hold full-screen exclusivity. Full-screen exclusivity is held until
/// either the `release_full_screen_exclusive` is called, or if any of the the other `Swapchain`
/// functions return `FullScreenExclusiveLost`.
#[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(())
}
/// Releases full-screen exclusivity.
///
/// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`],
/// and must currently hold full-screen exclusivity.
#[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(())
}
/// `FullScreenExclusive::AppControlled` is not the active full-screen exclusivity mode,
/// then this function will always return false. If true is returned the swapchain
/// is in `FullScreenExclusive::AppControlled` full-screen exclusivity mode and exclusivity
/// is currently acquired.
#[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)
}
}
// This method is necessary to allow `SwapchainImage`s to signal when they have been
// transitioned out of their initial `undefined` image layout.
//
// See the `ImageAccess::layout_initialized` method documentation for more details.
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
}
}
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()
}
}
/// Parameters to create a new `Swapchain`.
///
/// Many of the values here must be supported by the physical device.
/// [`PhysicalDevice`](crate::device::physical::PhysicalDevice) has several
/// methods to query what is supported.
#[derive(Clone, Debug)]
pub struct SwapchainCreateInfo {
/// The minimum number of images that will be created.
///
/// The implementation is allowed to create more than this number, but never less.
///
/// The default value is `2`.
pub min_image_count: u32,
/// The format of the created images.
///
/// If set to `None`, [`Format::R8G8B8A8_UNORM`] or [`Format::B8G8R8A8_UNORM`] will be selected,
/// based on which is supported by the surface.
///
/// The default value is `None`.
pub image_format: Option<Format>,
/// The color space of the created images.
///
/// The default value is [`ColorSpace::SrgbNonLinear`].
pub image_color_space: ColorSpace,
/// The extent of the created images.
///
/// If any of the values is 0, the value of
/// [`SurfaceCapabilities::current_extent`](crate::swapchain::SurfaceCapabilities) will be used.
///
/// The default value is `[0, 0]`.
pub image_extent: [u32; 2],
/// The number of array layers of the created images.
///
/// The default value is `1`.
pub image_array_layers: u32,
/// How the created images will be used.
///
/// The default value is [`ImageUsage::empty()`], which must be overridden.
pub image_usage: ImageUsage,
/// Whether the created images can be shared across multiple queues, or are limited to a single
/// queue.
///
/// The default value is [`Sharing::Exclusive`].
pub image_sharing: Sharing<SmallVec<[u32; 4]>>,
/// The transform that should be applied to an image before it is presented.
///
/// The default value is [`SurfaceTransform::Identity`].
pub pre_transform: SurfaceTransform,
/// How alpha values of the pixels in the image are to be treated.
///
/// The default value is [`CompositeAlpha::Opaque`].
pub composite_alpha: CompositeAlpha,
/// How the swapchain should behave when multiple images are waiting in the queue to be
/// presented.
///
/// The default is [`PresentMode::Fifo`].
pub present_mode: PresentMode,
/// Whether the implementation is allowed to discard rendering operations that affect regions of
/// the surface which aren't visible. This is important to take into account if your fragment
/// shader has side-effects or if you want to read back the content of the image afterwards.
///
/// The default value is `true`.
pub clipped: bool,
/// How full-screen exclusivity is to be handled.
///
/// If set to anything other than [`FullScreenExclusive::Default`], then the
/// [`ext_full_screen_exclusive`](crate::device::DeviceExtensions::ext_full_screen_exclusive)
/// extension must be enabled on the device.
///
/// The default value is [`FullScreenExclusive::Default`].
pub full_screen_exclusive: FullScreenExclusive,
/// For Win32 surfaces, if `full_screen_exclusive` is
/// [`FullScreenExclusive::ApplicationControlled`], this specifies the monitor on which
/// full-screen exclusivity should be used.
///
/// For this case, the value must be `Some`, and for all others it must be `None`.
///
/// The default value is `None`.
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(()),
}
}
}
/// Error that can happen when creating a `Swapchain`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SwapchainCreationError {
/// Not enough memory.
OomError(OomError),
/// The device was lost.
DeviceLost,
/// The surface was lost.
SurfaceLost,
/// The surface is already used by another swapchain.
SurfaceInUse,
/// The window is already in use by another API.
NativeWindowInUse,
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// The provided `composite_alpha` is not supported by the surface for this device.
CompositeAlphaNotSupported {
provided: CompositeAlpha,
supported: CompositeAlphas,
},
/// The provided `format` and `color_space` are not supported by the surface for this device.
FormatColorSpaceNotSupported,
/// The provided `image_array_layers` is greater than what is supported by the surface for this
/// device.
ImageArrayLayersNotSupported { provided: u32, max_supported: u32 },
/// The provided `image_extent` is not within the range supported by the surface for this
/// device.
ImageExtentNotSupported {
provided: [u32; 2],
min_supported: [u32; 2],
max_supported: [u32; 2],
},
/// The provided `image_extent` contained at least one dimension of zero length.
/// This is prohibited by [VUID-VkSwapchainCreateInfoKHR-imageExtent-01689](https://khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSwapchainCreateInfoKHR.html#VUID-VkSwapchainCreateInfoKHR-imageExtent-01689)
/// which requires both the width and height be non-zero.
///
/// This error is distinct from `ImageExtentNotSupported` because a surface's minimum supported
/// length may not enforce this rule.
ImageExtentZeroLengthDimensions,
/// The provided image parameters are not supported as queried from `image_format_properties`.
ImageFormatPropertiesNotSupported,
/// The provided `image_sharing` was set to `Concurrent`, but one of the specified queue family
/// indices was out of range.
ImageSharingQueueFamilyIndexOutOfRange {
queue_family_index: u32,
queue_family_count: u32,
},
/// The provided `image_usage` has fields set that are not supported by the surface for this
/// device.
ImageUsageNotSupported {
provided: ImageUsage,
supported: ImageUsage,
},
/// The provided `min_image_count` is not within the range supported by the surface for this
/// device.
MinImageCountNotSupported {
provided: u32,
min_supported: u32,
max_supported: Option<u32>,
},
/// The provided `present_mode` is not supported by the surface for this device.
PresentModeNotSupported,
/// The provided `pre_transform` is not supported by the surface for this device.
PreTransformNotSupported {
provided: SurfaceTransform,
supported: SurfaceTransforms,
},
/// The provided `surface` is not supported by any of the device's queue families.
SurfaceNotSupported,
/// The swapchain has already been used to create a new one.
SwapchainAlreadyRetired,
/// The `win32_monitor` value was `Some` when it must be `None` or vice-versa.
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]
/// The way full-screen exclusivity is handled.
FullScreenExclusive = FullScreenExclusiveEXT(i32);
/// Indicates that the driver should determine the appropriate full-screen method
/// by whatever means it deems appropriate.
Default = DEFAULT,
/// Indicates that the driver may use full-screen exclusive mechanisms when available.
/// Such mechanisms may result in better performance and/or the availability of
/// different presentation capabilities, but may require a more disruptive transition
// during swapchain initialization, first presentation and/or destruction.
Allowed = ALLOWED,
/// Indicates that the driver should avoid using full-screen mechanisms which rely
/// on disruptive transitions.
Disallowed = DISALLOWED,
/// Indicates the application will manage full-screen exclusive mode by using the
/// [`Swapchain::acquire_full_screen_exclusive()`] and
/// [`Swapchain::release_full_screen_exclusive()`] functions.
ApplicationControlled = APPLICATION_CONTROLLED,
}
/// A wrapper around a Win32 monitor handle.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR);
impl Win32Monitor {
/// Wraps a Win32 monitor handle.
///
/// # Safety
///
/// - `hmonitor` must be a valid handle as returned by the Win32 API.
pub unsafe fn new<T>(hmonitor: *const T) -> Self {
Self(hmonitor as _)
}
}
// Winit's `MonitorHandle` is Send on Win32, so this seems safe.
unsafe impl Send for Win32Monitor {}
unsafe impl Sync for Win32Monitor {}
/// Error that can happen when calling `Swapchain::acquire_full_screen_exclusive` or
/// `Swapchain::release_full_screen_exclusive`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FullScreenExclusiveError {
/// Not enough memory.
OomError(OomError),
/// Operation could not be completed for driver specific reasons.
InitializationFailed,
/// The surface is no longer accessible and must be recreated.
SurfaceLost,
/// Full-screen exclusivity is already acquired.
DoubleAcquire,
/// Full-screen exclusivity is not currently acquired.
DoubleRelease,
/// The swapchain is not in full-screen exclusive application controlled mode.
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)
}
}
/// Tries to take ownership of an image in order to draw on it.
///
/// The function returns the index of the image in the array of images that was returned
/// when creating the swapchain, plus a future that represents the moment when the image will
/// become available from the GPU (which may not be *immediately*).
///
/// If you try to draw on an image without acquiring it first, the execution will block. (TODO
/// behavior may change).
///
/// The second field in the tuple in the Ok result is a bool represent if the acquisition was
/// suboptimal. In this case the acquired image is still usable, but the swapchain should be
/// recreated as the Surface's properties no longer match the swapchain.
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,
} = {
// Check that this is not an old swapchain. From specs:
// > swapchain must not have been replaced by being passed as the
// > VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR
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),
},
))
}
/// Presents an image on the screen.
///
/// The actual behavior depends on the present mode that you passed when creating the swapchain.
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());
// TODO: restore this check with a dummy ImageAccess implementation
/*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead
// Normally if `check_image_access` returns false we're supposed to call the `gpu_access`
// function on the image instead. But since we know that this method on `SwapchainImage`
// always returns false anyway (by design), we don't need to do it.
assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/
PresentFuture {
previous: before,
queue,
swapchain_info,
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
}
/// Wait for an image to be presented to the user. Must be used with a `present_id` given to
/// `present_with_id`.
///
/// Returns a bool to represent if the presentation was suboptimal. In this case the swapchain is
/// still usable, but the swapchain should be recreated as the Surface's properties no longer match
/// the swapchain.
pub fn wait_for_present(
swapchain: Arc<Swapchain>,
present_id: u64,
timeout: Option<Duration>,
) -> Result<bool, PresentWaitError> {
let retired = swapchain.retired.lock();
// VUID-vkWaitForPresentKHR-swapchain-04997
if *retired {
return Err(PresentWaitError::OutOfDate);
}
if present_id == 0 {
return Err(PresentWaitError::PresentIdZero);
}
// VUID-vkWaitForPresentKHR-presentWait-06234
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)
}
}
}
/// Represents the moment when the GPU will have access to a swapchain image.
#[must_use]
pub struct SwapchainAcquireFuture {
swapchain: Arc<Swapchain>,
image_index: u32,
// Semaphore that is signalled when the acquire is complete. Empty if the acquire has already
// happened.
semaphore: Option<Arc<Semaphore>>,
// Fence that is signalled when the acquire is complete. Empty if the acquire has already
// happened.
fence: Option<Fence>,
finished: AtomicBool,
}
impl SwapchainAcquireFuture {
/// Returns the index of the image in the list of images returned when creating the swapchain.
pub fn image_index(&self) -> u32 {
self.image_index
}
/// Returns the corresponding swapchain.
pub fn swapchain(&self) -> &Arc<Swapchain> {
&self.swapchain
}
/// Blocks the current thread until the swapchain image has been acquired, or timeout
///
/// If timeout is `None`, will potentially block forever
///
/// You still need to join with this future for present to work
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceError> {
match &self.fence {
Some(fence) => fence.wait(timeout),
None => Ok(()),
}
}
}
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<(), AccessCheckError> {
Err(AccessCheckError::Unknown)
}
fn check_image_access(
&self,
image: &Image,
_range: Range<DeviceSize>,
_exclusive: bool,
expected_layout: ImageLayout,
_queue: &Queue,
) -> Result<(), 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(())
}
#[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 thread::panicking() {
return;
}
if let Some(fence) = &self.fence {
fence.wait(None).unwrap(); // TODO: handle error?
self.semaphore = None;
}
// TODO: if this future is destroyed without being presented, then eventually acquiring
// a new image will block forever ; difficulty: hard
}
}
unsafe impl DeviceOwned for SwapchainAcquireFuture {
fn device(&self) -> &Arc<Device> {
&self.swapchain.device
}
}
/// Error that can happen when calling `acquire_next_image`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum AcquireError {
/// Not enough memory.
OomError(OomError),
/// The connection to the device has been lost.
DeviceLost,
/// The timeout of the function has been reached before an image was available.
Timeout,
/// The surface is no longer accessible and must be recreated.
SurfaceLost,
/// The swapchain has lost or doesn't have full-screen exclusivity possibly for
/// implementation-specific reasons outside of the application’s control.
FullScreenExclusiveModeLost,
/// The surface has changed in a way that makes the swapchain unusable. You must query the
/// surface's new properties and recreate a new swapchain if you want to continue drawing.
OutOfDate,
/// Error during fence creation.
FenceError(FenceError),
/// Error during semaphore creation.
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),
}
}
}
/// Error that can happen when calling `acquire_next_image`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PresentWaitError {
/// Not enough memory.
OomError(OomError),
/// The connection to the device has been lost.
DeviceLost,
/// The surface has changed in a way that makes the swapchain unusable. You must query the
/// surface's new properties and recreate a new swapchain if you want to continue drawing.
OutOfDate,
/// The surface is no longer accessible and must be recreated.
SurfaceLost,
/// The swapchain has lost or doesn't have full-screen exclusivity possibly for
/// implementation-specific reasons outside of the application’s control.
FullScreenExclusiveModeLost,
/// The timeout of the function has been reached before the present occured.
Timeout,
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// Present id of zero is invalid.
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),
}
}
}
/// Represents a swapchain image being presented on the screen.
#[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,
// True if `flush()` has been called on the future, which means that the present command has
// been submitted.
flushed: AtomicBool,
// True if `signal_finished()` has been called on the future, which means that the future has
// been submitted and has already been processed by the GPU.
finished: AtomicBool,
}
impl<P> PresentFuture<P>
where
P: GpuFuture,
{
/// Returns the index of the image in the list of images returned when creating the swapchain.
pub fn image_id(&self) -> u32 {
self.swapchain_info.image_index
}
/// Returns the corresponding swapchain.
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 < 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();
// TODO: if the swapchain image layout is not PRESENT, should add a transition command
// buffer
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(_, _) => {
// submit the command buffer by flushing previous.
// Since the implementation should remember being flushed it's safe to call build_submission multiple times
self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()],
..Default::default()
})
}
SubmitAnyBuilder::BindSparse(_, _) => {
// submit the command buffer by flushing previous.
// Since the implementation should remember being flushed it's safe to call build_submission multiple times
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 {
// If `flushed` already contains `true`, then `build_submission` will return `Empty`.
let build_submission_result = self.build_submission();
self.flushed.store(true, Ordering::SeqCst);
match build_submission_result? {
SubmitAnyBuilder::Empty => Ok(()),
SubmitAnyBuilder::QueuePresent(present_info) => {
// VUID-VkPresentIdKHR-presentIds-04999
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<(), 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<(), AccessCheckError> {
if self.swapchain_info.swapchain.index_of_image(image)
== Some(self.swapchain_info.image_index)
{
// This future presents the swapchain image, which "unlocks" it. Therefore any attempt
// to use this swapchain image afterwards shouldn't get granted automatic access.
// Instead any attempt to access the image afterwards should get an authorization from
// a later swapchain acquire future. Hence why we return `Unknown` here.
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) {
if thread::panicking() {
return;
}
unsafe {
if !*self.flushed.get_mut() {
// Flushing may fail, that's okay. We will still wait for the queue later, so any
// previous futures that were flushed correctly will still be waited upon.
self.flush().ok();
}
if !*self.finished.get_mut() {
// Block until the queue finished.
self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap();
self.previous.signal_finished();
}
}
}
}
pub struct AcquiredImage {
pub image_index: u32,
pub suboptimal: bool,
}
/// Unsafe variant of `acquire_next_image`.
///
/// # Safety
///
/// - The semaphore and/or the fence must be kept alive until it is signaled.
/// - The swapchain must not have been replaced by being passed as the old swapchain when creating
/// a new one.
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,
})
}