| // Copyright (c) 2016 The vulkano developers |
| // Licensed under the Apache License, Version 2.0 |
| // <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT |
| // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, |
| // at your option. All files in the project carrying such |
| // notice may not be copied, modified, or distributed except |
| // according to those terms. |
| |
| use crate::buffer::BufferAccess; |
| use crate::check_errors; |
| use crate::command_buffer::submit::SubmitAnyBuilder; |
| use crate::command_buffer::submit::SubmitPresentBuilder; |
| use crate::command_buffer::submit::SubmitPresentError; |
| use crate::command_buffer::submit::SubmitSemaphoresWaitBuilder; |
| use crate::device::Device; |
| use crate::device::DeviceOwned; |
| use crate::device::Queue; |
| use crate::format::Format; |
| use crate::image::swapchain::SwapchainImage; |
| use crate::image::sys::UnsafeImage; |
| use crate::image::ImageAccess; |
| use crate::image::ImageCreateFlags; |
| use crate::image::ImageDimensions; |
| use crate::image::ImageInner; |
| use crate::image::ImageLayout; |
| use crate::image::ImageTiling; |
| use crate::image::ImageType; |
| use crate::image::ImageUsage; |
| use crate::image::SampleCount; |
| use crate::swapchain::CapabilitiesError; |
| use crate::swapchain::ColorSpace; |
| use crate::swapchain::CompositeAlpha; |
| use crate::swapchain::PresentMode; |
| use crate::swapchain::PresentRegion; |
| use crate::swapchain::Surface; |
| use crate::swapchain::SurfaceSwapchainLock; |
| use crate::swapchain::SurfaceTransform; |
| use crate::sync::semaphore::SemaphoreError; |
| use crate::sync::AccessCheckError; |
| use crate::sync::AccessError; |
| use crate::sync::AccessFlags; |
| use crate::sync::Fence; |
| use crate::sync::FlushError; |
| use crate::sync::GpuFuture; |
| use crate::sync::PipelineStages; |
| use crate::sync::Semaphore; |
| use crate::sync::SharingMode; |
| use crate::Error; |
| use crate::OomError; |
| use crate::Success; |
| use crate::VulkanObject; |
| use std::error; |
| use std::fmt; |
| use std::mem; |
| use std::mem::MaybeUninit; |
| use std::ptr; |
| use std::sync::atomic::AtomicBool; |
| use std::sync::atomic::Ordering; |
| use std::sync::Arc; |
| use std::sync::Mutex; |
| use std::time::Duration; |
| |
| /// The way fullscreen exclusivity is handled. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| #[repr(i32)] |
| pub enum FullscreenExclusive { |
| /// Indicates that the driver should determine the appropriate full-screen method |
| /// by whatever means it deems appropriate. |
| Default = ash::vk::FullScreenExclusiveEXT::DEFAULT.as_raw(), |
| /// Indicates that the driver may use full-screen exclusive mechanisms when available. |
| /// Such mechanisms may result in better performance and/or the availability of |
| /// different presentation capabilities, but may require a more disruptive transition |
| // during swapchain initialization, first presentation and/or destruction. |
| Allowed = ash::vk::FullScreenExclusiveEXT::ALLOWED.as_raw(), |
| /// Indicates that the driver should avoid using full-screen mechanisms which rely |
| /// on disruptive transitions. |
| Disallowed = ash::vk::FullScreenExclusiveEXT::DISALLOWED.as_raw(), |
| /// Indicates the application will manage full-screen exclusive mode by using |
| /// `Swapchain::acquire_fullscreen_exclusive()` and |
| /// `Swapchain::release_fullscreen_exclusive()` functions. |
| AppControlled = ash::vk::FullScreenExclusiveEXT::APPLICATION_CONTROLLED.as_raw(), |
| } |
| |
| impl From<FullscreenExclusive> for ash::vk::FullScreenExclusiveEXT { |
| #[inline] |
| fn from(val: FullscreenExclusive) -> Self { |
| Self::from_raw(val as i32) |
| } |
| } |
| |
| /// Tries to take ownership of an image in order to draw on it. |
| /// |
| /// The function returns the index of the image in the array of images that was returned |
| /// when creating the swapchain, plus a future that represents the moment when the image will |
| /// become available from the GPU (which may not be *immediately*). |
| /// |
| /// If you try to draw on an image without acquiring it first, the execution will block. (TODO |
| /// behavior may change). |
| /// |
| /// The second field in the tuple in the Ok result is a bool represent if the acquisition was |
| /// suboptimal. In this case the acquired image is still usable, but the swapchain should be |
| /// recreated as the Surface's properties no longer match the swapchain. |
| pub fn acquire_next_image<W>( |
| swapchain: Arc<Swapchain<W>>, |
| timeout: Option<Duration>, |
| ) -> Result<(usize, bool, SwapchainAcquireFuture<W>), AcquireError> { |
| let semaphore = Semaphore::from_pool(swapchain.device.clone())?; |
| let fence = Fence::from_pool(swapchain.device.clone())?; |
| |
| let AcquiredImage { id, suboptimal } = { |
| // Check that this is not an old swapchain. From specs: |
| // > swapchain must not have been replaced by being passed as the |
| // > VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR |
| let stale = swapchain.stale.lock().unwrap(); |
| if *stale { |
| return Err(AcquireError::OutOfDate); |
| } |
| |
| let acquire_result = |
| unsafe { acquire_next_image_raw(&swapchain, timeout, Some(&semaphore), Some(&fence)) }; |
| |
| if let &Err(AcquireError::FullscreenExclusiveLost) = &acquire_result { |
| swapchain |
| .fullscreen_exclusive_held |
| .store(false, Ordering::SeqCst); |
| } |
| |
| acquire_result? |
| }; |
| |
| Ok(( |
| id, |
| suboptimal, |
| SwapchainAcquireFuture { |
| swapchain, |
| semaphore: Some(semaphore), |
| fence: Some(fence), |
| image_id: id, |
| finished: AtomicBool::new(false), |
| }, |
| )) |
| } |
| |
| /// Presents an image on the screen. |
| /// |
| /// The parameter is the same index as what `acquire_next_image` returned. The image must |
| /// have been acquired first. |
| /// |
| /// The actual behavior depends on the present mode that you passed when creating the |
| /// swapchain. |
| pub fn present<F, W>( |
| swapchain: Arc<Swapchain<W>>, |
| before: F, |
| queue: Arc<Queue>, |
| index: usize, |
| ) -> PresentFuture<F, W> |
| where |
| F: GpuFuture, |
| { |
| assert!(index < swapchain.images.len()); |
| |
| // TODO: restore this check with a dummy ImageAccess implementation |
| /*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead |
| // Normally if `check_image_access` returns false we're supposed to call the `gpu_access` |
| // function on the image instead. But since we know that this method on `SwapchainImage` |
| // always returns false anyway (by design), we don't need to do it. |
| assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/ |
| |
| PresentFuture { |
| previous: before, |
| queue, |
| swapchain, |
| image_id: index, |
| present_region: None, |
| flushed: AtomicBool::new(false), |
| finished: AtomicBool::new(false), |
| } |
| } |
| |
| /// Same as `swapchain::present`, except it allows specifying a present region. |
| /// Areas outside the present region may be ignored by Vulkan in order to optimize presentation. |
| /// |
| /// This is just an optimization hint, as the Vulkan driver is free to ignore the given present region. |
| /// |
| /// If `VK_KHR_incremental_present` is not enabled on the device, the parameter will be ignored. |
| pub fn present_incremental<F, W>( |
| swapchain: Arc<Swapchain<W>>, |
| before: F, |
| queue: Arc<Queue>, |
| index: usize, |
| present_region: PresentRegion, |
| ) -> PresentFuture<F, W> |
| where |
| F: GpuFuture, |
| { |
| assert!(index < swapchain.images.len()); |
| |
| // TODO: restore this check with a dummy ImageAccess implementation |
| /*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead |
| // Normally if `check_image_access` returns false we're supposed to call the `gpu_access` |
| // function on the image instead. But since we know that this method on `SwapchainImage` |
| // always returns false anyway (by design), we don't need to do it. |
| assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/ |
| |
| PresentFuture { |
| previous: before, |
| queue, |
| swapchain, |
| image_id: index, |
| present_region: Some(present_region), |
| flushed: AtomicBool::new(false), |
| finished: AtomicBool::new(false), |
| } |
| } |
| |
| /// Contains the swapping system and the images that can be shown on a surface. |
| pub struct Swapchain<W> { |
| // The Vulkan device this swapchain was created with. |
| device: Arc<Device>, |
| // The surface, which we need to keep alive. |
| surface: Arc<Surface<W>>, |
| // The swapchain object. |
| swapchain: ash::vk::SwapchainKHR, |
| |
| // The images of this swapchain. |
| images: Vec<ImageEntry>, |
| |
| // If true, that means we have tried to use this swapchain to recreate a new swapchain. The current |
| // swapchain can no longer be used for anything except presenting already-acquired images. |
| // |
| // We use a `Mutex` instead of an `AtomicBool` because we want to keep that locked while |
| // we acquire the image. |
| stale: Mutex<bool>, |
| |
| // Parameters passed to the constructor. |
| num_images: u32, |
| format: Format, |
| color_space: ColorSpace, |
| dimensions: [u32; 2], |
| layers: u32, |
| usage: ImageUsage, |
| sharing_mode: SharingMode, |
| transform: SurfaceTransform, |
| composite_alpha: CompositeAlpha, |
| present_mode: PresentMode, |
| fullscreen_exclusive: FullscreenExclusive, |
| fullscreen_exclusive_held: AtomicBool, |
| clipped: bool, |
| } |
| |
| struct ImageEntry { |
| // The actual image. |
| image: UnsafeImage, |
| // If true, then the image is still in the undefined layout and must be transitioned. |
| undefined_layout: AtomicBool, |
| } |
| |
| impl<W> Swapchain<W> { |
| /// Starts the process of building a new swapchain, using default values for the parameters. |
| #[inline] |
| pub fn start(device: Arc<Device>, surface: Arc<Surface<W>>) -> SwapchainBuilder<W> { |
| SwapchainBuilder { |
| device, |
| surface, |
| |
| num_images: 2, |
| format: None, |
| color_space: ColorSpace::SrgbNonLinear, |
| dimensions: None, |
| layers: 1, |
| usage: ImageUsage::none(), |
| sharing_mode: SharingMode::Exclusive, |
| transform: Default::default(), |
| composite_alpha: CompositeAlpha::Opaque, |
| present_mode: PresentMode::Fifo, |
| fullscreen_exclusive: FullscreenExclusive::Default, |
| clipped: true, |
| |
| old_swapchain: None, |
| } |
| } |
| |
| /// Starts building a new swapchain from an existing swapchain. |
| /// |
| /// Use this when a swapchain has become invalidated, such as due to window resizes. |
| /// The builder is pre-filled with the parameters of the old one, except for `dimensions`, |
| /// which is set to `None`. |
| #[inline] |
| pub fn recreate(self: &Arc<Self>) -> SwapchainBuilder<W> { |
| SwapchainBuilder { |
| device: self.device().clone(), |
| surface: self.surface().clone(), |
| |
| num_images: self.images.len() as u32, |
| format: Some(self.format), |
| color_space: self.color_space, |
| dimensions: None, |
| layers: self.layers, |
| usage: self.usage, |
| sharing_mode: self.sharing_mode.clone(), |
| transform: self.transform, |
| composite_alpha: self.composite_alpha, |
| present_mode: self.present_mode, |
| fullscreen_exclusive: self.fullscreen_exclusive, |
| clipped: self.clipped, |
| |
| old_swapchain: Some(self.clone()), |
| } |
| } |
| |
| /// Returns the saved Surface, from the Swapchain creation. |
| #[inline] |
| pub fn surface(&self) -> &Arc<Surface<W>> { |
| &self.surface |
| } |
| |
| /// Returns of the images that belong to this swapchain. |
| #[inline] |
| pub fn raw_image(&self, offset: usize) -> Option<ImageInner> { |
| self.images.get(offset).map(|i| ImageInner { |
| image: &i.image, |
| first_layer: 0, |
| num_layers: self.layers as usize, |
| first_mipmap_level: 0, |
| num_mipmap_levels: 1, |
| }) |
| } |
| |
| /// Returns the number of images of the swapchain. |
| #[inline] |
| pub fn num_images(&self) -> u32 { |
| self.images.len() as u32 |
| } |
| |
| /// Returns the format of the images of the swapchain. |
| #[inline] |
| pub fn format(&self) -> Format { |
| self.format |
| } |
| |
| /// Returns the dimensions of the images of the swapchain. |
| #[inline] |
| pub fn dimensions(&self) -> [u32; 2] { |
| self.dimensions |
| } |
| |
| /// Returns the number of layers of the images of the swapchain. |
| #[inline] |
| pub fn layers(&self) -> u32 { |
| self.layers |
| } |
| |
| /// Returns the transform that was passed when creating the swapchain. |
| #[inline] |
| pub fn transform(&self) -> SurfaceTransform { |
| self.transform |
| } |
| |
| /// Returns the alpha mode that was passed when creating the swapchain. |
| #[inline] |
| pub fn composite_alpha(&self) -> CompositeAlpha { |
| self.composite_alpha |
| } |
| |
| /// Returns the present mode that was passed when creating the swapchain. |
| #[inline] |
| pub fn present_mode(&self) -> PresentMode { |
| self.present_mode |
| } |
| |
| /// Returns the value of `clipped` that was passed when creating the swapchain. |
| #[inline] |
| pub fn clipped(&self) -> bool { |
| self.clipped |
| } |
| |
| /// Returns the value of 'fullscreen_exclusive` that was passed when creating the swapchain. |
| #[inline] |
| pub fn fullscreen_exclusive(&self) -> FullscreenExclusive { |
| self.fullscreen_exclusive |
| } |
| |
| /// `FullscreenExclusive::AppControlled` must be the active fullscreen exclusivity mode. |
| /// Acquire fullscreen exclusivity until either the `release_fullscreen_exclusive` is |
| /// called, or if any of the the other `Swapchain` functions return `FullscreenExclusiveLost`. |
| /// Requires: `FullscreenExclusive::AppControlled` |
| pub fn acquire_fullscreen_exclusive(&self) -> Result<(), FullscreenExclusiveError> { |
| if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { |
| return Err(FullscreenExclusiveError::NotAppControlled); |
| } |
| |
| if self.fullscreen_exclusive_held.swap(true, Ordering::SeqCst) { |
| return Err(FullscreenExclusiveError::DoubleAcquire); |
| } |
| |
| unsafe { |
| check_errors( |
| self.device |
| .fns() |
| .ext_full_screen_exclusive |
| .acquire_full_screen_exclusive_mode_ext( |
| self.device.internal_object(), |
| self.swapchain, |
| ), |
| )?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// `FullscreenExclusive::AppControlled` must be the active fullscreen exclusivity mode. |
| /// Release fullscreen exclusivity. |
| pub fn release_fullscreen_exclusive(&self) -> Result<(), FullscreenExclusiveError> { |
| if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { |
| return Err(FullscreenExclusiveError::NotAppControlled); |
| } |
| |
| if !self.fullscreen_exclusive_held.swap(false, Ordering::SeqCst) { |
| return Err(FullscreenExclusiveError::DoubleRelease); |
| } |
| |
| unsafe { |
| check_errors( |
| self.device |
| .fns() |
| .ext_full_screen_exclusive |
| .release_full_screen_exclusive_mode_ext( |
| self.device.internal_object(), |
| self.swapchain, |
| ), |
| )?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// `FullscreenExclusive::AppControlled` is not the active fullscreen exclusivity mode, |
| /// then this function will always return false. If true is returned the swapchain |
| /// is in `FullscreenExclusive::AppControlled` fullscreen exclusivity mode and exclusivity |
| /// is currently acquired. |
| pub fn is_fullscreen_exclusive(&self) -> bool { |
| if self.fullscreen_exclusive != FullscreenExclusive::AppControlled { |
| false |
| } else { |
| self.fullscreen_exclusive_held.load(Ordering::SeqCst) |
| } |
| } |
| |
| // This method is necessary to allow `SwapchainImage`s to signal when they have been |
| // transitioned out of their initial `undefined` image layout. |
| // |
| // See the `ImageAccess::layout_initialized` method documentation for more details. |
| pub(crate) fn image_layout_initialized(&self, image_offset: usize) { |
| let image_entry = self.images.get(image_offset); |
| if let Some(ref image_entry) = image_entry { |
| image_entry.undefined_layout.store(false, Ordering::SeqCst); |
| } |
| } |
| |
| pub(crate) fn is_image_layout_initialized(&self, image_offset: usize) -> bool { |
| let image_entry = self.images.get(image_offset); |
| if let Some(ref image_entry) = image_entry { |
| !image_entry.undefined_layout.load(Ordering::SeqCst) |
| } else { |
| false |
| } |
| } |
| } |
| |
| unsafe impl<W> VulkanObject for Swapchain<W> { |
| type Object = ash::vk::SwapchainKHR; |
| |
| #[inline] |
| fn internal_object(&self) -> ash::vk::SwapchainKHR { |
| self.swapchain |
| } |
| } |
| |
| unsafe impl<W> DeviceOwned for Swapchain<W> { |
| fn device(&self) -> &Arc<Device> { |
| &self.device |
| } |
| } |
| |
| impl<W> fmt::Debug for Swapchain<W> { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!(fmt, "<Vulkan swapchain {:?}>", self.swapchain) |
| } |
| } |
| |
| impl<W> Drop for Swapchain<W> { |
| #[inline] |
| fn drop(&mut self) { |
| unsafe { |
| let fns = self.device.fns(); |
| fns.khr_swapchain.destroy_swapchain_khr( |
| self.device.internal_object(), |
| self.swapchain, |
| ptr::null(), |
| ); |
| self.surface.flag().store(false, Ordering::Release); |
| } |
| } |
| } |
| |
| /// Builder for a [`Swapchain`]. |
| #[derive(Debug)] |
| pub struct SwapchainBuilder<W> { |
| device: Arc<Device>, |
| surface: Arc<Surface<W>>, |
| old_swapchain: Option<Arc<Swapchain<W>>>, |
| |
| num_images: u32, |
| format: Option<Format>, // None = use a default |
| color_space: ColorSpace, |
| dimensions: Option<[u32; 2]>, |
| layers: u32, |
| usage: ImageUsage, |
| sharing_mode: SharingMode, |
| transform: SurfaceTransform, |
| composite_alpha: CompositeAlpha, |
| present_mode: PresentMode, |
| fullscreen_exclusive: FullscreenExclusive, |
| clipped: bool, |
| } |
| |
| impl<W> SwapchainBuilder<W> { |
| /// Builds a new swapchain. Allocates images who content can be made visible on a surface. |
| /// |
| /// See also the `Surface::get_capabilities` function which returns the values that are |
| /// supported by the implementation. All the parameters that you pass to the builder |
| /// must be supported. |
| /// |
| /// This function returns the swapchain plus a list of the images that belong to the |
| /// swapchain. The order in which the images are returned is important for the |
| /// `acquire_next_image` and `present` functions. |
| /// |
| /// # Panic |
| /// |
| /// - Panics if the device and the surface don't belong to the same instance. |
| /// - Panics if `usage` is empty. |
| /// |
| // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? |
| pub fn build( |
| self, |
| ) -> Result<(Arc<Swapchain<W>>, Vec<Arc<SwapchainImage<W>>>), SwapchainCreationError> { |
| let SwapchainBuilder { |
| device, |
| surface, |
| old_swapchain, |
| |
| num_images, |
| format, |
| color_space, |
| dimensions, |
| layers, |
| usage, |
| sharing_mode, |
| transform, |
| composite_alpha, |
| present_mode, |
| fullscreen_exclusive, |
| clipped, |
| } = self; |
| |
| assert_eq!( |
| device.instance().internal_object(), |
| surface.instance().internal_object() |
| ); |
| |
| // Checking that the requested parameters match the capabilities. |
| let capabilities = surface.capabilities(device.physical_device())?; |
| if num_images < capabilities.min_image_count { |
| return Err(SwapchainCreationError::UnsupportedMinImagesCount); |
| } |
| if let Some(c) = capabilities.max_image_count { |
| if num_images > c { |
| return Err(SwapchainCreationError::UnsupportedMaxImagesCount); |
| } |
| } |
| |
| let format = { |
| if let Some(format) = format { |
| if !capabilities |
| .supported_formats |
| .iter() |
| .any(|&(f, c)| f == format && c == color_space) |
| { |
| return Err(SwapchainCreationError::UnsupportedFormat); |
| } |
| format |
| } else { |
| if let Some(format) = [Format::R8G8B8A8Unorm, Format::B8G8R8A8Unorm] |
| .iter() |
| .copied() |
| .find(|&format| { |
| capabilities |
| .supported_formats |
| .iter() |
| .any(|&(f, c)| f == format && c == color_space) |
| }) |
| { |
| format |
| } else { |
| return Err(SwapchainCreationError::UnsupportedFormat); |
| } |
| } |
| }; |
| |
| let dimensions = if let Some(dimensions) = dimensions { |
| if dimensions[0] < capabilities.min_image_extent[0] { |
| return Err(SwapchainCreationError::UnsupportedDimensions); |
| } |
| if dimensions[1] < capabilities.min_image_extent[1] { |
| return Err(SwapchainCreationError::UnsupportedDimensions); |
| } |
| if dimensions[0] > capabilities.max_image_extent[0] { |
| return Err(SwapchainCreationError::UnsupportedDimensions); |
| } |
| if dimensions[1] > capabilities.max_image_extent[1] { |
| return Err(SwapchainCreationError::UnsupportedDimensions); |
| } |
| dimensions |
| } else { |
| capabilities.current_extent.unwrap() |
| }; |
| if layers < 1 || layers > capabilities.max_image_array_layers { |
| return Err(SwapchainCreationError::UnsupportedArrayLayers); |
| } |
| if (ash::vk::ImageUsageFlags::from(usage) |
| & ash::vk::ImageUsageFlags::from(capabilities.supported_usage_flags)) |
| != ash::vk::ImageUsageFlags::from(usage) |
| { |
| return Err(SwapchainCreationError::UnsupportedUsageFlags); |
| } |
| if !capabilities.supported_transforms.supports(transform) { |
| return Err(SwapchainCreationError::UnsupportedSurfaceTransform); |
| } |
| if !capabilities |
| .supported_composite_alpha |
| .supports(composite_alpha) |
| { |
| return Err(SwapchainCreationError::UnsupportedCompositeAlpha); |
| } |
| if !capabilities.present_modes.supports(present_mode) { |
| return Err(SwapchainCreationError::UnsupportedPresentMode); |
| } |
| |
| let flags = ImageCreateFlags::none(); |
| |
| // check that the physical device supports the swapchain image configuration |
| match device.image_format_properties( |
| format, |
| ImageType::Dim2d, |
| ImageTiling::Optimal, |
| usage, |
| flags, |
| ) { |
| Ok(_) => (), |
| Err(e) => { |
| eprintln!("{}", e); |
| return Err(SwapchainCreationError::UnsupportedImageConfiguration); |
| } |
| } |
| |
| // If we recreate a swapchain, make sure that the surface is the same. |
| if let Some(ref sc) = old_swapchain { |
| if surface.internal_object() != sc.surface.internal_object() { |
| return Err(SwapchainCreationError::OldSwapchainSurfaceMismatch); |
| } |
| } else { |
| // Checking that the surface doesn't already have a swapchain. |
| let has_already = surface.flag().swap(true, Ordering::AcqRel); |
| if has_already { |
| return Err(SwapchainCreationError::SurfaceInUse); |
| } |
| } |
| |
| if !device.enabled_extensions().khr_swapchain { |
| return Err(SwapchainCreationError::MissingExtensionKHRSwapchain); |
| } |
| |
| let mut surface_full_screen_exclusive_info = None; |
| |
| // TODO: VK_EXT_FULL_SCREEN_EXCLUSIVE requires these extensions, so they should always |
| // be enabled if it is. A separate check here is unnecessary; this should be checked at |
| // device creation. |
| if device.enabled_extensions().ext_full_screen_exclusive |
| && surface |
| .instance() |
| .enabled_extensions() |
| .khr_get_physical_device_properties2 |
| && surface |
| .instance() |
| .enabled_extensions() |
| .khr_get_surface_capabilities2 |
| { |
| surface_full_screen_exclusive_info = Some(ash::vk::SurfaceFullScreenExclusiveInfoEXT { |
| full_screen_exclusive: fullscreen_exclusive.into(), |
| ..Default::default() |
| }); |
| } |
| |
| let p_next = match surface_full_screen_exclusive_info.as_ref() { |
| Some(some) => unsafe { mem::transmute(some as *const _) }, |
| None => ptr::null(), |
| }; |
| |
| // Required by the specs. |
| assert_ne!(usage, ImageUsage::none()); |
| |
| if let Some(ref old_swapchain) = old_swapchain { |
| let mut stale = old_swapchain.stale.lock().unwrap(); |
| |
| // The swapchain has already been used to create a new one. |
| if *stale { |
| return Err(SwapchainCreationError::OldSwapchainAlreadyUsed); |
| } else { |
| // According to the documentation of VkSwapchainCreateInfoKHR: |
| // |
| // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE, |
| // > any images not acquired by the application may be freed by the implementation, |
| // > which may occur even if creation of the new swapchain fails. |
| // |
| // Therefore, we set stale to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails. |
| *stale = true; |
| } |
| } |
| |
| let fns = device.fns(); |
| |
| let swapchain = unsafe { |
| let (sh_mode, sh_count, sh_indices) = match sharing_mode { |
| SharingMode::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, ptr::null()), |
| SharingMode::Concurrent(ref ids) => ( |
| ash::vk::SharingMode::CONCURRENT, |
| ids.len() as u32, |
| ids.as_ptr(), |
| ), |
| }; |
| |
| let infos = ash::vk::SwapchainCreateInfoKHR { |
| p_next, |
| flags: ash::vk::SwapchainCreateFlagsKHR::empty(), |
| surface: surface.internal_object(), |
| min_image_count: num_images, |
| image_format: format.into(), |
| image_color_space: color_space.into(), |
| image_extent: ash::vk::Extent2D { |
| width: dimensions[0], |
| height: dimensions[1], |
| }, |
| image_array_layers: layers, |
| image_usage: usage.into(), |
| image_sharing_mode: sh_mode, |
| queue_family_index_count: sh_count, |
| p_queue_family_indices: sh_indices, |
| pre_transform: transform.into(), |
| composite_alpha: composite_alpha.into(), |
| present_mode: present_mode.into(), |
| clipped: if clipped { |
| ash::vk::TRUE |
| } else { |
| ash::vk::FALSE |
| }, |
| old_swapchain: if let Some(ref old_swapchain) = old_swapchain { |
| old_swapchain.swapchain |
| } else { |
| ash::vk::SwapchainKHR::null() |
| }, |
| ..Default::default() |
| }; |
| |
| let mut output = MaybeUninit::uninit(); |
| check_errors(fns.khr_swapchain.create_swapchain_khr( |
| device.internal_object(), |
| &infos, |
| ptr::null(), |
| output.as_mut_ptr(), |
| ))?; |
| output.assume_init() |
| }; |
| |
| let image_handles = unsafe { |
| let mut num = 0; |
| check_errors(fns.khr_swapchain.get_swapchain_images_khr( |
| device.internal_object(), |
| swapchain, |
| &mut num, |
| ptr::null_mut(), |
| ))?; |
| |
| let mut images = Vec::with_capacity(num as usize); |
| check_errors(fns.khr_swapchain.get_swapchain_images_khr( |
| device.internal_object(), |
| swapchain, |
| &mut num, |
| images.as_mut_ptr(), |
| ))?; |
| images.set_len(num as usize); |
| images |
| }; |
| |
| let images = image_handles |
| .into_iter() |
| .map(|image| unsafe { |
| let dims = ImageDimensions::Dim2d { |
| width: dimensions[0], |
| height: dimensions[1], |
| array_layers: layers, |
| }; |
| |
| let img = UnsafeImage::from_raw( |
| device.clone(), |
| image, |
| usage, |
| format, |
| flags, |
| dims, |
| SampleCount::Sample1, |
| 1, |
| ); |
| |
| ImageEntry { |
| image: img, |
| undefined_layout: AtomicBool::new(true), |
| } |
| }) |
| .collect::<Vec<_>>(); |
| |
| let fullscreen_exclusive_held = old_swapchain |
| .as_ref() |
| .map(|old_swapchain| { |
| if old_swapchain.fullscreen_exclusive != FullscreenExclusive::AppControlled { |
| false |
| } else { |
| old_swapchain |
| .fullscreen_exclusive_held |
| .load(Ordering::SeqCst) |
| } |
| }) |
| .unwrap_or(false); |
| |
| let swapchain = Arc::new(Swapchain { |
| device: device.clone(), |
| surface: surface.clone(), |
| swapchain, |
| images, |
| stale: Mutex::new(false), |
| num_images, |
| format, |
| color_space, |
| dimensions, |
| layers, |
| usage: usage.clone(), |
| sharing_mode, |
| transform, |
| composite_alpha, |
| present_mode, |
| fullscreen_exclusive, |
| fullscreen_exclusive_held: AtomicBool::new(fullscreen_exclusive_held), |
| clipped, |
| }); |
| |
| let swapchain_images = unsafe { |
| let mut swapchain_images = Vec::with_capacity(swapchain.images.len()); |
| for n in 0..swapchain.images.len() { |
| swapchain_images.push(SwapchainImage::from_raw(swapchain.clone(), n)?); |
| } |
| swapchain_images |
| }; |
| |
| Ok((swapchain, swapchain_images)) |
| } |
| |
| /// Sets the number of images that will be created. |
| /// |
| /// The default is 2. |
| #[inline] |
| pub fn num_images(mut self, num_images: u32) -> Self { |
| self.num_images = num_images; |
| self |
| } |
| |
| /// Sets the pixel format that will be used for the images. |
| /// |
| /// The default is either `R8G8B8A8Unorm` or `B8G8R8A8Unorm`, whichever is supported. |
| #[inline] |
| pub fn format(mut self, format: Format) -> Self { |
| self.format = Some(format); |
| self |
| } |
| |
| /// Sets the color space that will be used for the images. |
| /// |
| /// The default is `SrgbNonLinear`. |
| #[inline] |
| pub fn color_space(mut self, color_space: ColorSpace) -> Self { |
| self.color_space = color_space; |
| self |
| } |
| |
| /// Sets the dimensions of the images. |
| /// |
| /// The default is `None`, which means the value of |
| /// [`Capabilities::current_extent`](crate::swapchain::Capabilities::current_extent) will be |
| /// used. Setting this will override it with a custom `Some` value. |
| #[inline] |
| pub fn dimensions(mut self, dimensions: [u32; 2]) -> Self { |
| self.dimensions = Some(dimensions); |
| self |
| } |
| |
| /// Sets the number of layers for each image. |
| /// |
| /// The default is 1. |
| #[inline] |
| pub fn layers(mut self, layers: u32) -> Self { |
| self.layers = layers; |
| self |
| } |
| |
| /// Sets how the images will be used. |
| /// |
| /// The default is `ImageUsage::none()`. |
| #[inline] |
| pub fn usage(mut self, usage: ImageUsage) -> Self { |
| self.usage = usage; |
| self |
| } |
| |
| /// Sets the sharing mode of the images. |
| /// |
| /// The default is `Exclusive`. |
| #[inline] |
| pub fn sharing_mode<S>(mut self, sharing_mode: S) -> Self |
| where |
| S: Into<SharingMode>, |
| { |
| self.sharing_mode = sharing_mode.into(); |
| self |
| } |
| |
| /// Sets the transform that is to be applied to the surface. |
| /// |
| /// The default is `Identity`. |
| #[inline] |
| pub fn transform(mut self, transform: SurfaceTransform) -> Self { |
| self.transform = transform; |
| self |
| } |
| |
| /// Sets how alpha values of the pixels in the image are to be treated. |
| /// |
| /// The default is `Opaque`. |
| #[inline] |
| pub fn composite_alpha(mut self, composite_alpha: CompositeAlpha) -> Self { |
| self.composite_alpha = composite_alpha; |
| self |
| } |
| |
| /// Sets the present mode for the swapchain. |
| /// |
| /// The default is `Fifo`. |
| #[inline] |
| pub fn present_mode(mut self, present_mode: PresentMode) -> Self { |
| self.present_mode = present_mode; |
| self |
| } |
| |
| /// Sets how fullscreen exclusivity is to be handled. |
| /// |
| /// The default is `Default`. |
| #[inline] |
| pub fn fullscreen_exclusive(mut self, fullscreen_exclusive: FullscreenExclusive) -> Self { |
| self.fullscreen_exclusive = fullscreen_exclusive; |
| self |
| } |
| |
| /// Sets whether the implementation is allowed to discard rendering operations that affect |
| /// regions of the surface which aren't visible. This is important to take into account if |
| /// your fragment shader has side-effects or if you want to read back the content of the image |
| /// afterwards. |
| /// |
| /// The default is `true`. |
| #[inline] |
| pub fn clipped(mut self, clipped: bool) -> Self { |
| self.clipped = clipped; |
| self |
| } |
| } |
| |
| /// Error that can happen when creation a swapchain. |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub enum SwapchainCreationError { |
| /// Not enough memory. |
| OomError(OomError), |
| /// The device was lost. |
| DeviceLost, |
| /// The surface was lost. |
| SurfaceLost, |
| /// The surface is already used by another swapchain. |
| SurfaceInUse, |
| /// The window is already in use by another API. |
| NativeWindowInUse, |
| /// The `VK_KHR_swapchain` extension was not enabled. |
| MissingExtensionKHRSwapchain, |
| /// The `VK_EXT_full_screen_exclusive` extension was not enabled. |
| MissingExtensionExtFullScreenExclusive, |
| /// Surface mismatch between old and new swapchain. |
| OldSwapchainSurfaceMismatch, |
| /// The old swapchain has already been used to recreate another one. |
| OldSwapchainAlreadyUsed, |
| /// The requested number of swapchain images is not supported by the surface. |
| UnsupportedMinImagesCount, |
| /// The requested number of swapchain images is not supported by the surface. |
| UnsupportedMaxImagesCount, |
| /// The requested image format is not supported by the surface. |
| UnsupportedFormat, |
| /// The requested dimensions are not supported by the surface. |
| UnsupportedDimensions, |
| /// The requested array layers count is not supported by the surface. |
| UnsupportedArrayLayers, |
| /// The requested image usage is not supported by the surface. |
| UnsupportedUsageFlags, |
| /// The requested surface transform is not supported by the surface. |
| UnsupportedSurfaceTransform, |
| /// The requested composite alpha is not supported by the surface. |
| UnsupportedCompositeAlpha, |
| /// The requested present mode is not supported by the surface. |
| UnsupportedPresentMode, |
| /// The image configuration is not supported by the physical device. |
| UnsupportedImageConfiguration, |
| } |
| |
| impl error::Error for SwapchainCreationError { |
| #[inline] |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| SwapchainCreationError::OomError(ref err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl fmt::Display for SwapchainCreationError { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!( |
| fmt, |
| "{}", |
| match *self { |
| SwapchainCreationError::OomError(_) => "not enough memory available", |
| SwapchainCreationError::DeviceLost => "the device was lost", |
| SwapchainCreationError::SurfaceLost => "the surface was lost", |
| SwapchainCreationError::SurfaceInUse => { |
| "the surface is already used by another swapchain" |
| } |
| SwapchainCreationError::NativeWindowInUse => { |
| "the window is already in use by another API" |
| } |
| SwapchainCreationError::MissingExtensionKHRSwapchain => { |
| "the `VK_KHR_swapchain` extension was not enabled" |
| } |
| SwapchainCreationError::MissingExtensionExtFullScreenExclusive => { |
| "the `VK_EXT_full_screen_exclusive` extension was not enabled" |
| } |
| SwapchainCreationError::OldSwapchainSurfaceMismatch => { |
| "surface mismatch between old and new swapchain" |
| } |
| SwapchainCreationError::OldSwapchainAlreadyUsed => { |
| "old swapchain has already been used to recreate a new one" |
| } |
| SwapchainCreationError::UnsupportedMinImagesCount => { |
| "the requested number of swapchain images is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedMaxImagesCount => { |
| "the requested number of swapchain images is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedFormat => { |
| "the requested image format is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedDimensions => { |
| "the requested dimensions are not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedArrayLayers => { |
| "the requested array layers count is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedUsageFlags => { |
| "the requested image usage is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedSurfaceTransform => { |
| "the requested surface transform is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedCompositeAlpha => { |
| "the requested composite alpha is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedPresentMode => { |
| "the requested present mode is not supported by the surface" |
| } |
| SwapchainCreationError::UnsupportedImageConfiguration => { |
| "the requested image configuration is not supported by the physical device" |
| } |
| } |
| ) |
| } |
| } |
| |
| impl From<Error> for SwapchainCreationError { |
| #[inline] |
| fn from(err: Error) -> SwapchainCreationError { |
| match err { |
| err @ Error::OutOfHostMemory => SwapchainCreationError::OomError(OomError::from(err)), |
| err @ Error::OutOfDeviceMemory => SwapchainCreationError::OomError(OomError::from(err)), |
| Error::DeviceLost => SwapchainCreationError::DeviceLost, |
| Error::SurfaceLost => SwapchainCreationError::SurfaceLost, |
| Error::NativeWindowInUse => SwapchainCreationError::NativeWindowInUse, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| impl From<OomError> for SwapchainCreationError { |
| #[inline] |
| fn from(err: OomError) -> SwapchainCreationError { |
| SwapchainCreationError::OomError(err) |
| } |
| } |
| |
| impl From<CapabilitiesError> for SwapchainCreationError { |
| #[inline] |
| fn from(err: CapabilitiesError) -> SwapchainCreationError { |
| match err { |
| CapabilitiesError::OomError(err) => SwapchainCreationError::OomError(err), |
| CapabilitiesError::SurfaceLost => SwapchainCreationError::SurfaceLost, |
| } |
| } |
| } |
| |
| /// Represents the moment when the GPU will have access to a swapchain image. |
| #[must_use] |
| pub struct SwapchainAcquireFuture<W> { |
| swapchain: Arc<Swapchain<W>>, |
| image_id: usize, |
| // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already |
| // happened. |
| semaphore: Option<Semaphore>, |
| // Fence that is signalled when the acquire is complete. Empty if the acquire has already |
| // happened. |
| fence: Option<Fence>, |
| finished: AtomicBool, |
| } |
| |
| impl<W> SwapchainAcquireFuture<W> { |
| /// Returns the index of the image in the list of images returned when creating the swapchain. |
| #[inline] |
| pub fn image_id(&self) -> usize { |
| self.image_id |
| } |
| |
| /// Returns the corresponding swapchain. |
| #[inline] |
| pub fn swapchain(&self) -> &Arc<Swapchain<W>> { |
| &self.swapchain |
| } |
| } |
| |
| unsafe impl<W> GpuFuture for SwapchainAcquireFuture<W> { |
| #[inline] |
| fn cleanup_finished(&mut self) {} |
| |
| #[inline] |
| unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> { |
| if let Some(ref semaphore) = self.semaphore { |
| let mut sem = SubmitSemaphoresWaitBuilder::new(); |
| sem.add_wait_semaphore(&semaphore); |
| Ok(SubmitAnyBuilder::SemaphoresWait(sem)) |
| } else { |
| Ok(SubmitAnyBuilder::Empty) |
| } |
| } |
| |
| #[inline] |
| fn flush(&self) -> Result<(), FlushError> { |
| Ok(()) |
| } |
| |
| #[inline] |
| unsafe fn signal_finished(&self) { |
| self.finished.store(true, Ordering::SeqCst); |
| } |
| |
| #[inline] |
| fn queue_change_allowed(&self) -> bool { |
| true |
| } |
| |
| #[inline] |
| fn queue(&self) -> Option<Arc<Queue>> { |
| None |
| } |
| |
| #[inline] |
| fn check_buffer_access( |
| &self, |
| _: &dyn BufferAccess, |
| _: bool, |
| _: &Queue, |
| ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { |
| Err(AccessCheckError::Unknown) |
| } |
| |
| #[inline] |
| fn check_image_access( |
| &self, |
| image: &dyn ImageAccess, |
| layout: ImageLayout, |
| _: bool, |
| _: &Queue, |
| ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { |
| let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap(); |
| if swapchain_image.image.internal_object() != image.inner().image.internal_object() { |
| return Err(AccessCheckError::Unknown); |
| } |
| |
| if self.swapchain.images[self.image_id] |
| .undefined_layout |
| .load(Ordering::Relaxed) |
| && layout != ImageLayout::Undefined |
| { |
| return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { |
| requested: layout, |
| })); |
| } |
| |
| if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc { |
| return Err(AccessCheckError::Denied( |
| AccessError::UnexpectedImageLayout { |
| allowed: ImageLayout::PresentSrc, |
| requested: layout, |
| }, |
| )); |
| } |
| |
| Ok(None) |
| } |
| } |
| |
| unsafe impl<W> DeviceOwned for SwapchainAcquireFuture<W> { |
| #[inline] |
| fn device(&self) -> &Arc<Device> { |
| &self.swapchain.device |
| } |
| } |
| |
| impl<W> Drop for SwapchainAcquireFuture<W> { |
| fn drop(&mut self) { |
| if let Some(ref fence) = self.fence { |
| fence.wait(None).unwrap(); // TODO: handle error? |
| self.semaphore = None; |
| } |
| |
| // TODO: if this future is destroyed without being presented, then eventually acquiring |
| // a new image will block forever ; difficulty: hard |
| } |
| } |
| |
| /// Error that can happen when calling `Swapchain::acquire_fullscreen_exclusive` or `Swapchain::release_fullscreen_exclusive` |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| #[repr(u32)] |
| pub enum FullscreenExclusiveError { |
| /// Not enough memory. |
| OomError(OomError), |
| |
| /// Operation could not be completed for driver specific reasons. |
| InitializationFailed, |
| |
| /// The surface is no longer accessible and must be recreated. |
| SurfaceLost, |
| |
| /// Fullscreen exclusivity is already acquired. |
| DoubleAcquire, |
| |
| /// Fullscreen exclusivity is not current acquired. |
| DoubleRelease, |
| |
| /// Swapchain is not in fullscreen exclusive app controlled mode |
| NotAppControlled, |
| } |
| |
| impl From<Error> for FullscreenExclusiveError { |
| #[inline] |
| fn from(err: Error) -> FullscreenExclusiveError { |
| match err { |
| err @ Error::OutOfHostMemory => FullscreenExclusiveError::OomError(OomError::from(err)), |
| err @ Error::OutOfDeviceMemory => { |
| FullscreenExclusiveError::OomError(OomError::from(err)) |
| } |
| Error::SurfaceLost => FullscreenExclusiveError::SurfaceLost, |
| Error::InitializationFailed => FullscreenExclusiveError::InitializationFailed, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| impl From<OomError> for FullscreenExclusiveError { |
| #[inline] |
| fn from(err: OomError) -> FullscreenExclusiveError { |
| FullscreenExclusiveError::OomError(err) |
| } |
| } |
| |
| impl error::Error for FullscreenExclusiveError { |
| #[inline] |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| FullscreenExclusiveError::OomError(ref err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl fmt::Display for FullscreenExclusiveError { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!( |
| fmt, |
| "{}", |
| match *self { |
| FullscreenExclusiveError::OomError(_) => "not enough memory", |
| FullscreenExclusiveError::SurfaceLost => { |
| "the surface of this swapchain is no longer valid" |
| } |
| FullscreenExclusiveError::InitializationFailed => { |
| "operation could not be completed for driver specific reasons" |
| } |
| FullscreenExclusiveError::DoubleAcquire => |
| "fullscreen exclusivity is already acquired", |
| FullscreenExclusiveError::DoubleRelease => "fullscreen exclusivity is not acquired", |
| FullscreenExclusiveError::NotAppControlled => { |
| "swapchain is not in fullscreen exclusive app controlled mode" |
| } |
| } |
| ) |
| } |
| } |
| |
| /// Error that can happen when calling `acquire_next_image`. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| #[repr(u32)] |
| pub enum AcquireError { |
| /// Not enough memory. |
| OomError(OomError), |
| |
| /// The connection to the device has been lost. |
| DeviceLost, |
| |
| /// The timeout of the function has been reached before an image was available. |
| Timeout, |
| |
| /// The surface is no longer accessible and must be recreated. |
| SurfaceLost, |
| |
| /// The swapchain has lost or doesn't have fullscreen exclusivity possibly for |
| /// implementation-specific reasons outside of the application’s control. |
| FullscreenExclusiveLost, |
| |
| /// The surface has changed in a way that makes the swapchain unusable. You must query the |
| /// surface's new properties and recreate a new swapchain if you want to continue drawing. |
| OutOfDate, |
| |
| /// Error during semaphore creation |
| SemaphoreError(SemaphoreError), |
| } |
| |
| impl error::Error for AcquireError { |
| #[inline] |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| AcquireError::OomError(ref err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl fmt::Display for AcquireError { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!( |
| fmt, |
| "{}", |
| match *self { |
| AcquireError::OomError(_) => "not enough memory", |
| AcquireError::DeviceLost => "the connection to the device has been lost", |
| AcquireError::Timeout => "no image is available for acquiring yet", |
| AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid", |
| AcquireError::OutOfDate => "the swapchain needs to be recreated", |
| AcquireError::FullscreenExclusiveLost => { |
| "the swapchain no longer has fullscreen exclusivity" |
| } |
| AcquireError::SemaphoreError(_) => "error creating semaphore", |
| } |
| ) |
| } |
| } |
| |
| impl From<SemaphoreError> for AcquireError { |
| fn from(err: SemaphoreError) -> Self { |
| AcquireError::SemaphoreError(err) |
| } |
| } |
| |
| impl From<OomError> for AcquireError { |
| #[inline] |
| fn from(err: OomError) -> AcquireError { |
| AcquireError::OomError(err) |
| } |
| } |
| |
| impl From<Error> for AcquireError { |
| #[inline] |
| fn from(err: Error) -> AcquireError { |
| match err { |
| err @ Error::OutOfHostMemory => AcquireError::OomError(OomError::from(err)), |
| err @ Error::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)), |
| Error::DeviceLost => AcquireError::DeviceLost, |
| Error::SurfaceLost => AcquireError::SurfaceLost, |
| Error::OutOfDate => AcquireError::OutOfDate, |
| Error::FullscreenExclusiveLost => AcquireError::FullscreenExclusiveLost, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| /// Represents a swapchain image being presented on the screen. |
| #[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"] |
| pub struct PresentFuture<P, W> |
| where |
| P: GpuFuture, |
| { |
| previous: P, |
| queue: Arc<Queue>, |
| swapchain: Arc<Swapchain<W>>, |
| image_id: usize, |
| present_region: Option<PresentRegion>, |
| // True if `flush()` has been called on the future, which means that the present command has |
| // been submitted. |
| flushed: AtomicBool, |
| // True if `signal_finished()` has been called on the future, which means that the future has |
| // been submitted and has already been processed by the GPU. |
| finished: AtomicBool, |
| } |
| |
| impl<P, W> PresentFuture<P, W> |
| where |
| P: GpuFuture, |
| { |
| /// Returns the index of the image in the list of images returned when creating the swapchain. |
| #[inline] |
| pub fn image_id(&self) -> usize { |
| self.image_id |
| } |
| |
| /// Returns the corresponding swapchain. |
| #[inline] |
| pub fn swapchain(&self) -> &Arc<Swapchain<W>> { |
| &self.swapchain |
| } |
| } |
| |
| unsafe impl<P, W> GpuFuture for PresentFuture<P, W> |
| where |
| P: GpuFuture, |
| { |
| #[inline] |
| fn cleanup_finished(&mut self) { |
| self.previous.cleanup_finished(); |
| } |
| |
| #[inline] |
| unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> { |
| if self.flushed.load(Ordering::SeqCst) { |
| return Ok(SubmitAnyBuilder::Empty); |
| } |
| |
| let queue = self.previous.queue().map(|q| q.clone()); |
| |
| // TODO: if the swapchain image layout is not PRESENT, should add a transition command |
| // buffer |
| |
| Ok(match self.previous.build_submission()? { |
| SubmitAnyBuilder::Empty => { |
| let mut builder = SubmitPresentBuilder::new(); |
| builder.add_swapchain( |
| &self.swapchain, |
| self.image_id as u32, |
| self.present_region.as_ref(), |
| ); |
| SubmitAnyBuilder::QueuePresent(builder) |
| } |
| SubmitAnyBuilder::SemaphoresWait(sem) => { |
| let mut builder: SubmitPresentBuilder = sem.into(); |
| builder.add_swapchain( |
| &self.swapchain, |
| self.image_id as u32, |
| self.present_region.as_ref(), |
| ); |
| SubmitAnyBuilder::QueuePresent(builder) |
| } |
| SubmitAnyBuilder::CommandBuffer(cb) => { |
| // submit the command buffer by flushing previous. |
| // Since the implementation should remember being flushed it's safe to call build_submission multiple times |
| self.previous.flush()?; |
| |
| let mut builder = SubmitPresentBuilder::new(); |
| builder.add_swapchain( |
| &self.swapchain, |
| self.image_id as u32, |
| self.present_region.as_ref(), |
| ); |
| SubmitAnyBuilder::QueuePresent(builder) |
| } |
| SubmitAnyBuilder::BindSparse(cb) => { |
| // submit the command buffer by flushing previous. |
| // Since the implementation should remember being flushed it's safe to call build_submission multiple times |
| self.previous.flush()?; |
| |
| let mut builder = SubmitPresentBuilder::new(); |
| builder.add_swapchain( |
| &self.swapchain, |
| self.image_id as u32, |
| self.present_region.as_ref(), |
| ); |
| SubmitAnyBuilder::QueuePresent(builder) |
| } |
| SubmitAnyBuilder::QueuePresent(present) => { |
| unimplemented!() // TODO: |
| /*present.submit(); |
| let mut builder = SubmitPresentBuilder::new(); |
| builder.add_swapchain(self.command_buffer.inner(), self.image_id); |
| SubmitAnyBuilder::CommandBuffer(builder)*/ |
| } |
| }) |
| } |
| |
| #[inline] |
| fn flush(&self) -> Result<(), FlushError> { |
| unsafe { |
| // If `flushed` already contains `true`, then `build_submission` will return `Empty`. |
| |
| let build_submission_result = self.build_submission(); |
| |
| if let &Err(FlushError::FullscreenExclusiveLost) = &build_submission_result { |
| self.swapchain |
| .fullscreen_exclusive_held |
| .store(false, Ordering::SeqCst); |
| } |
| |
| match build_submission_result? { |
| SubmitAnyBuilder::Empty => {} |
| SubmitAnyBuilder::QueuePresent(present) => { |
| let present_result = present.submit(&self.queue); |
| |
| if let &Err(SubmitPresentError::FullscreenExclusiveLost) = &present_result { |
| self.swapchain |
| .fullscreen_exclusive_held |
| .store(false, Ordering::SeqCst); |
| } |
| |
| present_result?; |
| } |
| _ => unreachable!(), |
| } |
| |
| self.flushed.store(true, Ordering::SeqCst); |
| Ok(()) |
| } |
| } |
| |
| #[inline] |
| unsafe fn signal_finished(&self) { |
| self.flushed.store(true, Ordering::SeqCst); |
| self.finished.store(true, Ordering::SeqCst); |
| self.previous.signal_finished(); |
| } |
| |
| #[inline] |
| fn queue_change_allowed(&self) -> bool { |
| false |
| } |
| |
| #[inline] |
| fn queue(&self) -> Option<Arc<Queue>> { |
| debug_assert!(match self.previous.queue() { |
| None => true, |
| Some(q) => q.is_same(&self.queue), |
| }); |
| |
| Some(self.queue.clone()) |
| } |
| |
| #[inline] |
| fn check_buffer_access( |
| &self, |
| buffer: &dyn BufferAccess, |
| exclusive: bool, |
| queue: &Queue, |
| ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { |
| self.previous.check_buffer_access(buffer, exclusive, queue) |
| } |
| |
| #[inline] |
| fn check_image_access( |
| &self, |
| image: &dyn ImageAccess, |
| layout: ImageLayout, |
| exclusive: bool, |
| queue: &Queue, |
| ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { |
| let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap(); |
| if swapchain_image.image.internal_object() == image.inner().image.internal_object() { |
| // This future presents the swapchain image, which "unlocks" it. Therefore any attempt |
| // to use this swapchain image afterwards shouldn't get granted automatic access. |
| // Instead any attempt to access the image afterwards should get an authorization from |
| // a later swapchain acquire future. Hence why we return `Unknown` here. |
| Err(AccessCheckError::Unknown) |
| } else { |
| self.previous |
| .check_image_access(image, layout, exclusive, queue) |
| } |
| } |
| } |
| |
| unsafe impl<P, W> DeviceOwned for PresentFuture<P, W> |
| where |
| P: GpuFuture, |
| { |
| #[inline] |
| fn device(&self) -> &Arc<Device> { |
| self.queue.device() |
| } |
| } |
| |
| impl<P, W> Drop for PresentFuture<P, W> |
| where |
| P: GpuFuture, |
| { |
| fn drop(&mut self) { |
| unsafe { |
| if !*self.finished.get_mut() { |
| match self.flush() { |
| Ok(()) => { |
| // Block until the queue finished. |
| self.queue().unwrap().wait().unwrap(); |
| self.previous.signal_finished(); |
| } |
| Err(_) => { |
| // In case of error we simply do nothing, as there's nothing to do |
| // anyway. |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| pub struct AcquiredImage { |
| pub id: usize, |
| pub suboptimal: bool, |
| } |
| |
| /// Unsafe variant of `acquire_next_image`. |
| /// |
| /// # Safety |
| /// |
| /// - The semaphore and/or the fence must be kept alive until it is signaled. |
| /// - The swapchain must not have been replaced by being passed as the old swapchain when creating |
| /// a new one. |
| pub unsafe fn acquire_next_image_raw<W>( |
| swapchain: &Swapchain<W>, |
| timeout: Option<Duration>, |
| semaphore: Option<&Semaphore>, |
| fence: Option<&Fence>, |
| ) -> Result<AcquiredImage, AcquireError> { |
| let fns = swapchain.device.fns(); |
| |
| let timeout_ns = if let Some(timeout) = timeout { |
| timeout |
| .as_secs() |
| .saturating_mul(1_000_000_000) |
| .saturating_add(timeout.subsec_nanos() as u64) |
| } else { |
| u64::MAX |
| }; |
| |
| let mut out = MaybeUninit::uninit(); |
| let r = check_errors( |
| fns.khr_swapchain.acquire_next_image_khr( |
| swapchain.device.internal_object(), |
| swapchain.swapchain, |
| timeout_ns, |
| semaphore |
| .map(|s| s.internal_object()) |
| .unwrap_or(ash::vk::Semaphore::null()), |
| fence |
| .map(|f| f.internal_object()) |
| .unwrap_or(ash::vk::Fence::null()), |
| out.as_mut_ptr(), |
| ), |
| )?; |
| |
| let out = out.assume_init(); |
| let (id, suboptimal) = match r { |
| Success::Success => (out as usize, false), |
| Success::Suboptimal => (out as usize, true), |
| Success::NotReady => return Err(AcquireError::Timeout), |
| Success::Timeout => return Err(AcquireError::Timeout), |
| s => panic!("unexpected success value: {:?}", s), |
| }; |
| |
| Ok(AcquiredImage { id, suboptimal }) |
| } |