diff options
Diffstat (limited to 'src/swapchain/swapchain.rs')
-rw-r--r-- | src/swapchain/swapchain.rs | 1724 |
1 files changed, 1724 insertions, 0 deletions
diff --git a/src/swapchain/swapchain.rs b/src/swapchain/swapchain.rs new file mode 100644 index 0000000..3eb9051 --- /dev/null +++ b/src/swapchain/swapchain.rs @@ -0,0 +1,1724 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use 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(), +} + +impl From<FullscreenExclusive> for ash::vk::FullScreenExclusiveEXT { + #[inline] + fn from(val: FullscreenExclusive) -> Self { + Self::from_raw(val as i32) + } +} + +/// 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())?; + + 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); + } + + let acquire_result = + unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) }; + + if let &Err(AcquireError::FullscreenExclusiveLost) = &acquire_result { + swapchain + .fullscreen_exclusive_held + .store(false, Ordering::SeqCst); + } + + acquire_result? + }; + + Ok(( + id, + suboptimal, + SwapchainAcquireFuture { + swapchain, + semaphore: Some(semaphore), + fence: Some(fence), + image_id: id, + finished: AtomicBool::new(false), + }, + )) +} + +/// 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()); + + // 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, + image_id: index, + present_region: None, + flushed: AtomicBool::new(false), + finished: AtomicBool::new(false), + } +} + +/// 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()); + + // 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, + image_id: index, + present_region: Some(present_region), + flushed: AtomicBool::new(false), + finished: AtomicBool::new(false), + } +} + +/// 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, + + // 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. + 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, +} + +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, +} + +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, + + 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, + + old_swapchain: None, + } + } + + /// 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`. + #[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, + composite_alpha: self.composite_alpha, + present_mode: self.present_mode, + fullscreen_exclusive: self.fullscreen_exclusive, + clipped: self.clipped, + + old_swapchain: Some(self.clone()), + } + } + + /// Returns the saved Surface, from the Swapchain creation. + #[inline] + pub fn surface(&self) -> &Arc<Surface<W>> { + &self.surface + } + + /// Returns of the images that belong to this 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, + }) + } + + /// Returns the number of images of the swapchain. + #[inline] + pub fn num_images(&self) -> u32 { + self.images.len() as u32 + } + + /// Returns the format of the images of the swapchain. + #[inline] + pub fn format(&self) -> Format { + self.format + } + + /// Returns the dimensions of the images of the swapchain. + #[inline] + pub fn dimensions(&self) -> [u32; 2] { + self.dimensions + } + + /// Returns the number of layers of the images of the swapchain. + #[inline] + pub fn layers(&self) -> u32 { + self.layers + } + + /// Returns the transform that was passed when creating the swapchain. + #[inline] + pub fn transform(&self) -> SurfaceTransform { + self.transform + } + + /// Returns the alpha mode that was passed when creating the swapchain. + #[inline] + pub fn composite_alpha(&self) -> CompositeAlpha { + self.composite_alpha + } + + /// Returns the present mode that was passed when creating the swapchain. + #[inline] + pub fn present_mode(&self) -> PresentMode { + self.present_mode + } + + /// Returns the value of `clipped` that was passed when creating the swapchain. + #[inline] + pub fn clipped(&self) -> bool { + self.clipped + } + + /// Returns the value of 'fullscreen_exclusive` that was passed when creating the swapchain. + #[inline] + pub fn fullscreen_exclusive(&self) -> FullscreenExclusive { + self.fullscreen_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); + } + + if self.fullscreen_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, + ), + )?; + } + + 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); + } + + if !self.fullscreen_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, + ), + )?; + } + + Ok(()) + } + + /// `FullscreenExclusive::AppControlled` is not the active fullscreen 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 currently acquired. + pub fn is_fullscreen_exclusive(&self) -> bool { + if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { + false + } else { + self.fullscreen_exclusive_held.load(Ordering::SeqCst) + } + } + + // This method is necessary to allow `SwapchainImage`s to signal when they have been + // transitioned out of their initial `undefined` image layout. + // + // See the `ImageAccess::layout_initialized` method documentation for more details. + pub(crate) fn image_layout_initialized(&self, image_offset: usize) { + let image_entry = self.images.get(image_offset); + if let Some(ref image_entry) = image_entry { + image_entry.undefined_layout.store(false, Ordering::SeqCst); + } + } + + pub(crate) fn is_image_layout_initialized(&self, image_offset: usize) -> bool { + let image_entry = self.images.get(image_offset); + if let Some(ref image_entry) = image_entry { + !image_entry.undefined_layout.load(Ordering::SeqCst) + } else { + false + } + } +} + +unsafe impl<W> VulkanObject for Swapchain<W> { + type Object = ash::vk::SwapchainKHR; + + #[inline] + fn internal_object(&self) -> ash::vk::SwapchainKHR { + self.swapchain + } +} + +unsafe impl<W> DeviceOwned for Swapchain<W> { + fn device(&self) -> &Arc<Device> { + &self.device + } +} + +impl<W> fmt::Debug for Swapchain<W> { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "<Vulkan swapchain {:?}>", self.swapchain) + } +} + +impl<W> Drop for Swapchain<W> { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + fns.khr_swapchain.destroy_swapchain_khr( + self.device.internal_object(), + self.swapchain, + ptr::null(), + ); + self.surface.flag().store(false, Ordering::Release); + } + } +} + +/// 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<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 { + device, + surface, + old_swapchain, + + num_images, + format, + color_space, + dimensions, + layers, + usage, + sharing_mode, + transform, + composite_alpha, + present_mode, + fullscreen_exclusive, + clipped, + } = 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); + } + } + + 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); + } + } + }; + + 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); + } + + 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); + } + } + + // 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); + } + } + + if !device.enabled_extensions().khr_swapchain { + return Err(SwapchainCreationError::MissingExtensionKHRSwapchain); + } + + 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() + }); + } + + let p_next = match surface_full_screen_exclusive_info.as_ref() { + Some(some) => unsafe { mem::transmute(some as *const _) }, + None => ptr::null(), + }; + + // Required by the specs. + assert_ne!(usage, ImageUsage::none()); + + if let Some(ref old_swapchain) = old_swapchain { + let mut stale = old_swapchain.stale.lock().unwrap(); + + // 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; + } + } + + let fns = device.fns(); + + 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(), + ), + }; + + 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() + }; + + 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() + }; + + 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(), + ))?; + + 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 + }; + + 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); + + 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, + }); + + 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 + }; + + Ok((swapchain, swapchain_images)) + } + + /// Sets the number of images that will be created. + /// + /// The default is 2. + #[inline] + pub fn num_images(mut self, num_images: u32) -> Self { + self.num_images = num_images; + self + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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. + /// + /// The default is `true`. + #[inline] + pub fn clipped(mut self, clipped: bool) -> Self { + self.clipped = clipped; + self + } +} + +/// Error that can happen when creation a swapchain. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SwapchainCreationError { + /// Not enough memory. + OomError(OomError), + /// The device was lost. + DeviceLost, + /// The surface was lost. + SurfaceLost, + /// The surface is already used by another swapchain. + SurfaceInUse, + /// The window is already in use by another API. + NativeWindowInUse, + /// 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, +} + +impl error::Error for SwapchainCreationError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + SwapchainCreationError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for SwapchainCreationError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + 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" + } + SwapchainCreationError::UnsupportedPresentMode => { + "the requested present mode is not supported by the surface" + } + SwapchainCreationError::UnsupportedImageConfiguration => { + "the requested image configuration is not supported by the physical device" + } + } + ) + } +} + +impl From<Error> for SwapchainCreationError { + #[inline] + fn from(err: Error) -> SwapchainCreationError { + 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, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +impl From<OomError> for SwapchainCreationError { + #[inline] + fn from(err: OomError) -> SwapchainCreationError { + SwapchainCreationError::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, + } + } +} + +/// 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, + // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already + // happened. + semaphore: Option<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> { + /// 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 + } + + /// Returns the corresponding swapchain. + #[inline] + pub fn swapchain(&self) -> &Arc<Swapchain<W>> { + &self.swapchain + } +} + +unsafe impl<W> GpuFuture for SwapchainAcquireFuture<W> { + #[inline] + fn cleanup_finished(&mut self) {} + + #[inline] + unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> { + if let Some(ref semaphore) = self.semaphore { + let mut sem = SubmitSemaphoresWaitBuilder::new(); + sem.add_wait_semaphore(&semaphore); + Ok(SubmitAnyBuilder::SemaphoresWait(sem)) + } else { + Ok(SubmitAnyBuilder::Empty) + } + } + + #[inline] + fn flush(&self) -> Result<(), FlushError> { + Ok(()) + } + + #[inline] + unsafe fn signal_finished(&self) { + self.finished.store(true, Ordering::SeqCst); + } + + #[inline] + fn queue_change_allowed(&self) -> bool { + true + } + + #[inline] + fn queue(&self) -> Option<Arc<Queue>> { + None + } + + #[inline] + fn check_buffer_access( + &self, + _: &dyn BufferAccess, + _: bool, + _: &Queue, + ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { + Err(AccessCheckError::Unknown) + } + + #[inline] + fn check_image_access( + &self, + image: &dyn ImageAccess, + layout: ImageLayout, + _: bool, + _: &Queue, + ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { + let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap(); + if swapchain_image.image.internal_object() != image.inner().image.internal_object() { + return Err(AccessCheckError::Unknown); + } + + if self.swapchain.images[self.image_id] + .undefined_layout + .load(Ordering::Relaxed) + && layout != ImageLayout::Undefined + { + return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { + requested: layout, + })); + } + + if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc { + return Err(AccessCheckError::Denied( + AccessError::UnexpectedImageLayout { + allowed: ImageLayout::PresentSrc, + requested: layout, + }, + )); + } + + Ok(None) + } +} + +unsafe impl<W> DeviceOwned for SwapchainAcquireFuture<W> { + #[inline] + fn device(&self) -> &Arc<Device> { + &self.swapchain.device + } +} + +impl<W> Drop for SwapchainAcquireFuture<W> { + fn drop(&mut self) { + if let Some(ref fence) = self.fence { + fence.wait(None).unwrap(); // TODO: handle error? + self.semaphore = None; + } + + // TODO: if this future is destroyed without being presented, then eventually acquiring + // a new image will block forever ; difficulty: hard + } +} + +/// Error that can happen when calling `Swapchain::acquire_fullscreen_exclusive` or `Swapchain::release_fullscreen_exclusive` +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum FullscreenExclusiveError { + /// Not enough memory. + OomError(OomError), + + /// Operation could not be completed for driver specific reasons. + InitializationFailed, + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, + + /// Fullscreen exclusivity is already acquired. + DoubleAcquire, + + /// Fullscreen exclusivity is not current acquired. + DoubleRelease, + + /// Swapchain is not in fullscreen exclusive app controlled mode + NotAppControlled, +} + +impl From<Error> for FullscreenExclusiveError { + #[inline] + fn from(err: Error) -> FullscreenExclusiveError { + match err { + err @ Error::OutOfHostMemory => FullscreenExclusiveError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => { + FullscreenExclusiveError::OomError(OomError::from(err)) + } + Error::SurfaceLost => FullscreenExclusiveError::SurfaceLost, + Error::InitializationFailed => FullscreenExclusiveError::InitializationFailed, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +impl From<OomError> for FullscreenExclusiveError { + #[inline] + fn from(err: OomError) -> FullscreenExclusiveError { + FullscreenExclusiveError::OomError(err) + } +} + +impl error::Error for FullscreenExclusiveError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + FullscreenExclusiveError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for FullscreenExclusiveError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + FullscreenExclusiveError::OomError(_) => "not enough memory", + FullscreenExclusiveError::SurfaceLost => { + "the surface of this swapchain is no longer valid" + } + FullscreenExclusiveError::InitializationFailed => { + "operation could not be completed for driver specific reasons" + } + FullscreenExclusiveError::DoubleAcquire => + "fullscreen exclusivity is already acquired", + FullscreenExclusiveError::DoubleRelease => "fullscreen exclusivity is not acquired", + FullscreenExclusiveError::NotAppControlled => { + "swapchain is not in fullscreen exclusive app controlled mode" + } + } + ) + } +} + +/// Error that can happen when calling `acquire_next_image`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum AcquireError { + /// Not enough memory. + OomError(OomError), + + /// The connection to the device has been lost. + DeviceLost, + + /// The timeout of the function has been reached before an image was available. + Timeout, + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, + + /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for + /// implementation-specific reasons outside of the application’s control. + FullscreenExclusiveLost, + + /// The surface has changed in a way that makes the swapchain unusable. You must query the + /// surface's new properties and recreate a new swapchain if you want to continue drawing. + OutOfDate, + + /// Error during semaphore creation + SemaphoreError(SemaphoreError), +} + +impl error::Error for AcquireError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + AcquireError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for AcquireError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + AcquireError::OomError(_) => "not enough memory", + AcquireError::DeviceLost => "the connection to the device has been lost", + AcquireError::Timeout => "no image is available for acquiring yet", + AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid", + AcquireError::OutOfDate => "the swapchain needs to be recreated", + AcquireError::FullscreenExclusiveLost => { + "the swapchain no longer has fullscreen exclusivity" + } + AcquireError::SemaphoreError(_) => "error creating semaphore", + } + ) + } +} + +impl From<SemaphoreError> for AcquireError { + fn from(err: SemaphoreError) -> Self { + AcquireError::SemaphoreError(err) + } +} + +impl From<OomError> for AcquireError { + #[inline] + fn from(err: OomError) -> AcquireError { + AcquireError::OomError(err) + } +} + +impl From<Error> for AcquireError { + #[inline] + fn from(err: Error) -> AcquireError { + match err { + err @ Error::OutOfHostMemory => AcquireError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)), + Error::DeviceLost => AcquireError::DeviceLost, + Error::SurfaceLost => AcquireError::SurfaceLost, + Error::OutOfDate => AcquireError::OutOfDate, + Error::FullscreenExclusiveLost => AcquireError::FullscreenExclusiveLost, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +/// 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> +where + P: GpuFuture, +{ + previous: P, + queue: Arc<Queue>, + swapchain: Arc<Swapchain<W>>, + image_id: usize, + present_region: Option<PresentRegion>, + // True if `flush()` has been called on the future, which means that the present command has + // been submitted. + flushed: AtomicBool, + // True if `signal_finished()` has been called on the future, which means that the future has + // been submitted and has already been processed by the GPU. + finished: AtomicBool, +} + +impl<P, W> PresentFuture<P, W> +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 + } + + /// Returns the corresponding swapchain. + #[inline] + pub fn swapchain(&self) -> &Arc<Swapchain<W>> { + &self.swapchain + } +} + +unsafe impl<P, W> GpuFuture for PresentFuture<P, W> +where + P: GpuFuture, +{ + #[inline] + fn cleanup_finished(&mut self) { + self.previous.cleanup_finished(); + } + + #[inline] + unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> { + if self.flushed.load(Ordering::SeqCst) { + return Ok(SubmitAnyBuilder::Empty); + } + + let queue = self.previous.queue().map(|q| q.clone()); + + // 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::CommandBuffer(cb) => { + // 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::BindSparse(cb) => { + // 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(present) => { + unimplemented!() // TODO: + /*present.submit(); + let mut builder = SubmitPresentBuilder::new(); + builder.add_swapchain(self.command_buffer.inner(), self.image_id); + SubmitAnyBuilder::CommandBuffer(builder)*/ + } + }) + } + + #[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); + } + + 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); + } + + present_result?; + } + _ => unreachable!(), + } + + self.flushed.store(true, Ordering::SeqCst); + Ok(()) + } + } + + #[inline] + unsafe fn signal_finished(&self) { + self.flushed.store(true, Ordering::SeqCst); + self.finished.store(true, Ordering::SeqCst); + self.previous.signal_finished(); + } + + #[inline] + fn queue_change_allowed(&self) -> bool { + false + } + + #[inline] + fn queue(&self) -> Option<Arc<Queue>> { + debug_assert!(match self.previous.queue() { + None => true, + Some(q) => q.is_same(&self.queue), + }); + + Some(self.queue.clone()) + } + + #[inline] + fn check_buffer_access( + &self, + buffer: &dyn BufferAccess, + exclusive: bool, + queue: &Queue, + ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { + self.previous.check_buffer_access(buffer, exclusive, queue) + } + + #[inline] + fn check_image_access( + &self, + image: &dyn ImageAccess, + layout: ImageLayout, + exclusive: bool, + queue: &Queue, + ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { + let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap(); + if swapchain_image.image.internal_object() == image.inner().image.internal_object() { + // This future presents the swapchain image, which "unlocks" it. Therefore any attempt + // to use this swapchain image afterwards shouldn't get granted automatic access. + // Instead any attempt to access the image afterwards should get an authorization from + // a later swapchain acquire future. Hence why we return `Unknown` here. + Err(AccessCheckError::Unknown) + } else { + self.previous + .check_image_access(image, layout, exclusive, queue) + } + } +} + +unsafe impl<P, W> DeviceOwned for PresentFuture<P, W> +where + P: GpuFuture, +{ + #[inline] + fn device(&self) -> &Arc<Device> { + self.queue.device() + } +} + +impl<P, W> Drop for PresentFuture<P, W> +where + P: GpuFuture, +{ + fn drop(&mut self) { + unsafe { + if !*self.finished.get_mut() { + match self.flush() { + Ok(()) => { + // 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. + } + } + } + } + } +} + +pub struct AcquiredImage { + pub id: usize, + pub suboptimal: bool, +} + +/// Unsafe variant of `acquire_next_image`. +/// +/// # Safety +/// +/// - The semaphore and/or the fence must be kept alive until it is signaled. +/// - The swapchain must not have been replaced by being passed as the old swapchain when creating +/// a new one. +pub unsafe fn acquire_next_image_raw<W>( + swapchain: &Swapchain<W>, + timeout: Option<Duration>, + semaphore: Option<&Semaphore>, + fence: Option<&Fence>, +) -> Result<AcquiredImage, AcquireError> { + let fns = swapchain.device.fns(); + + let timeout_ns = if let Some(timeout) = timeout { + timeout + .as_secs() + .saturating_mul(1_000_000_000) + .saturating_add(timeout.subsec_nanos() as u64) + } else { + u64::MAX + }; + + let mut out = MaybeUninit::uninit(); + let r = check_errors( + fns.khr_swapchain.acquire_next_image_khr( + swapchain.device.internal_object(), + swapchain.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), + }; + + Ok(AcquiredImage { id, suboptimal }) +} |