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