diff options
Diffstat (limited to 'src/swapchain/swapchain.rs')
-rw-r--r-- | src/swapchain/swapchain.rs | 2929 |
1 files changed, 1740 insertions, 1189 deletions
diff --git a/src/swapchain/swapchain.rs b/src/swapchain/swapchain.rs index 3eb9051..400b2b9 100644 --- a/src/swapchain/swapchain.rs +++ b/src/swapchain/swapchain.rs @@ -7,354 +7,782 @@ // notice may not be copied, modified, or distributed except // according to those terms. -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::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::ImageInner; -use crate::image::ImageLayout; -use crate::image::ImageTiling; -use crate::image::ImageType; -use crate::image::ImageUsage; -use crate::image::SampleCount; -use crate::swapchain::CapabilitiesError; -use crate::swapchain::ColorSpace; -use crate::swapchain::CompositeAlpha; -use crate::swapchain::PresentMode; -use crate::swapchain::PresentRegion; -use crate::swapchain::Surface; -use crate::swapchain::SurfaceSwapchainLock; -use crate::swapchain::SurfaceTransform; -use crate::sync::semaphore::SemaphoreError; -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::SharingMode; -use crate::Error; -use crate::OomError; -use crate::Success; -use crate::VulkanObject; -use std::error; -use std::fmt; -use std::mem; -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; - -/// The way fullscreen exclusivity is handled. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(i32)] -pub enum FullscreenExclusive { - /// Indicates that the driver should determine the appropriate full-screen method - /// by whatever means it deems appropriate. - Default = ash::vk::FullScreenExclusiveEXT::DEFAULT.as_raw(), - /// 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 = ash::vk::FullScreenExclusiveEXT::ALLOWED.as_raw(), - /// Indicates that the driver should avoid using full-screen mechanisms which rely - /// on disruptive transitions. - Disallowed = ash::vk::FullScreenExclusiveEXT::DISALLOWED.as_raw(), - /// Indicates the application will manage full-screen exclusive mode by using - /// `Swapchain::acquire_fullscreen_exclusive()` and - /// `Swapchain::release_fullscreen_exclusive()` functions. - AppControlled = ash::vk::FullScreenExclusiveEXT::APPLICATION_CONTROLLED.as_raw(), -} +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, +}; -impl From<FullscreenExclusive> for ash::vk::FullScreenExclusiveEXT { - #[inline] - fn from(val: FullscreenExclusive) -> Self { - Self::from_raw(val as i32) - } +/// 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>, } -/// 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<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())?; +#[derive(Debug)] +struct ImageEntry { + handle: ash::vk::Image, + layout_initialized: AtomicBool, +} - let AcquiredImage { id, 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 stale = swapchain.stale.lock().unwrap(); - if *stale { - return Err(AcquireError::OutOfDate); +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 acquire_result = - unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) }; + 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; - if let &Err(AcquireError::FullscreenExclusiveLost) = &acquire_result { - swapchain - .fullscreen_exclusive_held - .store(false, Ordering::SeqCst); + 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; + } } - acquire_result? - }; + let (handle, image_handles) = + unsafe { Self::create(&self.device, &self.surface, &create_info, Some(self))? }; - Ok(( - id, - suboptimal, - SwapchainAcquireFuture { - swapchain, - semaphore: Some(semaphore), - fence: Some(fence), - image_id: id, - finished: AtomicBool::new(false), - }, - )) -} + let full_screen_exclusive_held = + if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { + false + } else { + self.full_screen_exclusive_held.load(Ordering::SeqCst) + }; -/// Presents an image on the screen. -/// -/// The parameter is the same index as what `acquire_next_image` returned. The image must -/// have been acquired first. -/// -/// The actual behavior depends on the present mode that you passed when creating the -/// swapchain. -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()); + 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; - // 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*/ + 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), + }); - PresentFuture { - previous: before, - queue, - swapchain, - image_id: index, - present_region: None, - flushed: AtomicBool::new(false), - finished: AtomicBool::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)) } -} -/// Same as `swapchain::present`, except it allows specifying a present region. -/// Areas outside the present region may be ignored by Vulkan in order to optimize presentation. -/// -/// This is just an optimization hint, as the Vulkan driver is free to ignore the given present region. -/// -/// If `VK_KHR_incremental_present` is not enabled on the device, the parameter will be ignored. -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()); + 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; - // 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*/ + if !device.enabled_extensions().khr_swapchain { + return Err(SwapchainCreationError::RequirementNotMet { + required_for: "`Swapchain::new`", + requires_one_of: RequiresOneOf { + device_extensions: &["khr_swapchain"], + ..Default::default() + }, + }); + } - PresentFuture { - previous: before, - queue, - swapchain, - image_id: index, - present_region: Some(present_region), - flushed: AtomicBool::new(false), - finished: AtomicBool::new(false), - } -} + assert_eq!(device.instance(), surface.instance()); -/// Contains the swapping system and the images that can be shown on a surface. -pub struct Swapchain<W> { - // The Vulkan device this swapchain was created with. - device: Arc<Device>, - // The surface, which we need to keep alive. - surface: Arc<Surface<W>>, - // The swapchain object. - swapchain: ash::vk::SwapchainKHR, + // VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter + image_color_space.validate_device(device)?; - // The images of this swapchain. - images: Vec<ImageEntry>, + // VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter + image_usage.validate_device(device)?; - // 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. - stale: Mutex<bool>, - - // Parameters passed to the constructor. - num_images: u32, - format: Format, - color_space: ColorSpace, - dimensions: [u32; 2], - layers: u32, - usage: ImageUsage, - sharing_mode: SharingMode, - transform: SurfaceTransform, - composite_alpha: CompositeAlpha, - present_mode: PresentMode, - fullscreen_exclusive: FullscreenExclusive, - fullscreen_exclusive_held: AtomicBool, - clipped: bool, -} + // VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask + assert!(!image_usage.is_empty()); -struct ImageEntry { - // The actual image. - image: UnsafeImage, - // If true, then the image is still in the undefined layout and must be transitioned. - undefined_layout: AtomicBool, -} + // VUID-VkSwapchainCreateInfoKHR-preTransform-parameter + pre_transform.validate_device(device)?; -impl<W> Swapchain<W> { - /// Starts the process of building a new swapchain, using default values for the parameters. - #[inline] - pub fn start(device: Arc<Device>, surface: Arc<Surface<W>>) -> SwapchainBuilder<W> { - SwapchainBuilder { - device, - surface, + // VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter + composite_alpha.validate_device(device)?; - num_images: 2, - format: None, - color_space: ColorSpace::SrgbNonLinear, - dimensions: None, - layers: 1, - usage: ImageUsage::none(), - sharing_mode: SharingMode::Exclusive, - transform: Default::default(), - composite_alpha: CompositeAlpha::Opaque, - present_mode: PresentMode::Fifo, - fullscreen_exclusive: FullscreenExclusive::Default, - clipped: true, + // 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, + }); + } - old_swapchain: None, + // 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(()) } - /// Starts building a new swapchain from an existing swapchain. - /// - /// Use this when a swapchain has become invalidated, such as due to window resizes. - /// The builder is pre-filled with the parameters of the old one, except for `dimensions`, - /// which is set to `None`. + 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 recreate(self: &Arc<Self>) -> SwapchainBuilder<W> { - SwapchainBuilder { - device: self.device().clone(), - surface: self.surface().clone(), - - num_images: self.images.len() as u32, - format: Some(self.format), - color_space: self.color_space, - dimensions: None, - layers: self.layers, - usage: self.usage, - sharing_mode: self.sharing_mode.clone(), - transform: self.transform, + 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, - fullscreen_exclusive: self.fullscreen_exclusive, clipped: self.clipped, - - old_swapchain: Some(self.clone()), + 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<W>> { + pub fn surface(&self) -> &Arc<Surface> { &self.surface } - /// Returns of the images that belong to this swapchain. + /// If `image` is one of the images of this swapchain, returns its index within the swapchain. #[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.layers as usize, - first_mipmap_level: 0, - num_mipmap_levels: 1, - }) + 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 num_images(&self) -> u32 { + pub fn image_count(&self) -> u32 { self.images.len() as u32 } /// Returns the format of the images of the swapchain. #[inline] - pub fn format(&self) -> Format { - self.format + 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 } - /// Returns the dimensions of the images of the swapchain. #[inline] - pub fn dimensions(&self) -> [u32; 2] { - self.dimensions + pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool { + &self.full_screen_exclusive_held } - /// Returns the number of layers of the images of the swapchain. #[inline] - pub fn layers(&self) -> u32 { - self.layers + 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 transform that was passed when creating the swapchain. + /// Returns the pre-transform that was passed when creating the swapchain. #[inline] - pub fn transform(&self) -> SurfaceTransform { - self.transform + pub fn pre_transform(&self) -> SurfaceTransform { + self.pre_transform } /// Returns the alpha mode that was passed when creating the swapchain. @@ -375,75 +803,81 @@ impl<W> Swapchain<W> { self.clipped } - /// Returns the value of 'fullscreen_exclusive` that was passed when creating the swapchain. + /// Returns the value of 'full_screen_exclusive` that was passed when creating the swapchain. #[inline] - pub fn fullscreen_exclusive(&self) -> FullscreenExclusive { - self.fullscreen_exclusive + pub fn full_screen_exclusive(&self) -> FullScreenExclusive { + self.full_screen_exclusive } - /// `FullscreenExclusive::AppControlled` must be the active fullscreen exclusivity mode. - /// Acquire fullscreen exclusivity until either the `release_fullscreen_exclusive` is - /// called, or if any of the the other `Swapchain` functions return `FullscreenExclusiveLost`. - /// Requires: `FullscreenExclusive::AppControlled` - pub fn acquire_fullscreen_exclusive(&self) -> Result<(), FullscreenExclusiveError> { - if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { - return Err(FullscreenExclusiveError::NotAppControlled); + /// 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.fullscreen_exclusive_held.swap(true, Ordering::SeqCst) { - return Err(FullscreenExclusiveError::DoubleAcquire); + 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.swapchain, - ), - )?; + 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(()) } - /// `FullscreenExclusive::AppControlled` must be the active fullscreen exclusivity mode. - /// Release fullscreen exclusivity. - pub fn release_fullscreen_exclusive(&self) -> Result<(), FullscreenExclusiveError> { - if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { - return Err(FullscreenExclusiveError::NotAppControlled); + /// 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.fullscreen_exclusive_held.swap(false, Ordering::SeqCst) { - return Err(FullscreenExclusiveError::DoubleRelease); + 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.swapchain, - ), - )?; + 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 fullscreen exclusivity mode, + /// `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` fullscreen exclusivity mode and exclusivity + /// is in `FullScreenExclusive::AppControlled` full-screen exclusivity mode and exclusivity /// is currently acquired. - pub fn is_fullscreen_exclusive(&self) -> bool { - if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { + #[inline] + pub fn is_full_screen_exclusive(&self) -> bool { + if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { false } else { - self.fullscreen_exclusive_held.load(Ordering::SeqCst) + self.full_screen_exclusive_held.load(Ordering::SeqCst) } } @@ -451,813 +885,870 @@ impl<W> Swapchain<W> { // 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_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 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_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) + 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 } } } -unsafe impl<W> VulkanObject for Swapchain<W> { - type Object = ash::vk::SwapchainKHR; - +impl Drop for Swapchain { #[inline] - fn internal_object(&self) -> ash::vk::SwapchainKHR { - self.swapchain + 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<W> DeviceOwned for Swapchain<W> { - fn device(&self) -> &Arc<Device> { - &self.device - } -} +unsafe impl VulkanObject for Swapchain { + type Handle = ash::vk::SwapchainKHR; -impl<W> fmt::Debug for Swapchain<W> { #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "<Vulkan swapchain {:?}>", self.swapchain) + fn handle(&self) -> Self::Handle { + self.handle } } -impl<W> Drop for Swapchain<W> { +unsafe impl DeviceOwned for Swapchain { #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - fns.khr_swapchain.destroy_swapchain_khr( - self.device.internal_object(), - self.swapchain, - ptr::null(), - ); - self.surface.flag().store(false, Ordering::Release); - } + fn device(&self) -> &Arc<Device> { + &self.device } } -/// Builder for a [`Swapchain`]. -#[derive(Debug)] -pub struct SwapchainBuilder<W> { - device: Arc<Device>, - surface: Arc<Surface<W>>, - old_swapchain: Option<Arc<Swapchain<W>>>, - - num_images: u32, - format: Option<Format>, // None = use a default - color_space: ColorSpace, - dimensions: Option<[u32; 2]>, - layers: u32, - usage: ImageUsage, - sharing_mode: SharingMode, - transform: SurfaceTransform, - composite_alpha: CompositeAlpha, - present_mode: PresentMode, - fullscreen_exclusive: FullscreenExclusive, - clipped: bool, -} +impl_id_counter!(Swapchain); -impl<W> SwapchainBuilder<W> { - /// Builds a new swapchain. Allocates images who content can be made visible on a surface. - /// - /// See also the `Surface::get_capabilities` function which returns the values that are - /// supported by the implementation. All the parameters that you pass to the builder - /// must be supported. - /// - /// 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. - /// - /// # Panic - /// - /// - Panics if the device and the surface don't belong to the same instance. - /// - Panics if `usage` is empty. - /// - // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? - pub fn build( - self, - ) -> Result<(Arc<Swapchain<W>>, Vec<Arc<SwapchainImage<W>>>), SwapchainCreationError> { - let SwapchainBuilder { +impl Debug for Swapchain { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + let Self { + handle, device, surface, - old_swapchain, - - num_images, - format, - color_space, - dimensions, - layers, - usage, - sharing_mode, - transform, + id: _, + min_image_count, + image_format, + image_color_space, + image_extent, + image_array_layers, + image_usage, + image_sharing, + pre_transform, composite_alpha, present_mode, - fullscreen_exclusive, clipped, + full_screen_exclusive, + win32_monitor, + prev_present_id, + full_screen_exclusive_held, + images, + retired, } = self; - assert_eq!( - device.instance().internal_object(), - surface.instance().internal_object() - ); - - // Checking that the requested parameters match the capabilities. - let capabilities = surface.capabilities(device.physical_device())?; - if num_images < capabilities.min_image_count { - return Err(SwapchainCreationError::UnsupportedMinImagesCount); - } - if let Some(c) = capabilities.max_image_count { - if num_images > c { - return Err(SwapchainCreationError::UnsupportedMaxImagesCount); - } - } + 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() + } +} - let format = { - if let Some(format) = format { - if !capabilities - .supported_formats - .iter() - .any(|&(f, c)| f == format && c == color_space) - { - return Err(SwapchainCreationError::UnsupportedFormat); - } - format - } else { - if let Some(format) = [Format::R8G8B8A8Unorm, Format::B8G8R8A8Unorm] - .iter() - .copied() - .find(|&format| { - capabilities - .supported_formats - .iter() - .any(|&(f, c)| f == format && c == color_space) - }) - { - format - } else { - return Err(SwapchainCreationError::UnsupportedFormat); - } - } - }; +/// 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, - let dimensions = if let Some(dimensions) = dimensions { - if dimensions[0] < capabilities.min_image_extent[0] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - if dimensions[1] < capabilities.min_image_extent[1] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - if dimensions[0] > capabilities.max_image_extent[0] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - if dimensions[1] > capabilities.max_image_extent[1] { - return Err(SwapchainCreationError::UnsupportedDimensions); - } - dimensions - } else { - capabilities.current_extent.unwrap() - }; - if layers < 1 || layers > capabilities.max_image_array_layers { - return Err(SwapchainCreationError::UnsupportedArrayLayers); - } - if (ash::vk::ImageUsageFlags::from(usage) - & ash::vk::ImageUsageFlags::from(capabilities.supported_usage_flags)) - != ash::vk::ImageUsageFlags::from(usage) - { - return Err(SwapchainCreationError::UnsupportedUsageFlags); - } - if !capabilities.supported_transforms.supports(transform) { - return Err(SwapchainCreationError::UnsupportedSurfaceTransform); - } - if !capabilities - .supported_composite_alpha - .supports(composite_alpha) - { - return Err(SwapchainCreationError::UnsupportedCompositeAlpha); - } - if !capabilities.present_modes.supports(present_mode) { - return Err(SwapchainCreationError::UnsupportedPresentMode); - } + /// 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>, - let flags = ImageCreateFlags::none(); - - // check that the physical device supports the swapchain image configuration - match device.image_format_properties( - format, - ImageType::Dim2d, - ImageTiling::Optimal, - usage, - flags, - ) { - Ok(_) => (), - Err(e) => { - eprintln!("{}", e); - return Err(SwapchainCreationError::UnsupportedImageConfiguration); - } - } + /// The color space of the created images. + /// + /// The default value is [`ColorSpace::SrgbNonLinear`]. + pub image_color_space: ColorSpace, - // If we recreate a swapchain, make sure that the surface is the same. - if let Some(ref sc) = old_swapchain { - if surface.internal_object() != sc.surface.internal_object() { - return Err(SwapchainCreationError::OldSwapchainSurfaceMismatch); - } - } else { - // Checking that the surface doesn't already have a swapchain. - let has_already = surface.flag().swap(true, Ordering::AcqRel); - if has_already { - return Err(SwapchainCreationError::SurfaceInUse); - } - } + /// 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], - if !device.enabled_extensions().khr_swapchain { - return Err(SwapchainCreationError::MissingExtensionKHRSwapchain); - } + /// The number of array layers of the created images. + /// + /// The default value is `1`. + pub image_array_layers: u32, - let mut surface_full_screen_exclusive_info = None; - - // TODO: VK_EXT_FULL_SCREEN_EXCLUSIVE requires these extensions, so they should always - // be enabled if it is. A separate check here is unnecessary; this should be checked at - // device creation. - if device.enabled_extensions().ext_full_screen_exclusive - && surface - .instance() - .enabled_extensions() - .khr_get_physical_device_properties2 - && surface - .instance() - .enabled_extensions() - .khr_get_surface_capabilities2 - { - surface_full_screen_exclusive_info = Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT { - full_screen_exclusive: fullscreen_exclusive.into(), - ..Default::default() - }); - } + /// How the created images will be used. + /// + /// The default value is [`ImageUsage::empty()`], which must be overridden. + pub image_usage: ImageUsage, - let p_next = match surface_full_screen_exclusive_info.as_ref() { - Some(some) => unsafe { mem::transmute(some as *const _) }, - None => ptr::null(), - }; + /// 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]>>, - // Required by the specs. - assert_ne!(usage, ImageUsage::none()); + /// The transform that should be applied to an image before it is presented. + /// + /// The default value is [`SurfaceTransform::Identity`]. + pub pre_transform: SurfaceTransform, - if let Some(ref old_swapchain) = old_swapchain { - let mut stale = old_swapchain.stale.lock().unwrap(); + /// How alpha values of the pixels in the image are to be treated. + /// + /// The default value is [`CompositeAlpha::Opaque`]. + pub composite_alpha: CompositeAlpha, - // The swapchain has already been used to create a new one. - if *stale { - return Err(SwapchainCreationError::OldSwapchainAlreadyUsed); - } 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 stale to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails. - *stale = true; - } - } + /// 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, - let fns = device.fns(); + /// 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, - let swapchain = unsafe { - let (sh_mode, sh_count, sh_indices) = match sharing_mode { - SharingMode::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()), - SharingMode::Concurrent(ref ids) => ( - ash::vk::SharingMode::CONCURRENT, - ids.len() as u32, - ids.as_ptr(), - ), - }; + /// 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, - let infos = ash::vk::SwapchainCreateInfoKHR { - p_next, - flags: ash::vk::SwapchainCreateFlagsKHR::empty(), - surface: surface.internal_object(), - min_image_count: num_images, - image_format: format.into(), - image_color_space: color_space.into(), - image_extent: ash::vk::Extent2D { - width: dimensions[0], - height: dimensions[1], - }, - image_array_layers: layers, - image_usage: usage.into(), - image_sharing_mode: sh_mode, - queue_family_index_count: sh_count, - p_queue_family_indices: sh_indices, - pre_transform: transform.into(), - composite_alpha: composite_alpha.into(), - present_mode: present_mode.into(), - clipped: if clipped { - ash::vk::TRUE - } else { - ash::vk::FALSE - }, - old_swapchain: if let Some(ref old_swapchain) = old_swapchain { - old_swapchain.swapchain - } else { - ash::vk::SwapchainKHR::null() - }, - ..Default::default() - }; + /// 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>, - let mut output = MaybeUninit::uninit(); - check_errors(fns.khr_swapchain.create_swapchain_khr( - device.internal_object(), - &infos, - ptr::null(), - output.as_mut_ptr(), - ))?; - output.assume_init() - }; + pub _ne: crate::NonExhaustive, +} - let image_handles = unsafe { - let mut num = 0; - check_errors(fns.khr_swapchain.get_swapchain_images_khr( - device.internal_object(), - swapchain, - &mut num, - ptr::null_mut(), - ))?; +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(()), + } + } +} - let mut images = Vec::with_capacity(num as usize); - check_errors(fns.khr_swapchain.get_swapchain_images_khr( - device.internal_object(), - swapchain, - &mut num, - images.as_mut_ptr(), - ))?; - images.set_len(num as usize); - images - }; +/// Error that can happen when creating a `Swapchain`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SwapchainCreationError { + /// Not enough memory. + OomError(OomError), - let images = image_handles - .into_iter() - .map(|image| unsafe { - let dims = ImageDimensions::Dim2d { - width: dimensions[0], - height: dimensions[1], - array_layers: layers, - }; - - let img = UnsafeImage::from_raw( - device.clone(), - image, - usage, - format, - flags, - dims, - SampleCount::Sample1, - 1, - ); - - ImageEntry { - image: img, - undefined_layout: AtomicBool::new(true), - } - }) - .collect::<Vec<_>>(); - - let fullscreen_exclusive_held = old_swapchain - .as_ref() - .map(|old_swapchain| { - if old_swapchain.fullscreen_exclusive != FullscreenExclusive::AppControlled { - false - } else { - old_swapchain - .fullscreen_exclusive_held - .load(Ordering::SeqCst) - } - }) - .unwrap_or(false); + /// The device was lost. + DeviceLost, - let swapchain = Arc::new(Swapchain { - device: device.clone(), - surface: surface.clone(), - swapchain, - images, - stale: Mutex::new(false), - num_images, - format, - color_space, - dimensions, - layers, - usage: usage.clone(), - sharing_mode, - transform, - composite_alpha, - present_mode, - fullscreen_exclusive, - fullscreen_exclusive_held: AtomicBool::new(fullscreen_exclusive_held), - clipped, - }); + /// The surface was lost. + SurfaceLost, - let swapchain_images = unsafe { - let mut swapchain_images = Vec::with_capacity(swapchain.images.len()); - for n in 0..swapchain.images.len() { - swapchain_images.push(SwapchainImage::from_raw(swapchain.clone(), n)?); - } - swapchain_images - }; + /// The surface is already used by another swapchain. + SurfaceInUse, - Ok((swapchain, swapchain_images)) - } + /// The window is already in use by another API. + NativeWindowInUse, - /// Sets the number of images that will be created. + 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. /// - /// The default is 2. - #[inline] - pub fn num_images(mut self, num_images: u32) -> Self { - self.num_images = num_images; - self - } + /// 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, +} - /// Sets the pixel format that will be used for the images. - /// - /// The default is either `R8G8B8A8Unorm` or `B8G8R8A8Unorm`, whichever is supported. - #[inline] - pub fn format(mut self, format: Format) -> Self { - self.format = Some(format); - self +impl Error for SwapchainCreationError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(err) => Some(err), + _ => None, + } } +} - /// Sets the color space that will be used for the images. - /// - /// The default is `SrgbNonLinear`. - #[inline] - pub fn color_space(mut self, color_space: ColorSpace) -> Self { - self.color_space = color_space; - self +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", + ), + } } +} - /// Sets the dimensions of the images. - /// - /// The default is `None`, which means the value of - /// [`Capabilities::current_extent`](crate::swapchain::Capabilities::current_extent) will be - /// used. Setting this will override it with a custom `Some` value. - #[inline] - pub fn dimensions(mut self, dimensions: [u32; 2]) -> Self { - self.dimensions = Some(dimensions); - self +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), + } } +} - /// Sets the number of layers for each image. - /// - /// The default is 1. - #[inline] - pub fn layers(mut self, layers: u32) -> Self { - self.layers = layers; - self +impl From<OomError> for SwapchainCreationError { + fn from(err: OomError) -> SwapchainCreationError { + Self::OomError(err) } +} - /// Sets how the images will be used. - /// - /// The default is `ImageUsage::none()`. - #[inline] - pub fn usage(mut self, usage: ImageUsage) -> Self { - self.usage = usage; - self +impl From<RequirementNotMet> for SwapchainCreationError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } } +} - /// Sets the sharing mode of the images. - /// - /// The default is `Exclusive`. - #[inline] - pub fn sharing_mode<S>(mut self, sharing_mode: S) -> Self - where - S: Into<SharingMode>, - { - self.sharing_mode = sharing_mode.into(); - self - } +vulkan_enum! { + #[non_exhaustive] - /// Sets the transform that is to be applied to the surface. - /// - /// The default is `Identity`. - #[inline] - pub fn transform(mut self, transform: SurfaceTransform) -> Self { - self.transform = transform; - self - } + /// The way full-screen exclusivity is handled. + FullScreenExclusive = FullScreenExclusiveEXT(i32); - /// Sets how alpha values of the pixels in the image are to be treated. - /// - /// The default is `Opaque`. - #[inline] - pub fn composite_alpha(mut self, composite_alpha: CompositeAlpha) -> Self { - self.composite_alpha = composite_alpha; - self - } + /// Indicates that the driver should determine the appropriate full-screen method + /// by whatever means it deems appropriate. + Default = DEFAULT, - /// Sets the present mode for the swapchain. - /// - /// The default is `Fifo`. - #[inline] - pub fn present_mode(mut self, present_mode: PresentMode) -> Self { - self.present_mode = present_mode; - self - } + /// 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, - /// Sets how fullscreen exclusivity is to be handled. - /// - /// The default is `Default`. - #[inline] - pub fn fullscreen_exclusive(mut self, fullscreen_exclusive: FullscreenExclusive) -> Self { - self.fullscreen_exclusive = fullscreen_exclusive; - self - } + /// 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); - /// Sets 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. +impl Win32Monitor { + /// Wraps a Win32 monitor handle. /// - /// The default is `true`. - #[inline] - pub fn clipped(mut self, clipped: bool) -> Self { - self.clipped = clipped; - self + /// # 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 _) } } -/// Error that can happen when creation a swapchain. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SwapchainCreationError { +// 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), - /// The device was lost. - DeviceLost, - /// The surface was lost. + + /// Operation could not be completed for driver specific reasons. + InitializationFailed, + + /// The surface is no longer accessible and must be recreated. SurfaceLost, - /// The surface is already used by another swapchain. - SurfaceInUse, - /// The window is already in use by another API. - NativeWindowInUse, - /// The `VK_KHR_swapchain` extension was not enabled. - MissingExtensionKHRSwapchain, - /// The `VK_EXT_full_screen_exclusive` extension was not enabled. - MissingExtensionExtFullScreenExclusive, - /// Surface mismatch between old and new swapchain. - OldSwapchainSurfaceMismatch, - /// The old swapchain has already been used to recreate another one. - OldSwapchainAlreadyUsed, - /// The requested number of swapchain images is not supported by the surface. - UnsupportedMinImagesCount, - /// The requested number of swapchain images is not supported by the surface. - UnsupportedMaxImagesCount, - /// The requested image format is not supported by the surface. - UnsupportedFormat, - /// The requested dimensions are not supported by the surface. - UnsupportedDimensions, - /// The requested array layers count is not supported by the surface. - UnsupportedArrayLayers, - /// The requested image usage is not supported by the surface. - UnsupportedUsageFlags, - /// The requested surface transform is not supported by the surface. - UnsupportedSurfaceTransform, - /// The requested composite alpha is not supported by the surface. - UnsupportedCompositeAlpha, - /// The requested present mode is not supported by the surface. - UnsupportedPresentMode, - /// The image configuration is not supported by the physical device. - UnsupportedImageConfiguration, + + /// 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::Error for SwapchainCreationError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - SwapchainCreationError::OomError(ref err) => Some(err), +impl Error for FullScreenExclusiveError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + FullScreenExclusiveError::OomError(err) => Some(err), _ => None, } } } -impl fmt::Display for SwapchainCreationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { +impl Display for FullScreenExclusiveError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { write!( - fmt, + f, "{}", - match *self { - SwapchainCreationError::OomError(_) => "not enough memory available", - SwapchainCreationError::DeviceLost => "the device was lost", - SwapchainCreationError::SurfaceLost => "the surface was lost", - SwapchainCreationError::SurfaceInUse => { - "the surface is already used by another swapchain" - } - SwapchainCreationError::NativeWindowInUse => { - "the window is already in use by another API" - } - SwapchainCreationError::MissingExtensionKHRSwapchain => { - "the `VK_KHR_swapchain` extension was not enabled" - } - SwapchainCreationError::MissingExtensionExtFullScreenExclusive => { - "the `VK_EXT_full_screen_exclusive` extension was not enabled" - } - SwapchainCreationError::OldSwapchainSurfaceMismatch => { - "surface mismatch between old and new swapchain" - } - SwapchainCreationError::OldSwapchainAlreadyUsed => { - "old swapchain has already been used to recreate a new one" - } - SwapchainCreationError::UnsupportedMinImagesCount => { - "the requested number of swapchain images is not supported by the surface" - } - SwapchainCreationError::UnsupportedMaxImagesCount => { - "the requested number of swapchain images is not supported by the surface" - } - SwapchainCreationError::UnsupportedFormat => { - "the requested image format is not supported by the surface" - } - SwapchainCreationError::UnsupportedDimensions => { - "the requested dimensions are not supported by the surface" - } - SwapchainCreationError::UnsupportedArrayLayers => { - "the requested array layers count is not supported by the surface" - } - SwapchainCreationError::UnsupportedUsageFlags => { - "the requested image usage is not supported by the surface" - } - SwapchainCreationError::UnsupportedSurfaceTransform => { - "the requested surface transform is not supported by the surface" - } - SwapchainCreationError::UnsupportedCompositeAlpha => { - "the requested composite alpha is not supported by the surface" + match self { + FullScreenExclusiveError::OomError(_) => "not enough memory", + FullScreenExclusiveError::SurfaceLost => { + "the surface of this swapchain is no longer valid" } - SwapchainCreationError::UnsupportedPresentMode => { - "the requested present mode is not supported by the surface" + FullScreenExclusiveError::InitializationFailed => { + "operation could not be completed for driver specific reasons" } - SwapchainCreationError::UnsupportedImageConfiguration => { - "the requested image configuration is not supported by the physical device" + 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 SwapchainCreationError { - #[inline] - fn from(err: Error) -> SwapchainCreationError { +impl From<VulkanError> for FullScreenExclusiveError { + fn from(err: VulkanError) -> FullScreenExclusiveError { match err { - err @ Error::OutOfHostMemory => SwapchainCreationError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => SwapchainCreationError::OomError(OomError::from(err)), - Error::DeviceLost => SwapchainCreationError::DeviceLost, - Error::SurfaceLost => SwapchainCreationError::SurfaceLost, - Error::NativeWindowInUse => SwapchainCreationError::NativeWindowInUse, + 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 SwapchainCreationError { - #[inline] - fn from(err: OomError) -> SwapchainCreationError { - SwapchainCreationError::OomError(err) +impl From<OomError> for FullScreenExclusiveError { + fn from(err: OomError) -> FullScreenExclusiveError { + FullScreenExclusiveError::OomError(err) } } -impl From<CapabilitiesError> for SwapchainCreationError { - #[inline] - fn from(err: CapabilitiesError) -> SwapchainCreationError { - match err { - CapabilitiesError::OomError(err) => SwapchainCreationError::OomError(err), - CapabilitiesError::SurfaceLost => SwapchainCreationError::SurfaceLost, +/// 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<W> { - swapchain: Arc<Swapchain<W>>, - image_id: usize, +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<Semaphore>, + 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<W> SwapchainAcquireFuture<W> { +impl SwapchainAcquireFuture { /// Returns the index of the image in the list of images returned when creating the swapchain. - #[inline] - pub fn image_id(&self) -> usize { - self.image_id + pub fn image_index(&self) -> u32 { + self.image_index } /// Returns the corresponding swapchain. - #[inline] - pub fn swapchain(&self) -> &Arc<Swapchain<W>> { + 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<W> GpuFuture for SwapchainAcquireFuture<W> { - #[inline] +unsafe impl GpuFuture for SwapchainAcquireFuture { 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); + let sem = smallvec![semaphore.clone()]; 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> { + _buffer: &Buffer, + _range: Range<DeviceSize>, + _exclusive: bool, + _queue: &Queue, + ) -> Result<(), 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() { + 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_id] - .undefined_layout + if !self.swapchain.images[self.image_index as usize] + .layout_initialized .load(Ordering::Relaxed) - && layout != ImageLayout::Undefined + && expected_layout != ImageLayout::Undefined { return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { - requested: layout, + requested: expected_layout, })); } - if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc { + if expected_layout != ImageLayout::Undefined && expected_layout != ImageLayout::PresentSrc { return Err(AccessCheckError::Denied( AccessError::UnexpectedImageLayout { allowed: ImageLayout::PresentSrc, - requested: layout, + requested: expected_layout, }, )); } - Ok(None) + Ok(()) } -} -unsafe impl<W> DeviceOwned for SwapchainAcquireFuture<W> { #[inline] - fn device(&self) -> &Arc<Device> { - &self.swapchain.device + 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<W> Drop for SwapchainAcquireFuture<W> { +impl Drop for SwapchainAcquireFuture { fn drop(&mut self) { - if let Some(ref fence) = self.fence { + if thread::panicking() { + return; + } + + if let Some(fence) = &self.fence { fence.wait(None).unwrap(); // TODO: handle error? self.semaphore = None; } @@ -1267,168 +1758,195 @@ impl<W> Drop for SwapchainAcquireFuture<W> { } } -/// Error that can happen when calling `Swapchain::acquire_fullscreen_exclusive` or `Swapchain::release_fullscreen_exclusive` +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 FullscreenExclusiveError { +pub enum AcquireError { /// Not enough memory. OomError(OomError), - /// Operation could not be completed for driver specific reasons. - InitializationFailed, + /// 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, - /// Fullscreen exclusivity is already acquired. - DoubleAcquire, - - /// Fullscreen exclusivity is not current acquired. - DoubleRelease, + /// The swapchain has lost or doesn't have full-screen exclusivity possibly for + /// implementation-specific reasons outside of the application’s control. + FullScreenExclusiveModeLost, - /// Swapchain is not in fullscreen exclusive app controlled mode - NotAppControlled, -} + /// 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, -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), - } - } -} + /// Error during fence creation. + FenceError(FenceError), -impl From<OomError> for FullscreenExclusiveError { - #[inline] - fn from(err: OomError) -> FullscreenExclusiveError { - FullscreenExclusiveError::OomError(err) - } + /// Error during semaphore creation. + SemaphoreError(SemaphoreError), } -impl error::Error for FullscreenExclusiveError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - FullscreenExclusiveError::OomError(ref err) => Some(err), +impl Error for AcquireError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + AcquireError::OomError(err) => Some(err), _ => None, } } } -impl fmt::Display for FullscreenExclusiveError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { +impl Display for AcquireError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { write!( - fmt, + 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 => - "fullscreen exclusivity is already acquired", - FullscreenExclusiveError::DoubleRelease => "fullscreen exclusivity is not acquired", - FullscreenExclusiveError::NotAppControlled => { - "swapchain is not in fullscreen exclusive app controlled mode" + 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 AcquireError { +pub enum PresentWaitError { /// 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 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 fullscreen exclusivity possibly for + /// The swapchain has lost or doesn't have full-screen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. - FullscreenExclusiveLost, + 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, + /// The timeout of the function has been reached before the present occured. + Timeout, - /// Error during semaphore creation - SemaphoreError(SemaphoreError), + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + + /// Present id of zero is invalid. + PresentIdZero, } -impl error::Error for AcquireError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - AcquireError::OomError(ref err) => Some(err), +impl Error for PresentWaitError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(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 fullscreen exclusivity" - } - AcquireError::SemaphoreError(_) => "error creating semaphore", +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<SemaphoreError> for AcquireError { - fn from(err: SemaphoreError) -> Self { - AcquireError::SemaphoreError(err) +impl From<OomError> for PresentWaitError { + fn from(err: OomError) -> PresentWaitError { + Self::OomError(err) } } -impl From<OomError> for AcquireError { - #[inline] - fn from(err: OomError) -> AcquireError { - AcquireError::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<Error> for AcquireError { - #[inline] - fn from(err: Error) -> AcquireError { +impl From<VulkanError> for PresentWaitError { + fn from(err: VulkanError) -> PresentWaitError { 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, + 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), } } @@ -1436,15 +1954,13 @@ impl From<Error> for AcquireError { /// 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, W> +pub struct PresentFuture<P> where P: GpuFuture, { previous: P, queue: Arc<Queue>, - swapchain: Arc<Swapchain<W>>, - image_id: usize, - present_region: Option<PresentRegion>, + swapchain_info: SwapchainPresentInfo, // True if `flush()` has been called on the future, which means that the present command has // been submitted. flushed: AtomicBool, @@ -1453,174 +1969,180 @@ where finished: AtomicBool, } -impl<P, W> PresentFuture<P, W> +impl<P> PresentFuture<P> where P: GpuFuture, { /// Returns the index of the image in the list of images returned when creating the swapchain. - #[inline] - pub fn image_id(&self) -> usize { - self.image_id + pub fn image_id(&self) -> u32 { + self.swapchain_info.image_index } /// Returns the corresponding swapchain. - #[inline] - pub fn swapchain(&self) -> &Arc<Swapchain<W>> { - &self.swapchain + pub fn swapchain(&self) -> &Arc<Swapchain> { + &self.swapchain_info.swapchain } } -unsafe impl<P, W> GpuFuture for PresentFuture<P, W> +unsafe impl<P> GpuFuture for PresentFuture<P> 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()); + 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 => { - 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::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(cb) => { + 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()?; - 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(PresentInfo { + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }) } - SubmitAnyBuilder::BindSparse(cb) => { + 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()?; - 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(PresentInfo { + swapchain_infos: vec![self.swapchain_info.clone()], + ..Default::default() + }) } - SubmitAnyBuilder::QueuePresent(present) => { - unimplemented!() // TODO: - /*present.submit(); - let mut builder = SubmitPresentBuilder::new(); - builder.add_swapchain(self.command_buffer.inner(), self.image_id); - SubmitAnyBuilder::CommandBuffer(builder)*/ + SubmitAnyBuilder::QueuePresent(mut present_info) => { + present_info + .swapchain_infos + .push(self.swapchain_info.clone()); + + SubmitAnyBuilder::QueuePresent(present_info) } }) } - #[inline] fn flush(&self) -> Result<(), FlushError> { unsafe { // If `flushed` already contains `true`, then `build_submission` will return `Empty`. let build_submission_result = self.build_submission(); - - if let &Err(FlushError::FullscreenExclusiveLost) = &build_submission_result { - self.swapchain - .fullscreen_exclusive_held - .store(false, Ordering::SeqCst); - } + self.flushed.store(true, 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 - .fullscreen_exclusive_held - .store(false, Ordering::SeqCst); + 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()), } - present_result?; + Ok(self + .queue + .with(|mut q| q.present_unchecked(present_info))? + .map(|r| r.map(|_| ())) + .fold(Ok(()), Result::and)?) } _ => 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.is_same(&self.queue), + Some(q) => q == self.queue, }); Some(self.queue.clone()) } - #[inline] fn check_buffer_access( &self, - buffer: &dyn BufferAccess, + buffer: &Buffer, + range: Range<DeviceSize>, exclusive: bool, queue: &Queue, - ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { - self.previous.check_buffer_access(buffer, exclusive, queue) + ) -> Result<(), AccessCheckError> { + self.previous + .check_buffer_access(buffer, range, exclusive, queue) } - #[inline] fn check_image_access( &self, - image: &dyn ImageAccess, - layout: ImageLayout, + image: &Image, + range: Range<DeviceSize>, exclusive: bool, + expected_layout: ImageLayout, 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() { + ) -> 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 @@ -1628,46 +2150,67 @@ where Err(AccessCheckError::Unknown) } else { self.previous - .check_image_access(image, layout, exclusive, queue) + .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, W> DeviceOwned for PresentFuture<P, W> +unsafe impl<P> DeviceOwned for PresentFuture<P> where P: GpuFuture, { - #[inline] fn device(&self) -> &Arc<Device> { self.queue.device() } } -impl<P, W> Drop for PresentFuture<P, W> +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() { - match self.flush() { - Ok(()) => { - // Block until the queue finished. - self.queue().unwrap().wait().unwrap(); - self.previous.signal_finished(); - } - Err(_) => { - // In case of error we simply do nothing, as there's nothing to do - // anyway. - } - } + // Block until the queue finished. + self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap(); + self.previous.signal_finished(); } } } } pub struct AcquiredImage { - pub id: usize, + pub image_index: u32, pub suboptimal: bool, } @@ -1678,8 +2221,8 @@ pub struct AcquiredImage { /// - 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<W>( - swapchain: &Swapchain<W>, +pub unsafe fn acquire_next_image_raw( + swapchain: &Swapchain, timeout: Option<Duration>, semaphore: Option<&Semaphore>, fence: Option<&Fence>, @@ -1696,29 +2239,37 @@ pub unsafe fn acquire_next_image_raw<W>( }; let mut out = MaybeUninit::uninit(); - let r = check_errors( - fns.khr_swapchain.acquire_next_image_khr( - swapchain.device.internal_object(), - swapchain.swapchain, - 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), + 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()), }; - Ok(AcquiredImage { id, suboptimal }) + 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, + }) } |