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