use super::SupportedCompositeAlpha;
use super::SupportedSurfaceTransforms;
use crate::buffer::BufferAccess;
use crate::check_errors;
use crate::command_buffer::submit::SubmitAnyBuilder;
use crate::command_buffer::submit::SubmitPresentBuilder;
use crate::command_buffer::submit::SubmitPresentError;
use crate::command_buffer::submit::SubmitSemaphoresWaitBuilder;
use crate::device::physical::SurfacePropertiesError;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::device::Queue;
use crate::format::Format;
use crate::image::swapchain::SwapchainImage;
use crate::image::sys::UnsafeImage;
use crate::image::ImageAccess;
use crate::image::ImageCreateFlags;
use crate::image::ImageDimensions;
use crate::image::ImageFormatInfo;
use crate::image::ImageInner;
use crate::image::ImageLayout;
use crate::image::ImageTiling;
use crate::image::ImageType;
use crate::image::ImageUsage;
use crate::image::SampleCount;
use crate::swapchain::ColorSpace;
use crate::swapchain::CompositeAlpha;
use crate::swapchain::PresentMode;
use crate::swapchain::PresentRegion;
use crate::swapchain::Surface;
use crate::swapchain::SurfaceApi;
use crate::swapchain::SurfaceInfo;
use crate::swapchain::SurfaceSwapchainLock;
use crate::swapchain::SurfaceTransform;
use crate::sync::AccessCheckError;
use crate::sync::AccessError;
use crate::sync::AccessFlags;
use crate::sync::Fence;
use crate::sync::FlushError;
use crate::sync::GpuFuture;
use crate::sync::PipelineStages;
use crate::sync::Semaphore;
use crate::sync::SemaphoreCreationError;
use crate::sync::Sharing;
use crate::Error;
use crate::OomError;
use crate::Success;
use crate::VulkanObject;
use smallvec::SmallVec;
use std::error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
#[derive(Debug)]
pub struct Swapchain<W> {
handle: ash::vk::SwapchainKHR,
device: Arc<Device>,
surface: Arc<Surface<W>>,
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>,
full_screen_exclusive_held: AtomicBool,
images: Vec<ImageEntry>,
retired: Mutex<bool>,
}
#[derive(Debug)]
struct ImageEntry {
image: UnsafeImage,
undefined_layout: AtomicBool,
}
impl<W> Swapchain<W> {
pub fn new(
device: Arc<Device>,
surface: Arc<Surface<W>>,
mut create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain<W>>, Vec<Arc<SwapchainImage<W>>>), SwapchainCreationError> {
assert_eq!(
device.instance().internal_object(),
surface.instance().internal_object()
);
if !device.enabled_extensions().khr_swapchain {
return Err(SwapchainCreationError::ExtensionNotEnabled {
extension: "khr_swapchain",
reason: "created a new swapchain",
});
}
Self::validate(&device, &surface, &mut create_info)?;
if surface.flag().swap(true, Ordering::AcqRel) {
return Err(SwapchainCreationError::SurfaceInUse);
}
let (handle, images) = unsafe {
let (handle, image_handles) = Self::create(&device, &surface, &create_info, None)?;
let images = Self::wrap_images(&device, image_handles, &create_info);
(handle, images)
};
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,
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,
full_screen_exclusive_held: AtomicBool::new(false),
images,
retired: Mutex::new(false),
});
let swapchain_images = (0..swapchain.images.len())
.map(|n| unsafe { SwapchainImage::from_raw(swapchain.clone(), n) })
.collect::<Result<_, _>>()?;
Ok((swapchain, swapchain_images))
}
#[inline]
pub fn recreate(
self: &Arc<Self>,
mut create_info: SwapchainCreateInfo,
) -> Result<(Arc<Swapchain<W>>, Vec<Arc<SwapchainImage<W>>>), SwapchainCreationError> {
Self::validate(&self.device, &self.surface, &mut create_info)?;
{
let mut retired = self.retired.lock().unwrap();
if *retired {
return Err(SwapchainCreationError::SwapchainAlreadyRetired);
} else {
*retired = true;
}
}
let (handle, images) = unsafe {
let (handle, image_handles) =
Self::create(&self.device, &self.surface, &create_info, Some(self))?;
let images = Self::wrap_images(&self.device, image_handles, &create_info);
(handle, images)
};
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(),
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,
full_screen_exclusive_held: AtomicBool::new(full_screen_exclusive_held),
images,
retired: Mutex::new(false),
});
let swapchain_images = (0..swapchain.images.len())
.map(|n| unsafe { SwapchainImage::from_raw(swapchain.clone(), n) })
.collect::<Result<_, _>>()?;
Ok((swapchain, swapchain_images))
}
fn validate(
device: &Device,
surface: &Surface<W>,
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;
assert!(image_usage != ImageUsage::none());
if full_screen_exclusive != FullScreenExclusive::Default
&& !device.enabled_extensions().ext_full_screen_exclusive
{
return Err(SwapchainCreationError::ExtensionNotEnabled {
extension: "ext_full_screen_exclusive",
reason: "`full_screen_exclusive` was not `FullScreenExclusive::Default`",
});
}
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);
}
}
*image_format = Some({
let surface_formats = device.physical_device().surface_formats(
&surface,
SurfaceInfo {
full_screen_exclusive,
win32_monitor,
..Default::default()
},
)?;
if let Some(format) = image_format {
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(|| f)
})
.ok_or_else(|| SwapchainCreationError::FormatColorSpaceNotSupported)?
}
});
let surface_capabilities = device.physical_device().surface_capabilities(
&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,
});
}
assert!(image_extent[0] != 0 || image_extent[1] != 0);
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(ids) => {
ids.sort_unstable();
ids.dedup();
assert!(ids.len() >= 2);
for &id in ids.iter() {
if device.physical_device().queue_family_by_id(id).is_none() {
return Err(SwapchainCreationError::ImageSharingInvalidQueueFamilyId {
id,
});
}
}
}
};
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 !device
.physical_device()
.surface_present_modes(&surface)?
.any(|mode| mode == present_mode)
{
return Err(SwapchainCreationError::PresentModeNotSupported);
}
if device
.physical_device()
.image_format_properties(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<W>,
create_info: &SwapchainCreateInfo,
old_swapchain: Option<&Swapchain<W>>,
) -> 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 surface_full_screen_exclusive_info =
if full_screen_exclusive != FullScreenExclusive::Default {
Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT {
full_screen_exclusive: full_screen_exclusive.into(),
..Default::default()
})
} else {
None
};
let mut surface_full_screen_exclusive_win32_info =
if let Some(Win32Monitor(hmonitor)) = win32_monitor {
Some(ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT {
hmonitor,
..Default::default()
})
} else {
None
};
let mut create_info = ash::vk::SwapchainCreateInfoKHR {
flags: ash::vk::SwapchainCreateFlagsKHR::empty(),
surface: surface.internal_object(),
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()
};
if let Some(surface_full_screen_exclusive_info) =
surface_full_screen_exclusive_info.as_mut()
{
surface_full_screen_exclusive_info.p_next = create_info.p_next as *mut _;
create_info.p_next = surface_full_screen_exclusive_info as *const _ as *const _;
}
if let Some(surface_full_screen_exclusive_win32_info) =
surface_full_screen_exclusive_win32_info.as_mut()
{
surface_full_screen_exclusive_win32_info.p_next = create_info.p_next as *mut _;
create_info.p_next = surface_full_screen_exclusive_win32_info as *const _ as *const _;
}
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.khr_swapchain.create_swapchain_khr(
device.internal_object(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
let image_handles = {
let fns = device.fns();
let mut num = 0;
check_errors(fns.khr_swapchain.get_swapchain_images_khr(
device.internal_object(),
handle,
&mut num,
ptr::null_mut(),
))?;
let mut images = Vec::with_capacity(num as usize);
check_errors(fns.khr_swapchain.get_swapchain_images_khr(
device.internal_object(),
handle,
&mut num,
images.as_mut_ptr(),
))?;
images.set_len(num as usize);
images
};
Ok((handle, image_handles))
}
unsafe fn wrap_images(
device: &Arc<Device>,
image_handles: Vec<ash::vk::Image>,
create_info: &SwapchainCreateInfo,
) -> Vec<ImageEntry> {
let &SwapchainCreateInfo {
image_format,
image_extent,
image_array_layers,
image_usage,
ref image_sharing, ..
} = create_info;
image_handles
.into_iter()
.map(|handle| {
let dims = ImageDimensions::Dim2d {
width: image_extent[0],
height: image_extent[1],
array_layers: image_array_layers,
};
let img = unsafe {
UnsafeImage::from_raw(
device.clone(),
handle,
image_usage,
image_format.unwrap(),
ImageCreateFlags::none(),
dims,
SampleCount::Sample1,
1,
)
};
ImageEntry {
image: img,
undefined_layout: AtomicBool::new(true),
}
})
.collect()
}
#[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<W>> {
&self.surface
}
#[inline]
pub fn raw_image(&self, offset: usize) -> Option<ImageInner> {
self.images.get(offset).map(|i| ImageInner {
image: &i.image,
first_layer: 0,
num_layers: self.image_array_layers as usize,
first_mipmap_level: 0,
num_mipmap_levels: 1,
})
}
#[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 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
}
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 {
check_errors(
self.device
.fns()
.ext_full_screen_exclusive
.acquire_full_screen_exclusive_mode_ext(
self.device.internal_object(),
self.handle,
),
)?;
}
Ok(())
}
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 {
check_errors(
self.device
.fns()
.ext_full_screen_exclusive
.release_full_screen_exclusive_mode_ext(
self.device.internal_object(),
self.handle,
),
)?;
}
Ok(())
}
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_offset: usize) {
let image_entry = self.images.get(image_offset);
if let Some(ref image_entry) = image_entry {
image_entry.undefined_layout.store(false, Ordering::SeqCst);
}
}
pub(crate) fn is_image_layout_initialized(&self, image_offset: usize) -> bool {
let image_entry = self.images.get(image_offset);
if let Some(ref image_entry) = image_entry {
!image_entry.undefined_layout.load(Ordering::SeqCst)
} else {
false
}
}
}
impl<W> Drop for Swapchain<W> {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.khr_swapchain.destroy_swapchain_khr(
self.device.internal_object(),
self.handle,
ptr::null(),
);
self.surface.flag().store(false, Ordering::Release);
}
}
}
unsafe impl<W> VulkanObject for Swapchain<W> {
type Object = ash::vk::SwapchainKHR;
#[inline]
fn internal_object(&self) -> ash::vk::SwapchainKHR {
self.handle
}
}
unsafe impl<W> DeviceOwned for Swapchain<W> {
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl<W> PartialEq for Swapchain<W> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl<W> Eq for Swapchain<W> {}
impl<W> Hash for Swapchain<W> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
}
}
#[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::none(),
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,
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
CompositeAlphaNotSupported {
provided: CompositeAlpha,
supported: SupportedCompositeAlpha,
},
FormatColorSpaceNotSupported,
ImageArrayLayersNotSupported { provided: u32, max_supported: u32 },
ImageExtentNotSupported {
provided: [u32; 2],
min_supported: [u32; 2],
max_supported: [u32; 2],
},
ImageFormatPropertiesNotSupported,
ImageSharingInvalidQueueFamilyId { id: u32 },
ImageUsageNotSupported {
provided: ImageUsage,
supported: ImageUsage,
},
MinImageCountNotSupported {
provided: u32,
min_supported: u32,
max_supported: Option<u32>,
},
PresentModeNotSupported,
PreTransformNotSupported {
provided: SurfaceTransform,
supported: SupportedSurfaceTransforms,
},
SwapchainAlreadyRetired,
Win32MonitorInvalid,
}
impl error::Error for SwapchainCreationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for SwapchainCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available",),
Self::DeviceLost => write!(fmt, "the device was lost",),
Self::SurfaceLost => write!(fmt, "the surface was lost",),
Self::SurfaceInUse => {
write!(fmt, "the surface is already used by another swapchain",)
}
Self::NativeWindowInUse => {
write!(fmt, "the window is already in use by another API")
}
Self::ExtensionNotEnabled { extension, reason } => write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
),
Self::CompositeAlphaNotSupported { .. } => write!(
fmt,
"the provided `composite_alpha` is not supported by the surface for this device",
),
Self::FormatColorSpaceNotSupported => write!(
fmt,
"the provided `format` and `color_space` are not supported by the surface for this device",
),
Self::ImageArrayLayersNotSupported { provided, max_supported } => write!(
fmt,
"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!(
fmt,
"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::ImageFormatPropertiesNotSupported => write!(
fmt,
"the provided image parameters are not supported as queried from `image_format_properties`",
),
Self::ImageSharingInvalidQueueFamilyId { id } => write!(
fmt,
"the provided `image_sharing` was set to `Concurrent`, but one of the specified queue family ids ({}) was not valid",
id,
),
Self::ImageUsageNotSupported { .. } => write!(
fmt,
"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!(
fmt,
"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!(
fmt,
"the provided `present_mode` is not supported by the surface for this device",
),
Self::PreTransformNotSupported { .. } => write!(
fmt,
"the provided `pre_transform` is not supported by the surface for this device",
),
Self::SwapchainAlreadyRetired => write!(
fmt,
"the swapchain has already been used to create a new one",
),
Self::Win32MonitorInvalid => write!(
fmt,
"the `win32_monitor` value was `Some` when it must be `None` or vice-versa",
),
}
}
}
impl From<Error> for SwapchainCreationError {
#[inline]
fn from(err: Error) -> SwapchainCreationError {
match err {
err @ Error::OutOfHostMemory => Self::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
Error::DeviceLost => Self::DeviceLost,
Error::SurfaceLost => Self::SurfaceLost,
Error::NativeWindowInUse => Self::NativeWindowInUse,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for SwapchainCreationError {
#[inline]
fn from(err: OomError) -> SwapchainCreationError {
Self::OomError(err)
}
}
impl From<SurfacePropertiesError> for SwapchainCreationError {
#[inline]
fn from(err: SurfacePropertiesError) -> SwapchainCreationError {
match err {
SurfacePropertiesError::OomError(err) => Self::OomError(err),
SurfacePropertiesError::SurfaceLost => Self::SurfaceLost,
SurfacePropertiesError::NotSupported => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
#[non_exhaustive]
pub enum FullScreenExclusive {
Default = ash::vk::FullScreenExclusiveEXT::DEFAULT.as_raw(),
Allowed = ash::vk::FullScreenExclusiveEXT::ALLOWED.as_raw(),
Disallowed = ash::vk::FullScreenExclusiveEXT::DISALLOWED.as_raw(),
ApplicationControlled = ash::vk::FullScreenExclusiveEXT::APPLICATION_CONTROLLED.as_raw(),
}
impl From<FullScreenExclusive> for ash::vk::FullScreenExclusiveEXT {
#[inline]
fn from(val: FullScreenExclusive) -> Self {
Self::from_raw(val as i32)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR);
impl Win32Monitor {
#[inline]
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::Error for FullScreenExclusiveError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
FullScreenExclusiveError::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for FullScreenExclusiveError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
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<Error> for FullScreenExclusiveError {
#[inline]
fn from(err: Error) -> FullScreenExclusiveError {
match err {
err @ Error::OutOfHostMemory => FullScreenExclusiveError::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory => {
FullScreenExclusiveError::OomError(OomError::from(err))
}
Error::SurfaceLost => FullScreenExclusiveError::SurfaceLost,
Error::InitializationFailed => FullScreenExclusiveError::InitializationFailed,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for FullScreenExclusiveError {
#[inline]
fn from(err: OomError) -> FullScreenExclusiveError {
FullScreenExclusiveError::OomError(err)
}
}
pub fn acquire_next_image<W>(
swapchain: Arc<Swapchain<W>>,
timeout: Option<Duration>,
) -> Result<(usize, bool, SwapchainAcquireFuture<W>), AcquireError> {
let semaphore = Semaphore::from_pool(swapchain.device.clone())?;
let fence = Fence::from_pool(swapchain.device.clone())?;
let AcquiredImage { id, suboptimal } = {
let retired = swapchain.retired.lock().unwrap();
if *retired {
return Err(AcquireError::OutOfDate);
}
let acquire_result =
unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) };
if let &Err(AcquireError::FullScreenExclusiveLost) = &acquire_result {
swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
acquire_result?
};
Ok((
id,
suboptimal,
SwapchainAcquireFuture {
swapchain,
semaphore: Some(semaphore),
fence: Some(fence),
image_id: id,
finished: AtomicBool::new(false),
},
))
}
pub fn present<F, W>(
swapchain: Arc<Swapchain<W>>,
before: F,
queue: Arc<Queue>,
index: usize,
) -> PresentFuture<F, W>
where
F: GpuFuture,
{
assert!(index < swapchain.images.len());
PresentFuture {
previous: before,
queue,
swapchain,
image_id: index,
present_region: None,
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
}
pub fn present_incremental<F, W>(
swapchain: Arc<Swapchain<W>>,
before: F,
queue: Arc<Queue>,
index: usize,
present_region: PresentRegion,
) -> PresentFuture<F, W>
where
F: GpuFuture,
{
assert!(index < swapchain.images.len());
PresentFuture {
previous: before,
queue,
swapchain,
image_id: index,
present_region: Some(present_region),
flushed: AtomicBool::new(false),
finished: AtomicBool::new(false),
}
}
#[must_use]
pub struct SwapchainAcquireFuture<W> {
swapchain: Arc<Swapchain<W>>,
image_id: usize,
semaphore: Option<Semaphore>,
fence: Option<Fence>,
finished: AtomicBool,
}
impl<W> SwapchainAcquireFuture<W> {
#[inline]
pub fn image_id(&self) -> usize {
self.image_id
}
#[inline]
pub fn swapchain(&self) -> &Arc<Swapchain<W>> {
&self.swapchain
}
}
unsafe impl<W> GpuFuture for SwapchainAcquireFuture<W> {
#[inline]
fn cleanup_finished(&mut self) {}
#[inline]
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
if let Some(ref semaphore) = self.semaphore {
let mut sem = SubmitSemaphoresWaitBuilder::new();
sem.add_wait_semaphore(&semaphore);
Ok(SubmitAnyBuilder::SemaphoresWait(sem))
} else {
Ok(SubmitAnyBuilder::Empty)
}
}
#[inline]
fn flush(&self) -> Result<(), FlushError> {
Ok(())
}
#[inline]
unsafe fn signal_finished(&self) {
self.finished.store(true, Ordering::SeqCst);
}
#[inline]
fn queue_change_allowed(&self) -> bool {
true
}
#[inline]
fn queue(&self) -> Option<Arc<Queue>> {
None
}
#[inline]
fn check_buffer_access(
&self,
_: &dyn BufferAccess,
_: bool,
_: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
Err(AccessCheckError::Unknown)
}
#[inline]
fn check_image_access(
&self,
image: &dyn ImageAccess,
layout: ImageLayout,
_: bool,
_: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap();
if swapchain_image.image.internal_object() != image.inner().image.internal_object() {
return Err(AccessCheckError::Unknown);
}
if self.swapchain.images[self.image_id]
.undefined_layout
.load(Ordering::Relaxed)
&& layout != ImageLayout::Undefined
{
return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized {
requested: layout,
}));
}
if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc {
return Err(AccessCheckError::Denied(
AccessError::UnexpectedImageLayout {
allowed: ImageLayout::PresentSrc,
requested: layout,
},
));
}
Ok(None)
}
}
impl<W> Drop for SwapchainAcquireFuture<W> {
fn drop(&mut self) {
if let Some(ref fence) = self.fence {
fence.wait(None).unwrap(); self.semaphore = None;
}
}
}
unsafe impl<W> DeviceOwned for SwapchainAcquireFuture<W> {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.swapchain.device
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum AcquireError {
OomError(OomError),
DeviceLost,
Timeout,
SurfaceLost,
FullScreenExclusiveLost,
OutOfDate,
SemaphoreError(SemaphoreCreationError),
}
impl error::Error for AcquireError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
AcquireError::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for AcquireError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
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::FullScreenExclusiveLost => {
"the swapchain no longer has full-screen exclusivity"
}
AcquireError::SemaphoreError(_) => "error creating semaphore",
}
)
}
}
impl From<SemaphoreCreationError> for AcquireError {
fn from(err: SemaphoreCreationError) -> Self {
AcquireError::SemaphoreError(err)
}
}
impl From<OomError> for AcquireError {
#[inline]
fn from(err: OomError) -> AcquireError {
AcquireError::OomError(err)
}
}
impl From<Error> for AcquireError {
#[inline]
fn from(err: Error) -> AcquireError {
match err {
err @ Error::OutOfHostMemory => AcquireError::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)),
Error::DeviceLost => AcquireError::DeviceLost,
Error::SurfaceLost => AcquireError::SurfaceLost,
Error::OutOfDate => AcquireError::OutOfDate,
Error::FullScreenExclusiveLost => AcquireError::FullScreenExclusiveLost,
_ => 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, W>
where
P: GpuFuture,
{
previous: P,
queue: Arc<Queue>,
swapchain: Arc<Swapchain<W>>,
image_id: usize,
present_region: Option<PresentRegion>,
flushed: AtomicBool,
finished: AtomicBool,
}
impl<P, W> PresentFuture<P, W>
where
P: GpuFuture,
{
#[inline]
pub fn image_id(&self) -> usize {
self.image_id
}
#[inline]
pub fn swapchain(&self) -> &Arc<Swapchain<W>> {
&self.swapchain
}
}
unsafe impl<P, W> GpuFuture for PresentFuture<P, W>
where
P: GpuFuture,
{
#[inline]
fn cleanup_finished(&mut self) {
self.previous.cleanup_finished();
}
#[inline]
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
if self.flushed.load(Ordering::SeqCst) {
return Ok(SubmitAnyBuilder::Empty);
}
let queue = self.previous.queue().map(|q| q.clone());
Ok(match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => {
let mut builder = SubmitPresentBuilder::new();
builder.add_swapchain(
&self.swapchain,
self.image_id as u32,
self.present_region.as_ref(),
);
SubmitAnyBuilder::QueuePresent(builder)
}
SubmitAnyBuilder::SemaphoresWait(sem) => {
let mut builder: SubmitPresentBuilder = sem.into();
builder.add_swapchain(
&self.swapchain,
self.image_id as u32,
self.present_region.as_ref(),
);
SubmitAnyBuilder::QueuePresent(builder)
}
SubmitAnyBuilder::CommandBuffer(cb) => {
self.previous.flush()?;
let mut builder = SubmitPresentBuilder::new();
builder.add_swapchain(
&self.swapchain,
self.image_id as u32,
self.present_region.as_ref(),
);
SubmitAnyBuilder::QueuePresent(builder)
}
SubmitAnyBuilder::BindSparse(cb) => {
self.previous.flush()?;
let mut builder = SubmitPresentBuilder::new();
builder.add_swapchain(
&self.swapchain,
self.image_id as u32,
self.present_region.as_ref(),
);
SubmitAnyBuilder::QueuePresent(builder)
}
SubmitAnyBuilder::QueuePresent(present) => {
unimplemented!()
}
})
}
#[inline]
fn flush(&self) -> Result<(), FlushError> {
unsafe {
let build_submission_result = self.build_submission();
if let &Err(FlushError::FullScreenExclusiveLost) = &build_submission_result {
self.swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
match build_submission_result? {
SubmitAnyBuilder::Empty => {}
SubmitAnyBuilder::QueuePresent(present) => {
let present_result = present.submit(&self.queue);
if let &Err(SubmitPresentError::FullScreenExclusiveLost) = &present_result {
self.swapchain
.full_screen_exclusive_held
.store(false, Ordering::SeqCst);
}
present_result?;
}
_ => unreachable!(),
}
self.flushed.store(true, Ordering::SeqCst);
Ok(())
}
}
#[inline]
unsafe fn signal_finished(&self) {
self.flushed.store(true, Ordering::SeqCst);
self.finished.store(true, Ordering::SeqCst);
self.previous.signal_finished();
}
#[inline]
fn queue_change_allowed(&self) -> bool {
false
}
#[inline]
fn queue(&self) -> Option<Arc<Queue>> {
debug_assert!(match self.previous.queue() {
None => true,
Some(q) => q == self.queue,
});
Some(self.queue.clone())
}
#[inline]
fn check_buffer_access(
&self,
buffer: &dyn BufferAccess,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.previous.check_buffer_access(buffer, exclusive, queue)
}
#[inline]
fn check_image_access(
&self,
image: &dyn ImageAccess,
layout: ImageLayout,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap();
if swapchain_image.image.internal_object() == image.inner().image.internal_object() {
Err(AccessCheckError::Unknown)
} else {
self.previous
.check_image_access(image, layout, exclusive, queue)
}
}
}
unsafe impl<P, W> DeviceOwned for PresentFuture<P, W>
where
P: GpuFuture,
{
#[inline]
fn device(&self) -> &Arc<Device> {
self.queue.device()
}
}
impl<P, W> Drop for PresentFuture<P, W>
where
P: GpuFuture,
{
fn drop(&mut self) {
unsafe {
if !*self.finished.get_mut() {
match self.flush() {
Ok(()) => {
self.queue().unwrap().wait().unwrap();
self.previous.signal_finished();
}
Err(_) => {
}
}
}
}
}
}
pub struct AcquiredImage {
pub id: usize,
pub suboptimal: bool,
}
pub unsafe fn acquire_next_image_raw<W>(
swapchain: &Swapchain<W>,
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 r = check_errors(
fns.khr_swapchain.acquire_next_image_khr(
swapchain.device.internal_object(),
swapchain.handle,
timeout_ns,
semaphore
.map(|s| s.internal_object())
.unwrap_or(ash::vk::Semaphore::null()),
fence
.map(|f| f.internal_object())
.unwrap_or(ash::vk::Fence::null()),
out.as_mut_ptr(),
),
)?;
let out = out.assume_init();
let (id, suboptimal) = match r {
Success::Success => (out as usize, false),
Success::Suboptimal => (out as usize, true),
Success::NotReady => return Err(AcquireError::Timeout),
Success::Timeout => return Err(AcquireError::Timeout),
s => panic!("unexpected success value: {:?}", s),
};
Ok(AcquiredImage { id, suboptimal })
}