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