// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use super::{ ColorSpace, CompositeAlpha, CompositeAlphas, PresentMode, Surface, SurfaceTransform, SurfaceTransforms, SwapchainPresentInfo, }; use crate::{ buffer::Buffer, device::{Device, DeviceOwned, Queue}, format::Format, image::{ sys::Image, ImageFormatInfo, ImageLayout, ImageTiling, ImageType, ImageUsage, SwapchainImage, }, macros::{impl_id_counter, vulkan_enum}, swapchain::{PresentInfo, SurfaceApi, SurfaceInfo, SurfaceSwapchainLock}, sync::{ fence::{Fence, FenceError}, future::{AccessCheckError, AccessError, FlushError, GpuFuture, SubmitAnyBuilder}, semaphore::{Semaphore, SemaphoreError}, Sharing, }, DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject, }; use parking_lot::Mutex; use smallvec::{smallvec, SmallVec}; use std::{ error::Error, fmt::{Debug, Display, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ops::Range, ptr, sync::{ atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, thread, time::Duration, }; /// Contains the swapping system and the images that can be shown on a surface. pub struct Swapchain { handle: ash::vk::SwapchainKHR, device: Arc, surface: Arc, 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>, pre_transform: SurfaceTransform, composite_alpha: CompositeAlpha, present_mode: PresentMode, clipped: bool, full_screen_exclusive: FullScreenExclusive, win32_monitor: Option, prev_present_id: AtomicU64, // Whether full-screen exclusive is currently held. full_screen_exclusive_held: AtomicBool, // The images of this swapchain. images: Vec, // 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, } #[derive(Debug)] struct ImageEntry { handle: ash::vk::Image, layout_initialized: AtomicBool, } impl Swapchain { /// Creates a new `Swapchain`. /// /// This function returns the swapchain plus a list of the images that belong to the /// swapchain. The order in which the images are returned is important for the /// `acquire_next_image` and `present` functions. /// /// # Panics /// /// - Panics if the device and the surface don't belong to the same instance. /// - Panics if `create_info.usage` is empty. /// // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? pub fn new( device: Arc, surface: Arc, mut create_info: SwapchainCreateInfo, ) -> Result<(Arc, Vec>), SwapchainCreationError> { Self::validate(&device, &surface, &mut create_info)?; // Checking that the surface doesn't already have a swapchain. if surface.flag().swap(true, Ordering::AcqRel) { return Err(SwapchainCreationError::SurfaceInUse); } let (handle, image_handles) = unsafe { Self::create(&device, &surface, &create_info, None)? }; let SwapchainCreateInfo { min_image_count, image_format, image_color_space, image_extent, image_array_layers, image_usage, image_sharing, pre_transform, composite_alpha, present_mode, clipped, full_screen_exclusive, win32_monitor, _ne: _, } = create_info; let swapchain = Arc::new(Swapchain { handle, device, surface, id: Self::next_id(), min_image_count, image_format: image_format.unwrap(), image_color_space, image_extent, image_array_layers, image_usage, image_sharing, pre_transform, composite_alpha, present_mode, clipped, full_screen_exclusive, win32_monitor, prev_present_id: Default::default(), full_screen_exclusive_held: AtomicBool::new(false), images: image_handles .iter() .map(|&handle| ImageEntry { handle, layout_initialized: AtomicBool::new(false), }) .collect(), retired: Mutex::new(false), }); let swapchain_images = image_handles .into_iter() .enumerate() .map(|(image_index, handle)| unsafe { SwapchainImage::from_handle(handle, swapchain.clone(), image_index as u32) }) .collect::>()?; 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, mut create_info: SwapchainCreateInfo, ) -> Result<(Arc, Vec>), SwapchainCreationError> { Self::validate(&self.device, &self.surface, &mut create_info)?; { let mut retired = self.retired.lock(); // The swapchain has already been used to create a new one. if *retired { return Err(SwapchainCreationError::SwapchainAlreadyRetired); } else { // According to the documentation of VkSwapchainCreateInfoKHR: // // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE, // > any images not acquired by the application may be freed by the implementation, // > which may occur even if creation of the new swapchain fails. // // Therefore, we set retired to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails. *retired = true; } } let (handle, image_handles) = unsafe { Self::create(&self.device, &self.surface, &create_info, Some(self))? }; let full_screen_exclusive_held = if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { false } else { self.full_screen_exclusive_held.load(Ordering::SeqCst) }; let SwapchainCreateInfo { min_image_count, image_format, image_color_space, image_extent, image_array_layers, image_usage, image_sharing, pre_transform, composite_alpha, present_mode, clipped, full_screen_exclusive, win32_monitor, _ne: _, } = create_info; let swapchain = Arc::new(Swapchain { handle, device: self.device.clone(), surface: self.surface.clone(), id: Self::next_id(), min_image_count, image_format: image_format.unwrap(), image_color_space, image_extent, image_array_layers, image_usage, image_sharing, pre_transform, composite_alpha, present_mode, clipped, full_screen_exclusive, win32_monitor, prev_present_id: Default::default(), full_screen_exclusive_held: AtomicBool::new(full_screen_exclusive_held), images: image_handles .iter() .map(|&handle| ImageEntry { handle, layout_initialized: AtomicBool::new(false), }) .collect(), retired: Mutex::new(false), }); let swapchain_images = image_handles .into_iter() .enumerate() .map(|(image_index, handle)| unsafe { SwapchainImage::from_handle(handle, swapchain.clone(), image_index as u32) }) .collect::>()?; Ok((swapchain, swapchain_images)) } fn validate( device: &Device, surface: &Surface, create_info: &mut SwapchainCreateInfo, ) -> Result<(), SwapchainCreationError> { let &mut SwapchainCreateInfo { min_image_count, ref mut image_format, image_color_space, ref mut image_extent, image_array_layers, image_usage, ref mut image_sharing, pre_transform, composite_alpha, present_mode, clipped: _, full_screen_exclusive, win32_monitor, _ne: _, } = create_info; if !device.enabled_extensions().khr_swapchain { return Err(SwapchainCreationError::RequirementNotMet { required_for: "`Swapchain::new`", requires_one_of: RequiresOneOf { device_extensions: &["khr_swapchain"], ..Default::default() }, }); } assert_eq!(device.instance(), surface.instance()); // VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter image_color_space.validate_device(device)?; // VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter image_usage.validate_device(device)?; // VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask assert!(!image_usage.is_empty()); // VUID-VkSwapchainCreateInfoKHR-preTransform-parameter pre_transform.validate_device(device)?; // VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter composite_alpha.validate_device(device)?; // VUID-VkSwapchainCreateInfoKHR-presentMode-parameter present_mode.validate_device(device)?; if full_screen_exclusive != FullScreenExclusive::Default { if !device.enabled_extensions().ext_full_screen_exclusive { return Err(SwapchainCreationError::RequirementNotMet { required_for: "`create_info.full_screen_exclusive` is not \ `FullScreenExclusive::Default`", requires_one_of: RequiresOneOf { device_extensions: &["ext_full_screen_exclusive"], ..Default::default() }, }); } // VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter full_screen_exclusive.validate_device(device)?; } if surface.api() == SurfaceApi::Win32 && full_screen_exclusive == FullScreenExclusive::ApplicationControlled { if win32_monitor.is_none() { return Err(SwapchainCreationError::Win32MonitorInvalid); } } else { if win32_monitor.is_some() { return Err(SwapchainCreationError::Win32MonitorInvalid); } } // VUID-VkSwapchainCreateInfoKHR-surface-01270 if !device .active_queue_family_indices() .iter() .copied() .any(|index| unsafe { // Use unchecked, because all validation has been done above. device .physical_device() .surface_support_unchecked(index, surface) .unwrap_or_default() }) { return Err(SwapchainCreationError::SurfaceNotSupported); } *image_format = Some({ // Use unchecked, because all validation has been done above. let surface_formats = unsafe { device.physical_device().surface_formats_unchecked( surface, SurfaceInfo { full_screen_exclusive, win32_monitor, ..Default::default() }, )? }; if let Some(format) = image_format { // VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter format.validate_device(device)?; // VUID-VkSwapchainCreateInfoKHR-imageFormat-01273 if !surface_formats .into_iter() .any(|(f, c)| f == *format && c == image_color_space) { return Err(SwapchainCreationError::FormatColorSpaceNotSupported); } *format } else { surface_formats .into_iter() .find_map(|(f, c)| { (c == image_color_space && [Format::R8G8B8A8_UNORM, Format::B8G8R8A8_UNORM].contains(&f)) .then_some(f) }) .ok_or(SwapchainCreationError::FormatColorSpaceNotSupported)? } }); // Use unchecked, because all validation has been done above. let surface_capabilities = unsafe { device.physical_device().surface_capabilities_unchecked( surface, SurfaceInfo { full_screen_exclusive, win32_monitor, ..Default::default() }, )? }; // VUID-VkSwapchainCreateInfoKHR-minImageCount-01272 // VUID-VkSwapchainCreateInfoKHR-presentMode-02839 if min_image_count < surface_capabilities.min_image_count || surface_capabilities .max_image_count .map_or(false, |c| min_image_count > c) { return Err(SwapchainCreationError::MinImageCountNotSupported { provided: min_image_count, min_supported: surface_capabilities.min_image_count, max_supported: surface_capabilities.max_image_count, }); } if image_extent[0] == 0 || image_extent[1] == 0 { *image_extent = surface_capabilities.current_extent.unwrap(); } // VUID-VkSwapchainCreateInfoKHR-imageExtent-01274 if image_extent[0] < surface_capabilities.min_image_extent[0] || image_extent[1] < surface_capabilities.min_image_extent[1] || image_extent[0] > surface_capabilities.max_image_extent[0] || image_extent[1] > surface_capabilities.max_image_extent[1] { return Err(SwapchainCreationError::ImageExtentNotSupported { provided: *image_extent, min_supported: surface_capabilities.min_image_extent, max_supported: surface_capabilities.max_image_extent, }); } // VUID-VkSwapchainCreateInfoKHR-imageExtent-01689 // On some platforms, dimensions of zero-length can occur by minimizing the surface. if image_extent.contains(&0) { return Err(SwapchainCreationError::ImageExtentZeroLengthDimensions); } // VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275 if image_array_layers == 0 || image_array_layers > surface_capabilities.max_image_array_layers { return Err(SwapchainCreationError::ImageArrayLayersNotSupported { provided: image_array_layers, max_supported: surface_capabilities.max_image_array_layers, }); } // VUID-VkSwapchainCreateInfoKHR-presentMode-01427 if (ash::vk::ImageUsageFlags::from(image_usage) & ash::vk::ImageUsageFlags::from(surface_capabilities.supported_usage_flags)) != ash::vk::ImageUsageFlags::from(image_usage) { return Err(SwapchainCreationError::ImageUsageNotSupported { provided: image_usage, supported: surface_capabilities.supported_usage_flags, }); } match image_sharing { Sharing::Exclusive => (), Sharing::Concurrent(queue_family_indices) => { // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278 // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428 queue_family_indices.sort_unstable(); queue_family_indices.dedup(); assert!(queue_family_indices.len() >= 2); for &queue_family_index in queue_family_indices.iter() { // VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428 if queue_family_index >= device.physical_device().queue_family_properties().len() as u32 { return Err( SwapchainCreationError::ImageSharingQueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: device .physical_device() .queue_family_properties() .len() as u32, }, ); } } } }; // VUID-VkSwapchainCreateInfoKHR-preTransform-01279 if !surface_capabilities .supported_transforms .contains_enum(pre_transform) { return Err(SwapchainCreationError::PreTransformNotSupported { provided: pre_transform, supported: surface_capabilities.supported_transforms, }); } // VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280 if !surface_capabilities .supported_composite_alpha .contains_enum(composite_alpha) { return Err(SwapchainCreationError::CompositeAlphaNotSupported { provided: composite_alpha, supported: surface_capabilities.supported_composite_alpha, }); } // VUID-VkSwapchainCreateInfoKHR-presentMode-01281 // Use unchecked, because all validation has been done above. if !unsafe { device .physical_device() .surface_present_modes_unchecked(surface)? } .any(|mode| mode == present_mode) { return Err(SwapchainCreationError::PresentModeNotSupported); } // VUID-VkSwapchainCreateInfoKHR-imageFormat-01778 // Use unchecked, because all validation has been done above. if unsafe { device .physical_device() .image_format_properties_unchecked(ImageFormatInfo { format: *image_format, image_type: ImageType::Dim2d, tiling: ImageTiling::Optimal, usage: image_usage, ..Default::default() })? } .is_none() { return Err(SwapchainCreationError::ImageFormatPropertiesNotSupported); } Ok(()) } unsafe fn create( device: &Device, surface: &Surface, create_info: &SwapchainCreateInfo, old_swapchain: Option<&Swapchain>, ) -> Result<(ash::vk::SwapchainKHR, Vec), SwapchainCreationError> { let &SwapchainCreateInfo { min_image_count, image_format, image_color_space, image_extent, image_array_layers, image_usage, ref image_sharing, pre_transform, composite_alpha, present_mode, clipped, full_screen_exclusive, win32_monitor, _ne: _, } = create_info; let (image_sharing_mode, queue_family_index_count, p_queue_family_indices) = match image_sharing { Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()), Sharing::Concurrent(ref ids) => ( ash::vk::SharingMode::CONCURRENT, ids.len() as u32, ids.as_ptr(), ), }; let mut info_vk = ash::vk::SwapchainCreateInfoKHR { flags: ash::vk::SwapchainCreateFlagsKHR::empty(), surface: surface.handle(), min_image_count, image_format: image_format.unwrap().into(), image_color_space: image_color_space.into(), image_extent: ash::vk::Extent2D { width: image_extent[0], height: image_extent[1], }, image_array_layers, image_usage: image_usage.into(), image_sharing_mode, queue_family_index_count, p_queue_family_indices, pre_transform: pre_transform.into(), composite_alpha: composite_alpha.into(), present_mode: present_mode.into(), clipped: clipped as ash::vk::Bool32, old_swapchain: old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle), ..Default::default() }; let mut surface_full_screen_exclusive_info_vk = None; let mut surface_full_screen_exclusive_win32_info_vk = None; if full_screen_exclusive != FullScreenExclusive::Default { let next = surface_full_screen_exclusive_info_vk.insert( ash::vk::SurfaceFullScreenExclusiveInfoEXT { full_screen_exclusive: full_screen_exclusive.into(), ..Default::default() }, ); next.p_next = info_vk.p_next as *mut _; info_vk.p_next = next as *const _ as *const _; } if let Some(Win32Monitor(hmonitor)) = win32_monitor { let next = surface_full_screen_exclusive_win32_info_vk.insert( ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { hmonitor, ..Default::default() }, ); next.p_next = info_vk.p_next as *mut _; info_vk.p_next = next as *const _ as *const _; } let fns = device.fns(); let handle = { let mut output = MaybeUninit::uninit(); (fns.khr_swapchain.create_swapchain_khr)( device.handle(), &info_vk, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; let image_handles = loop { let mut count = 0; (fns.khr_swapchain.get_swapchain_images_khr)( device.handle(), handle, &mut count, ptr::null_mut(), ) .result() .map_err(VulkanError::from)?; let mut images = Vec::with_capacity(count as usize); let result = (fns.khr_swapchain.get_swapchain_images_khr)( device.handle(), handle, &mut count, images.as_mut_ptr(), ); match result { ash::vk::Result::SUCCESS => { images.set_len(count as usize); break images; } ash::vk::Result::INCOMPLETE => (), err => return Err(VulkanError::from(err).into()), } }; Ok((handle, image_handles)) } /// Returns the creation parameters of the swapchain. #[inline] pub fn create_info(&self) -> SwapchainCreateInfo { SwapchainCreateInfo { min_image_count: self.min_image_count, image_format: Some(self.image_format), image_color_space: self.image_color_space, image_extent: self.image_extent, image_array_layers: self.image_array_layers, image_usage: self.image_usage, image_sharing: self.image_sharing.clone(), pre_transform: self.pre_transform, composite_alpha: self.composite_alpha, present_mode: self.present_mode, clipped: self.clipped, full_screen_exclusive: self.full_screen_exclusive, win32_monitor: self.win32_monitor, _ne: crate::NonExhaustive(()), } } /// Returns the saved Surface, from the Swapchain creation. #[inline] pub fn surface(&self) -> &Arc { &self.surface } /// If `image` is one of the images of this swapchain, returns its index within the swapchain. #[inline] pub fn index_of_image(&self, image: &Image) -> Option { self.images .iter() .position(|entry| entry.handle == image.handle()) .map(|i| i as u32) } /// Returns the number of images of the swapchain. #[inline] pub fn image_count(&self) -> u32 { self.images.len() as u32 } /// Returns the format of the images of the swapchain. #[inline] pub fn image_format(&self) -> Format { self.image_format } /// Returns the color space of the images of the swapchain. #[inline] pub fn image_color_space(&self) -> ColorSpace { self.image_color_space } /// Returns the extent of the images of the swapchain. #[inline] pub fn image_extent(&self) -> [u32; 2] { self.image_extent } /// Returns the number of array layers of the images of the swapchain. #[inline] pub fn image_array_layers(&self) -> u32 { self.image_array_layers } /// Returns the usage of the images of the swapchain. #[inline] pub fn image_usage(&self) -> ImageUsage { self.image_usage } /// Returns the sharing of the images of the swapchain. #[inline] pub fn image_sharing(&self) -> &Sharing> { &self.image_sharing } #[inline] pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool { &self.full_screen_exclusive_held } #[inline] pub(crate) unsafe fn try_claim_present_id(&self, present_id: NonZeroU64) -> bool { let present_id = u64::from(present_id); self.prev_present_id.fetch_max(present_id, Ordering::SeqCst) < present_id } /// Returns the pre-transform that was passed when creating the swapchain. #[inline] pub fn pre_transform(&self) -> SurfaceTransform { self.pre_transform } /// Returns the alpha mode that was passed when creating the swapchain. #[inline] pub fn composite_alpha(&self) -> CompositeAlpha { self.composite_alpha } /// Returns the present mode that was passed when creating the swapchain. #[inline] pub fn present_mode(&self) -> PresentMode { self.present_mode } /// Returns the value of `clipped` that was passed when creating the swapchain. #[inline] pub fn clipped(&self) -> bool { self.clipped } /// Returns the value of 'full_screen_exclusive` that was passed when creating the swapchain. #[inline] pub fn full_screen_exclusive(&self) -> FullScreenExclusive { self.full_screen_exclusive } /// Acquires full-screen exclusivity. /// /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], /// and must not already hold full-screen exclusivity. Full-screen exclusivity is held until /// either the `release_full_screen_exclusive` is called, or if any of the the other `Swapchain` /// functions return `FullScreenExclusiveLost`. #[inline] pub fn acquire_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> { if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { return Err(FullScreenExclusiveError::NotApplicationControlled); } if self.full_screen_exclusive_held.swap(true, Ordering::SeqCst) { return Err(FullScreenExclusiveError::DoubleAcquire); } unsafe { let fns = self.device.fns(); (fns.ext_full_screen_exclusive .acquire_full_screen_exclusive_mode_ext)( self.device.handle(), self.handle ) .result() .map_err(VulkanError::from)?; } Ok(()) } /// Releases full-screen exclusivity. /// /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`], /// and must currently hold full-screen exclusivity. #[inline] pub fn release_full_screen_exclusive(&self) -> Result<(), FullScreenExclusiveError> { if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { return Err(FullScreenExclusiveError::NotApplicationControlled); } if !self .full_screen_exclusive_held .swap(false, Ordering::SeqCst) { return Err(FullScreenExclusiveError::DoubleRelease); } unsafe { let fns = self.device.fns(); (fns.ext_full_screen_exclusive .release_full_screen_exclusive_mode_ext)( self.device.handle(), self.handle ) .result() .map_err(VulkanError::from)?; } Ok(()) } /// `FullScreenExclusive::AppControlled` is not the active full-screen exclusivity mode, /// then this function will always return false. If true is returned the swapchain /// is in `FullScreenExclusive::AppControlled` full-screen exclusivity mode and exclusivity /// is currently acquired. #[inline] pub fn is_full_screen_exclusive(&self) -> bool { if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled { false } else { self.full_screen_exclusive_held.load(Ordering::SeqCst) } } // This method is necessary to allow `SwapchainImage`s to signal when they have been // transitioned out of their initial `undefined` image layout. // // See the `ImageAccess::layout_initialized` method documentation for more details. pub(crate) fn image_layout_initialized(&self, image_index: u32) { let image_entry = self.images.get(image_index as usize); if let Some(image_entry) = image_entry { image_entry .layout_initialized .store(true, Ordering::Relaxed); } } pub(crate) fn is_image_layout_initialized(&self, image_index: u32) -> bool { let image_entry = self.images.get(image_index as usize); if let Some(image_entry) = image_entry { image_entry.layout_initialized.load(Ordering::Relaxed) } else { false } } } impl Drop for Swapchain { #[inline] fn drop(&mut self) { unsafe { let fns = self.device.fns(); (fns.khr_swapchain.destroy_swapchain_khr)( self.device.handle(), self.handle, ptr::null(), ); self.surface.flag().store(false, Ordering::Release); } } } unsafe impl VulkanObject for Swapchain { type Handle = ash::vk::SwapchainKHR; #[inline] fn handle(&self) -> Self::Handle { self.handle } } unsafe impl DeviceOwned for Swapchain { #[inline] fn device(&self) -> &Arc { &self.device } } impl_id_counter!(Swapchain); impl Debug for Swapchain { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { let Self { handle, device, surface, id: _, min_image_count, image_format, image_color_space, image_extent, image_array_layers, image_usage, image_sharing, pre_transform, composite_alpha, present_mode, clipped, full_screen_exclusive, win32_monitor, prev_present_id, full_screen_exclusive_held, images, retired, } = self; f.debug_struct("Swapchain") .field("handle", &handle) .field("device", &device.handle()) .field("surface", &surface.handle()) .field("min_image_count", &min_image_count) .field("image_format", &image_format) .field("image_color_space", &image_color_space) .field("image_extent", &image_extent) .field("image_array_layers", &image_array_layers) .field("image_usage", &image_usage) .field("image_sharing", &image_sharing) .field("pre_transform", &pre_transform) .field("composite_alpha", &composite_alpha) .field("present_mode", &present_mode) .field("clipped", &clipped) .field("full_screen_exclusive", &full_screen_exclusive) .field("win32_monitor", &win32_monitor) .field("prev_present_id", &prev_present_id) .field("full_screen_exclusive_held", &full_screen_exclusive_held) .field("images", &images) .field("retired", &retired) .finish() } } /// Parameters to create a new `Swapchain`. /// /// Many of the values here must be supported by the physical device. /// [`PhysicalDevice`](crate::device::physical::PhysicalDevice) has several /// methods to query what is supported. #[derive(Clone, Debug)] pub struct SwapchainCreateInfo { /// The minimum number of images that will be created. /// /// The implementation is allowed to create more than this number, but never less. /// /// The default value is `2`. pub min_image_count: u32, /// The format of the created images. /// /// If set to `None`, [`Format::R8G8B8A8_UNORM`] or [`Format::B8G8R8A8_UNORM`] will be selected, /// based on which is supported by the surface. /// /// The default value is `None`. pub image_format: Option, /// The color space of the created images. /// /// The default value is [`ColorSpace::SrgbNonLinear`]. pub image_color_space: ColorSpace, /// The extent of the created images. /// /// If any of the values is 0, the value of /// [`SurfaceCapabilities::current_extent`](crate::swapchain::SurfaceCapabilities) will be used. /// /// The default value is `[0, 0]`. pub image_extent: [u32; 2], /// The number of array layers of the created images. /// /// The default value is `1`. pub image_array_layers: u32, /// How the created images will be used. /// /// The default value is [`ImageUsage::empty()`], which must be overridden. pub image_usage: ImageUsage, /// Whether the created images can be shared across multiple queues, or are limited to a single /// queue. /// /// The default value is [`Sharing::Exclusive`]. pub image_sharing: Sharing>, /// The transform that should be applied to an image before it is presented. /// /// The default value is [`SurfaceTransform::Identity`]. pub pre_transform: SurfaceTransform, /// How alpha values of the pixels in the image are to be treated. /// /// The default value is [`CompositeAlpha::Opaque`]. pub composite_alpha: CompositeAlpha, /// How the swapchain should behave when multiple images are waiting in the queue to be /// presented. /// /// The default is [`PresentMode::Fifo`]. pub present_mode: PresentMode, /// Whether the implementation is allowed to discard rendering operations that affect regions of /// the surface which aren't visible. This is important to take into account if your fragment /// shader has side-effects or if you want to read back the content of the image afterwards. /// /// The default value is `true`. pub clipped: bool, /// How full-screen exclusivity is to be handled. /// /// If set to anything other than [`FullScreenExclusive::Default`], then the /// [`ext_full_screen_exclusive`](crate::device::DeviceExtensions::ext_full_screen_exclusive) /// extension must be enabled on the device. /// /// The default value is [`FullScreenExclusive::Default`]. pub full_screen_exclusive: FullScreenExclusive, /// For Win32 surfaces, if `full_screen_exclusive` is /// [`FullScreenExclusive::ApplicationControlled`], this specifies the monitor on which /// full-screen exclusivity should be used. /// /// For this case, the value must be `Some`, and for all others it must be `None`. /// /// The default value is `None`. pub win32_monitor: Option, pub _ne: crate::NonExhaustive, } impl Default for SwapchainCreateInfo { #[inline] fn default() -> Self { Self { min_image_count: 2, image_format: None, image_color_space: ColorSpace::SrgbNonLinear, image_extent: [0, 0], image_array_layers: 1, image_usage: ImageUsage::empty(), image_sharing: Sharing::Exclusive, pre_transform: SurfaceTransform::Identity, composite_alpha: CompositeAlpha::Opaque, present_mode: PresentMode::Fifo, clipped: true, full_screen_exclusive: FullScreenExclusive::Default, win32_monitor: None, _ne: crate::NonExhaustive(()), } } } /// Error that can happen when creating a `Swapchain`. #[derive(Clone, Debug, PartialEq, Eq)] pub enum SwapchainCreationError { /// Not enough memory. OomError(OomError), /// The device was lost. DeviceLost, /// The surface was lost. SurfaceLost, /// The surface is already used by another swapchain. SurfaceInUse, /// The window is already in use by another API. NativeWindowInUse, RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, /// The provided `composite_alpha` is not supported by the surface for this device. CompositeAlphaNotSupported { provided: CompositeAlpha, supported: CompositeAlphas, }, /// The provided `format` and `color_space` are not supported by the surface for this device. FormatColorSpaceNotSupported, /// The provided `image_array_layers` is greater than what is supported by the surface for this /// device. ImageArrayLayersNotSupported { provided: u32, max_supported: u32 }, /// The provided `image_extent` is not within the range supported by the surface for this /// device. ImageExtentNotSupported { provided: [u32; 2], min_supported: [u32; 2], max_supported: [u32; 2], }, /// The provided `image_extent` contained at least one dimension of zero length. /// This is prohibited by [VUID-VkSwapchainCreateInfoKHR-imageExtent-01689](https://khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSwapchainCreateInfoKHR.html#VUID-VkSwapchainCreateInfoKHR-imageExtent-01689) /// which requires both the width and height be non-zero. /// /// This error is distinct from `ImageExtentNotSupported` because a surface's minimum supported /// length may not enforce this rule. ImageExtentZeroLengthDimensions, /// The provided image parameters are not supported as queried from `image_format_properties`. ImageFormatPropertiesNotSupported, /// The provided `image_sharing` was set to `Concurrent`, but one of the specified queue family /// indices was out of range. ImageSharingQueueFamilyIndexOutOfRange { queue_family_index: u32, queue_family_count: u32, }, /// The provided `image_usage` has fields set that are not supported by the surface for this /// device. ImageUsageNotSupported { provided: ImageUsage, supported: ImageUsage, }, /// The provided `min_image_count` is not within the range supported by the surface for this /// device. MinImageCountNotSupported { provided: u32, min_supported: u32, max_supported: Option, }, /// The provided `present_mode` is not supported by the surface for this device. PresentModeNotSupported, /// The provided `pre_transform` is not supported by the surface for this device. PreTransformNotSupported { provided: SurfaceTransform, supported: SurfaceTransforms, }, /// The provided `surface` is not supported by any of the device's queue families. SurfaceNotSupported, /// The swapchain has already been used to create a new one. SwapchainAlreadyRetired, /// The `win32_monitor` value was `Some` when it must be `None` or vice-versa. Win32MonitorInvalid, } impl Error for SwapchainCreationError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::OomError(err) => Some(err), _ => None, } } } impl Display for SwapchainCreationError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::OomError(_) => write!(f, "not enough memory available"), Self::DeviceLost => write!(f, "the device was lost"), Self::SurfaceLost => write!(f, "the surface was lost"), Self::SurfaceInUse => { write!(f, "the surface is already used by another swapchain") } Self::NativeWindowInUse => { write!(f, "the window is already in use by another API") } Self::RequirementNotMet { required_for, requires_one_of, } => write!( f, "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), Self::CompositeAlphaNotSupported { .. } => write!( f, "the provided `composite_alpha` is not supported by the surface for this device", ), Self::FormatColorSpaceNotSupported => write!( f, "the provided `format` and `color_space` are not supported by the surface for this \ device", ), Self::ImageArrayLayersNotSupported { provided, max_supported, } => write!( f, "the provided `image_array_layers` ({}) is greater than what is supported ({}) by \ the surface for this device", provided, max_supported, ), Self::ImageExtentNotSupported { provided, min_supported, max_supported, } => write!( f, "the provided `image_extent` ({:?}) is not within the range (min: {:?}, max: {:?}) \ supported by the surface for this device", provided, min_supported, max_supported, ), Self::ImageExtentZeroLengthDimensions => write!( f, "the provided `image_extent` contained at least one dimension of zero length", ), Self::ImageFormatPropertiesNotSupported => write!( f, "the provided image parameters are not supported as queried from \ `image_format_properties`", ), Self::ImageSharingQueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: _, } => write!( f, "the provided `image_sharing` was set to `Concurrent`, but one of the specified \ queue family indices ({}) was out of range", queue_family_index, ), Self::ImageUsageNotSupported { .. } => write!( f, "the provided `image_usage` has fields set that are not supported by the surface \ for this device", ), Self::MinImageCountNotSupported { provided, min_supported, max_supported, } => write!( f, "the provided `min_image_count` ({}) is not within the range (min: {}, max: {:?}) \ supported by the surface for this device", provided, min_supported, max_supported, ), Self::PresentModeNotSupported => write!( f, "the provided `present_mode` is not supported by the surface for this device", ), Self::PreTransformNotSupported { .. } => write!( f, "the provided `pre_transform` is not supported by the surface for this device", ), Self::SurfaceNotSupported => write!( f, "the provided `surface` is not supported by any of the device's queue families", ), Self::SwapchainAlreadyRetired => { write!(f, "the swapchain has already been used to create a new one") } Self::Win32MonitorInvalid => write!( f, "the `win32_monitor` value was `Some` when it must be `None` or vice-versa", ), } } } impl From for SwapchainCreationError { fn from(err: VulkanError) -> SwapchainCreationError { match err { err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)), err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), VulkanError::DeviceLost => Self::DeviceLost, VulkanError::SurfaceLost => Self::SurfaceLost, VulkanError::NativeWindowInUse => Self::NativeWindowInUse, _ => panic!("unexpected error: {:?}", err), } } } impl From for SwapchainCreationError { fn from(err: OomError) -> SwapchainCreationError { Self::OomError(err) } } impl From for SwapchainCreationError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } } vulkan_enum! { #[non_exhaustive] /// The way full-screen exclusivity is handled. FullScreenExclusive = FullScreenExclusiveEXT(i32); /// Indicates that the driver should determine the appropriate full-screen method /// by whatever means it deems appropriate. Default = DEFAULT, /// Indicates that the driver may use full-screen exclusive mechanisms when available. /// Such mechanisms may result in better performance and/or the availability of /// different presentation capabilities, but may require a more disruptive transition // during swapchain initialization, first presentation and/or destruction. Allowed = ALLOWED, /// Indicates that the driver should avoid using full-screen mechanisms which rely /// on disruptive transitions. Disallowed = DISALLOWED, /// Indicates the application will manage full-screen exclusive mode by using the /// [`Swapchain::acquire_full_screen_exclusive()`] and /// [`Swapchain::release_full_screen_exclusive()`] functions. ApplicationControlled = APPLICATION_CONTROLLED, } /// A wrapper around a Win32 monitor handle. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR); impl Win32Monitor { /// Wraps a Win32 monitor handle. /// /// # Safety /// /// - `hmonitor` must be a valid handle as returned by the Win32 API. pub unsafe fn new(hmonitor: *const T) -> Self { Self(hmonitor as _) } } // Winit's `MonitorHandle` is Send on Win32, so this seems safe. unsafe impl Send for Win32Monitor {} unsafe impl Sync for Win32Monitor {} /// Error that can happen when calling `Swapchain::acquire_full_screen_exclusive` or /// `Swapchain::release_full_screen_exclusive`. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum FullScreenExclusiveError { /// Not enough memory. OomError(OomError), /// Operation could not be completed for driver specific reasons. InitializationFailed, /// The surface is no longer accessible and must be recreated. SurfaceLost, /// Full-screen exclusivity is already acquired. DoubleAcquire, /// Full-screen exclusivity is not currently acquired. DoubleRelease, /// The swapchain is not in full-screen exclusive application controlled mode. NotApplicationControlled, } impl Error for FullScreenExclusiveError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { FullScreenExclusiveError::OomError(err) => Some(err), _ => None, } } } impl Display for FullScreenExclusiveError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { write!( f, "{}", match self { FullScreenExclusiveError::OomError(_) => "not enough memory", FullScreenExclusiveError::SurfaceLost => { "the surface of this swapchain is no longer valid" } FullScreenExclusiveError::InitializationFailed => { "operation could not be completed for driver specific reasons" } FullScreenExclusiveError::DoubleAcquire => "full-screen exclusivity is already acquired", FullScreenExclusiveError::DoubleRelease => "full-screen exclusivity is not acquired", FullScreenExclusiveError::NotApplicationControlled => { "the swapchain is not in full-screen exclusive application controlled mode" } } ) } } impl From for FullScreenExclusiveError { fn from(err: VulkanError) -> FullScreenExclusiveError { match err { err @ VulkanError::OutOfHostMemory => { FullScreenExclusiveError::OomError(OomError::from(err)) } err @ VulkanError::OutOfDeviceMemory => { FullScreenExclusiveError::OomError(OomError::from(err)) } VulkanError::SurfaceLost => FullScreenExclusiveError::SurfaceLost, VulkanError::InitializationFailed => FullScreenExclusiveError::InitializationFailed, _ => panic!("unexpected error: {:?}", err), } } } impl From for FullScreenExclusiveError { fn from(err: OomError) -> FullScreenExclusiveError { FullScreenExclusiveError::OomError(err) } } /// Tries to take ownership of an image in order to draw on it. /// /// The function returns the index of the image in the array of images that was returned /// when creating the swapchain, plus a future that represents the moment when the image will /// become available from the GPU (which may not be *immediately*). /// /// If you try to draw on an image without acquiring it first, the execution will block. (TODO /// behavior may change). /// /// The second field in the tuple in the Ok result is a bool represent if the acquisition was /// suboptimal. In this case the acquired image is still usable, but the swapchain should be /// recreated as the Surface's properties no longer match the swapchain. pub fn acquire_next_image( swapchain: Arc, timeout: Option, ) -> 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( before: F, queue: Arc, swapchain_info: SwapchainPresentInfo, ) -> PresentFuture 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, present_id: u64, timeout: Option, ) -> Result { let retired = swapchain.retired.lock(); // VUID-vkWaitForPresentKHR-swapchain-04997 if *retired { return Err(PresentWaitError::OutOfDate); } if present_id == 0 { return Err(PresentWaitError::PresentIdZero); } // VUID-vkWaitForPresentKHR-presentWait-06234 if !swapchain.device.enabled_features().present_wait { return Err(PresentWaitError::RequirementNotMet { required_for: "`wait_for_present`", requires_one_of: RequiresOneOf { features: &["present_wait"], ..Default::default() }, }); } let timeout_ns = timeout.map(|dur| dur.as_nanos() as u64).unwrap_or(0); let result = unsafe { (swapchain.device.fns().khr_present_wait.wait_for_present_khr)( swapchain.device.handle(), swapchain.handle, present_id, timeout_ns, ) }; match result { ash::vk::Result::SUCCESS => Ok(false), ash::vk::Result::SUBOPTIMAL_KHR => Ok(true), ash::vk::Result::TIMEOUT => Err(PresentWaitError::Timeout), err => { let err = VulkanError::from(err).into(); if let PresentWaitError::FullScreenExclusiveModeLost = &err { swapchain .full_screen_exclusive_held .store(false, Ordering::SeqCst); } Err(err) } } } /// Represents the moment when the GPU will have access to a swapchain image. #[must_use] pub struct SwapchainAcquireFuture { swapchain: Arc, image_index: u32, // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already // happened. semaphore: Option>, // Fence that is signalled when the acquire is complete. Empty if the acquire has already // happened. fence: Option, finished: AtomicBool, } impl SwapchainAcquireFuture { /// Returns the index of the image in the list of images returned when creating the swapchain. pub fn image_index(&self) -> u32 { self.image_index } /// Returns the corresponding swapchain. pub fn swapchain(&self) -> &Arc { &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) -> Result<(), FenceError> { match &self.fence { Some(fence) => fence.wait(timeout), None => Ok(()), } } } unsafe impl GpuFuture for SwapchainAcquireFuture { fn cleanup_finished(&mut self) {} unsafe fn build_submission(&self) -> Result { if let Some(ref semaphore) = self.semaphore { let sem = smallvec![semaphore.clone()]; Ok(SubmitAnyBuilder::SemaphoresWait(sem)) } else { Ok(SubmitAnyBuilder::Empty) } } fn flush(&self) -> Result<(), FlushError> { Ok(()) } unsafe fn signal_finished(&self) { self.finished.store(true, Ordering::SeqCst); } fn queue_change_allowed(&self) -> bool { true } fn queue(&self) -> Option> { None } fn check_buffer_access( &self, _buffer: &Buffer, _range: Range, _exclusive: bool, _queue: &Queue, ) -> Result<(), AccessCheckError> { Err(AccessCheckError::Unknown) } fn check_image_access( &self, image: &Image, _range: Range, _exclusive: bool, expected_layout: ImageLayout, _queue: &Queue, ) -> Result<(), AccessCheckError> { if self.swapchain.index_of_image(image) != Some(self.image_index) { return Err(AccessCheckError::Unknown); } if !self.swapchain.images[self.image_index as usize] .layout_initialized .load(Ordering::Relaxed) && expected_layout != ImageLayout::Undefined { return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { requested: expected_layout, })); } if expected_layout != ImageLayout::Undefined && expected_layout != ImageLayout::PresentSrc { return Err(AccessCheckError::Denied( AccessError::UnexpectedImageLayout { allowed: ImageLayout::PresentSrc, requested: expected_layout, }, )); } Ok(()) } #[inline] fn check_swapchain_image_acquired( &self, swapchain: &Swapchain, image_index: u32, before: bool, ) -> Result<(), AccessCheckError> { if before { Ok(()) } else { if swapchain == self.swapchain.as_ref() && image_index == self.image_index { Ok(()) } else { Err(AccessCheckError::Unknown) } } } } impl Drop for SwapchainAcquireFuture { fn drop(&mut self) { if thread::panicking() { return; } if let Some(fence) = &self.fence { fence.wait(None).unwrap(); // TODO: handle error? self.semaphore = None; } // TODO: if this future is destroyed without being presented, then eventually acquiring // a new image will block forever ; difficulty: hard } } unsafe impl DeviceOwned for SwapchainAcquireFuture { fn device(&self) -> &Arc { &self.swapchain.device } } /// Error that can happen when calling `acquire_next_image`. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] pub enum AcquireError { /// Not enough memory. OomError(OomError), /// The connection to the device has been lost. DeviceLost, /// The timeout of the function has been reached before an image was available. Timeout, /// The surface is no longer accessible and must be recreated. SurfaceLost, /// The swapchain has lost or doesn't have full-screen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. FullScreenExclusiveModeLost, /// The surface has changed in a way that makes the swapchain unusable. You must query the /// surface's new properties and recreate a new swapchain if you want to continue drawing. OutOfDate, /// Error during fence creation. FenceError(FenceError), /// Error during semaphore creation. SemaphoreError(SemaphoreError), } impl Error for AcquireError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { AcquireError::OomError(err) => Some(err), _ => None, } } } impl Display for AcquireError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { write!( f, "{}", match self { AcquireError::OomError(_) => "not enough memory", AcquireError::DeviceLost => "the connection to the device has been lost", AcquireError::Timeout => "no image is available for acquiring yet", AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid", AcquireError::OutOfDate => "the swapchain needs to be recreated", AcquireError::FullScreenExclusiveModeLost => { "the swapchain no longer has full-screen exclusivity" } AcquireError::FenceError(_) => "error creating fence", AcquireError::SemaphoreError(_) => "error creating semaphore", } ) } } impl From for AcquireError { fn from(err: FenceError) -> Self { AcquireError::FenceError(err) } } impl From for AcquireError { fn from(err: SemaphoreError) -> Self { AcquireError::SemaphoreError(err) } } impl From for AcquireError { fn from(err: OomError) -> AcquireError { AcquireError::OomError(err) } } impl From for AcquireError { fn from(err: VulkanError) -> AcquireError { match err { err @ VulkanError::OutOfHostMemory => AcquireError::OomError(OomError::from(err)), err @ VulkanError::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)), VulkanError::DeviceLost => AcquireError::DeviceLost, VulkanError::SurfaceLost => AcquireError::SurfaceLost, VulkanError::OutOfDate => AcquireError::OutOfDate, VulkanError::FullScreenExclusiveModeLost => AcquireError::FullScreenExclusiveModeLost, _ => panic!("unexpected error: {:?}", err), } } } /// Error that can happen when calling `acquire_next_image`. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] pub enum PresentWaitError { /// Not enough memory. OomError(OomError), /// The connection to the device has been lost. DeviceLost, /// The surface has changed in a way that makes the swapchain unusable. You must query the /// surface's new properties and recreate a new swapchain if you want to continue drawing. OutOfDate, /// The surface is no longer accessible and must be recreated. SurfaceLost, /// The swapchain has lost or doesn't have full-screen exclusivity possibly for /// implementation-specific reasons outside of the application’s control. FullScreenExclusiveModeLost, /// The timeout of the function has been reached before the present occured. Timeout, RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, /// Present id of zero is invalid. PresentIdZero, } impl Error for PresentWaitError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::OomError(err) => Some(err), _ => None, } } } impl Display for PresentWaitError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::OomError(e) => write!(f, "{}", e), Self::DeviceLost => write!(f, "the connection to the device has been lost"), Self::Timeout => write!(f, "no image is available for acquiring yet"), Self::SurfaceLost => write!(f, "the surface of this swapchain is no longer valid"), Self::OutOfDate => write!(f, "the swapchain needs to be recreated"), Self::FullScreenExclusiveModeLost => { write!(f, "the swapchain no longer has full-screen exclusivity") } Self::RequirementNotMet { required_for, requires_one_of, } => write!( f, "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), Self::PresentIdZero => write!(f, "present id of zero is invalid"), } } } impl From for PresentWaitError { fn from(err: OomError) -> PresentWaitError { Self::OomError(err) } } impl From for PresentWaitError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } } impl From for PresentWaitError { fn from(err: VulkanError) -> PresentWaitError { match err { err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)), err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), VulkanError::DeviceLost => Self::DeviceLost, VulkanError::SurfaceLost => Self::SurfaceLost, VulkanError::OutOfDate => Self::OutOfDate, VulkanError::FullScreenExclusiveModeLost => Self::FullScreenExclusiveModeLost, _ => panic!("unexpected error: {:?}", err), } } } /// Represents a swapchain image being presented on the screen. #[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"] pub struct PresentFuture

where P: GpuFuture, { previous: P, queue: Arc, swapchain_info: SwapchainPresentInfo, // True if `flush()` has been called on the future, which means that the present command has // been submitted. flushed: AtomicBool, // True if `signal_finished()` has been called on the future, which means that the future has // been submitted and has already been processed by the GPU. finished: AtomicBool, } impl

PresentFuture

where P: GpuFuture, { /// Returns the index of the image in the list of images returned when creating the swapchain. pub fn image_id(&self) -> u32 { self.swapchain_info.image_index } /// Returns the corresponding swapchain. pub fn swapchain(&self) -> &Arc { &self.swapchain_info.swapchain } } unsafe impl

GpuFuture for PresentFuture

where P: GpuFuture, { fn cleanup_finished(&mut self) { self.previous.cleanup_finished(); } unsafe fn build_submission(&self) -> Result { if self.flushed.load(Ordering::SeqCst) { return Ok(SubmitAnyBuilder::Empty); } let mut swapchain_info = self.swapchain_info.clone(); debug_assert!(swapchain_info.image_index < swapchain_info.swapchain.image_count()); let device = swapchain_info.swapchain.device(); if !device.enabled_features().present_id { swapchain_info.present_id = None; } if device.enabled_extensions().khr_incremental_present { for rectangle in &swapchain_info.present_regions { assert!(rectangle.is_compatible_with(swapchain_info.swapchain.as_ref())); } } else { swapchain_info.present_regions = Default::default(); } let _queue = self.previous.queue(); // TODO: if the swapchain image layout is not PRESENT, should add a transition command // buffer Ok(match self.previous.build_submission()? { SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo { swapchain_infos: vec![self.swapchain_info.clone()], ..Default::default() }), SubmitAnyBuilder::SemaphoresWait(semaphores) => { SubmitAnyBuilder::QueuePresent(PresentInfo { wait_semaphores: semaphores.into_iter().collect(), swapchain_infos: vec![self.swapchain_info.clone()], ..Default::default() }) } SubmitAnyBuilder::CommandBuffer(_, _) => { // submit the command buffer by flushing previous. // Since the implementation should remember being flushed it's safe to call build_submission multiple times self.previous.flush()?; SubmitAnyBuilder::QueuePresent(PresentInfo { swapchain_infos: vec![self.swapchain_info.clone()], ..Default::default() }) } SubmitAnyBuilder::BindSparse(_, _) => { // submit the command buffer by flushing previous. // Since the implementation should remember being flushed it's safe to call build_submission multiple times self.previous.flush()?; SubmitAnyBuilder::QueuePresent(PresentInfo { swapchain_infos: vec![self.swapchain_info.clone()], ..Default::default() }) } SubmitAnyBuilder::QueuePresent(mut present_info) => { present_info .swapchain_infos .push(self.swapchain_info.clone()); SubmitAnyBuilder::QueuePresent(present_info) } }) } fn flush(&self) -> Result<(), FlushError> { unsafe { // If `flushed` already contains `true`, then `build_submission` will return `Empty`. let build_submission_result = self.build_submission(); self.flushed.store(true, Ordering::SeqCst); match build_submission_result? { SubmitAnyBuilder::Empty => Ok(()), SubmitAnyBuilder::QueuePresent(present_info) => { // VUID-VkPresentIdKHR-presentIds-04999 for swapchain_info in &present_info.swapchain_infos { if swapchain_info.present_id.map_or(false, |present_id| { !swapchain_info.swapchain.try_claim_present_id(present_id) }) { return Err(FlushError::PresentIdLessThanOrEqual); } } match self.previous.check_swapchain_image_acquired( &self.swapchain_info.swapchain, self.swapchain_info.image_index, true, ) { Ok(_) => (), Err(AccessCheckError::Unknown) => { return Err(AccessError::SwapchainImageNotAcquired.into()) } Err(AccessCheckError::Denied(e)) => return Err(e.into()), } Ok(self .queue .with(|mut q| q.present_unchecked(present_info))? .map(|r| r.map(|_| ())) .fold(Ok(()), Result::and)?) } _ => unreachable!(), } } } unsafe fn signal_finished(&self) { self.flushed.store(true, Ordering::SeqCst); self.finished.store(true, Ordering::SeqCst); self.previous.signal_finished(); } fn queue_change_allowed(&self) -> bool { false } fn queue(&self) -> Option> { debug_assert!(match self.previous.queue() { None => true, Some(q) => q == self.queue, }); Some(self.queue.clone()) } fn check_buffer_access( &self, buffer: &Buffer, range: Range, exclusive: bool, queue: &Queue, ) -> Result<(), AccessCheckError> { self.previous .check_buffer_access(buffer, range, exclusive, queue) } fn check_image_access( &self, image: &Image, range: Range, exclusive: bool, expected_layout: ImageLayout, queue: &Queue, ) -> Result<(), AccessCheckError> { if self.swapchain_info.swapchain.index_of_image(image) == Some(self.swapchain_info.image_index) { // This future presents the swapchain image, which "unlocks" it. Therefore any attempt // to use this swapchain image afterwards shouldn't get granted automatic access. // Instead any attempt to access the image afterwards should get an authorization from // a later swapchain acquire future. Hence why we return `Unknown` here. Err(AccessCheckError::Unknown) } else { self.previous .check_image_access(image, range, exclusive, expected_layout, queue) } } #[inline] fn check_swapchain_image_acquired( &self, swapchain: &Swapchain, image_index: u32, before: bool, ) -> Result<(), AccessCheckError> { if before { self.previous .check_swapchain_image_acquired(swapchain, image_index, false) } else if swapchain == self.swapchain_info.swapchain.as_ref() && image_index == self.swapchain_info.image_index { Err(AccessError::SwapchainImageNotAcquired.into()) } else { self.previous .check_swapchain_image_acquired(swapchain, image_index, false) } } } unsafe impl

DeviceOwned for PresentFuture

where P: GpuFuture, { fn device(&self) -> &Arc { self.queue.device() } } impl

Drop for PresentFuture

where P: GpuFuture, { fn drop(&mut self) { if thread::panicking() { return; } unsafe { if !*self.flushed.get_mut() { // Flushing may fail, that's okay. We will still wait for the queue later, so any // previous futures that were flushed correctly will still be waited upon. self.flush().ok(); } if !*self.finished.get_mut() { // Block until the queue finished. self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap(); self.previous.signal_finished(); } } } } pub struct AcquiredImage { pub image_index: u32, pub suboptimal: bool, } /// Unsafe variant of `acquire_next_image`. /// /// # Safety /// /// - The semaphore and/or the fence must be kept alive until it is signaled. /// - The swapchain must not have been replaced by being passed as the old swapchain when creating /// a new one. pub unsafe fn acquire_next_image_raw( swapchain: &Swapchain, timeout: Option, semaphore: Option<&Semaphore>, fence: Option<&Fence>, ) -> Result { let fns = swapchain.device.fns(); let timeout_ns = if let Some(timeout) = timeout { timeout .as_secs() .saturating_mul(1_000_000_000) .saturating_add(timeout.subsec_nanos() as u64) } else { u64::MAX }; let mut out = MaybeUninit::uninit(); let result = (fns.khr_swapchain.acquire_next_image_khr)( swapchain.device.handle(), swapchain.handle, timeout_ns, semaphore .map(|s| s.handle()) .unwrap_or(ash::vk::Semaphore::null()), fence.map(|f| f.handle()).unwrap_or(ash::vk::Fence::null()), out.as_mut_ptr(), ); let suboptimal = match result { ash::vk::Result::SUCCESS => false, ash::vk::Result::SUBOPTIMAL_KHR => true, ash::vk::Result::NOT_READY => return Err(AcquireError::Timeout), ash::vk::Result::TIMEOUT => return Err(AcquireError::Timeout), err => return Err(VulkanError::from(err).into()), }; if let Some(semaphore) = semaphore { let mut state = semaphore.state(); state.swapchain_acquire(); } if let Some(fence) = fence { let mut state = fence.state(); state.import_swapchain_acquire(); } Ok(AcquiredImage { image_index: out.assume_init(), suboptimal, }) }