vulkano/swapchain/
mod.rs

1//! Link between Vulkan and a window and/or the screen.
2//!
3//! Before you can draw on the screen or a window, you have to create two objects:
4//!
5//! - Create a `Surface` object that represents the location where the image will show up (either a
6//!   window or a monitor).
7//! - Create a `Swapchain` that uses that `Surface`.
8//!
9//! Creating a surface can be done with only an `Instance` object. However creating a swapchain
10//! requires a `Device` object.
11//!
12//! Once you have a swapchain, you can retrieve `Image` objects from it and draw to them just like
13//! you would draw on any other image.
14//!
15//! # Surfaces
16//!
17//! A surface is an object that represents a location where to render. It can be created from an
18//! instance and either a window handle (in a platform-specific way) or a monitor.
19//!
20//! In order to use surfaces, you will have to enable the `VK_KHR_surface` extension on the
21//! instance. See the `instance` module for more information about how to enable extensions.
22//!
23//! ## Creating a surface from a window
24//!
25//! There are 5 extensions that each allow you to create a surface from a type of window:
26//!
27//! - `VK_KHR_xlib_surface`
28//! - `VK_KHR_xcb_surface`
29//! - `VK_KHR_wayland_surface`
30//! - `VK_KHR_android_surface`
31//! - `VK_KHR_win32_surface`
32//!
33//! For example if you want to create a surface from an Android surface, you will have to enable
34//! the `VK_KHR_android_surface` extension and use `Surface::from_android`.
35//! See the documentation of `Surface` for all the possible constructors.
36//!
37//! Trying to use one of these functions without enabling the proper extension will result in an
38//! error.
39//!
40//! **Note that the `Surface` object is potentially unsafe**. It is your responsibility to
41//! keep the window alive for at least as long as the surface exists. In many cases Surface
42//! may be able to do this for you, if you pass it ownership of your Window (or a
43//! reference-counting container for it).
44//!
45//! ### Examples
46//!
47//! ```no_run
48//! use std::ptr;
49//! use vulkano::{
50//!     instance::{Instance, InstanceCreateInfo, InstanceExtensions},
51//!     swapchain::Surface,
52//!     Version, VulkanLibrary,
53//! };
54//!
55//! let instance = {
56//!     let library = VulkanLibrary::new()
57//!         .unwrap_or_else(|err| panic!("Couldn't load Vulkan library: {:?}", err));
58//!
59//!     let extensions = InstanceExtensions {
60//!         khr_surface: true,
61//!         khr_win32_surface: true, // If you don't enable this, `from_hwnd` will fail.
62//!         ..InstanceExtensions::empty()
63//!     };
64//!
65//!     Instance::new(
66//!         library,
67//!         InstanceCreateInfo {
68//!             enabled_extensions: extensions,
69//!             ..Default::default()
70//!         },
71//!     )
72//!     .unwrap_or_else(|err| panic!("Couldn't create instance: {:?}", err))
73//! };
74//!
75//! # use std::sync::Arc;
76//! # struct Window(*const u32);
77//! # impl Window { fn hwnd(&self) -> *const u32 { self.0 } }
78//! # unsafe impl Send for Window {}
79//! # unsafe impl Sync for Window {}
80//! # fn build_window() -> Arc<Window> { Arc::new(Window(ptr::null())) }
81//! let window = build_window(); // Third-party function, not provided by vulkano
82//! let _surface = {
83//!     let hinstance: ash::vk::HINSTANCE = 0; // Windows-specific object
84//!     unsafe {
85//!         Surface::from_win32(
86//!             instance.clone(),
87//!             hinstance,
88//!             window.hwnd() as ash::vk::HWND,
89//!             Some(window),
90//!         )
91//!     }
92//!     .unwrap()
93//! };
94//! ```
95//!
96//! # Swapchains
97//!
98//! A surface represents a location on the screen and can be created from an instance. Once you
99//! have a surface, the next step is to create a swapchain. Creating a swapchain requires a device,
100//! and allocates the resources that will be used to display images on the screen.
101//!
102//! A swapchain is composed of one or multiple images. Each image of the swapchain is presented in
103//! turn on the screen, one after another. More information below.
104//!
105//! Swapchains have several properties:
106//!
107//!  - The number of images that will cycle on the screen.
108//!  - The format of the images.
109//!  - The 2D dimensions of the images, plus a number of layers, for a total of three dimensions.
110//!  - The usage of the images, similar to creating other images.
111//!  - The queue families that are going to use the images, similar to creating other images.
112//!  - An additional transformation (rotation or mirroring) to perform on the final output.
113//!  - How the alpha of the final output will be interpreted.
114//!  - How to perform the cycling between images in regard to vsync.
115//!
116//! You can query the supported values of all these properties from the physical device.
117//!
118//! ## Creating a swapchain
119//!
120//! In order to create a swapchain, you will first have to enable the `VK_KHR_swapchain` extension
121//! on the device (and not on the instance like `VK_KHR_surface`):
122//!
123//! ```no_run
124//! # use vulkano::device::DeviceExtensions;
125//! let ext = DeviceExtensions {
126//!     khr_swapchain: true,
127//!     ..DeviceExtensions::empty()
128//! };
129//! ```
130//!
131//! Then, query the capabilities of the surface with
132//! [`PhysicalDevice::surface_capabilities`](crate::device::physical::PhysicalDevice::surface_capabilities)
133//! and
134//! [`PhysicalDevice::surface_formats`](crate::device::physical::PhysicalDevice::surface_formats)
135//! and choose which values you are going to use.
136//!
137//! ```no_run
138//! # use std::{error::Error, sync::Arc};
139//! # use vulkano::device::Device;
140//! # use vulkano::swapchain::Surface;
141//! # use std::cmp::{max, min};
142//! # fn choose_caps(device: Arc<Device>, surface: Arc<Surface>) -> Result<(), Box<dyn Error>> {
143//! let surface_capabilities = device
144//!     .physical_device()
145//!     .surface_capabilities(&surface, Default::default())?;
146//!
147//! // Use the current window size or some fixed resolution.
148//! let image_extent = surface_capabilities.current_extent.unwrap_or([640, 480]);
149//!
150//! // Try to use double-buffering.
151//! let min_image_count = match surface_capabilities.max_image_count {
152//!     None => max(2, surface_capabilities.min_image_count),
153//!     Some(limit) => min(max(2, surface_capabilities.min_image_count), limit)
154//! };
155//!
156//! // Preserve the current surface transform.
157//! let pre_transform = surface_capabilities.current_transform;
158//!
159//! // Use the first available format.
160//! let (image_format, color_space) = device
161//!     .physical_device()
162//!     .surface_formats(&surface, Default::default())?[0];
163//! # Ok(())
164//! # }
165//! ```
166//!
167//! Then, call [`Swapchain::new`].
168//!
169//! ```no_run
170//! # use std::{error::Error, sync::Arc};
171//! # use vulkano::device::{Device, Queue};
172//! # use vulkano::image::ImageUsage;
173//! # use vulkano::sync::SharingMode;
174//! # use vulkano::format::Format;
175//! # use vulkano::swapchain::{Surface, Swapchain, SurfaceTransform, PresentMode, CompositeAlpha, ColorSpace, FullScreenExclusive, SwapchainCreateInfo};
176//! # fn create_swapchain(
177//! #     device: Arc<Device>, surface: Arc<Surface>,
178//! #     min_image_count: u32, image_format: Format, image_extent: [u32; 2],
179//! #     pre_transform: SurfaceTransform, composite_alpha: CompositeAlpha,
180//! #     present_mode: PresentMode, full_screen_exclusive: FullScreenExclusive
181//! # ) -> Result<(), Box<dyn Error>> {
182//! // Create the swapchain and its images.
183//! let (swapchain, images) = Swapchain::new(
184//!     // Create the swapchain in this `device`'s memory.
185//!     device,
186//!     // The surface where the images will be presented.
187//!     surface,
188//!     // The creation parameters.
189//!     SwapchainCreateInfo {
190//!         // How many images to use in the swapchain.
191//!         min_image_count,
192//!         // The format of the images.
193//!         image_format,
194//!         // The size of each image.
195//!         image_extent,
196//!         // The created swapchain images will be used as a color attachment for rendering.
197//!         image_usage: ImageUsage::COLOR_ATTACHMENT,
198//!         // What transformation to use with the surface.
199//!         pre_transform,
200//!         // How to handle the alpha channel.
201//!         composite_alpha,
202//!         // How to present images.
203//!         present_mode,
204//!         // How to handle full-screen exclusivity
205//!         full_screen_exclusive,
206//!         ..Default::default()
207//!     }
208//! )?;
209//!
210//! # Ok(())
211//! # }
212//! ```
213//!
214//! Creating a swapchain not only returns the swapchain object, but also all the images that belong
215//! to it.
216//!
217//! ## Acquiring and presenting images
218//!
219//! Once you created a swapchain and retrieved all the images that belong to it (see previous
220//! section), you can draw on it. This is done in three steps:
221//!
222//!  - Call `swapchain::acquire_next_image`. This function will return the index of the image
223//!    (within the list returned by `Swapchain::new`) that is available to draw, plus a future
224//!    representing the moment when the GPU will gain access to that image.
225//!  - Draw on that image just like you would draw to any other image (see the documentation of the
226//!    `pipeline` module). You need to chain the draw after the future that was returned by
227//!    `acquire_next_image`.
228//!  - Call `Swapchain::present` with the same index and by chaining the futures, in order to tell
229//!    the implementation that you are finished drawing to the image and that it can queue a
230//!    command to present the image on the screen after the draw operations are finished.
231//!
232//! ```
233//! use vulkano::swapchain::{self, SwapchainPresentInfo};
234//! use vulkano::sync::GpuFuture;
235//! # let queue: ::std::sync::Arc<::vulkano::device::Queue> = return;
236//! # let mut swapchain: ::std::sync::Arc<swapchain::Swapchain> = return;
237//! // let mut (swapchain, images) = Swapchain::new(...);
238//! loop {
239//!     # let mut command_buffer: ::std::sync::Arc<::vulkano::command_buffer::PrimaryAutoCommandBuffer> = return;
240//!     let (image_index, suboptimal, acquire_future)
241//!         = swapchain::acquire_next_image(swapchain.clone(), None).unwrap();
242//!
243//!     // The command_buffer contains the draw commands that modify the framebuffer
244//!     // constructed from images[image_index]
245//!     acquire_future
246//!         .then_execute(queue.clone(), command_buffer)
247//!         .unwrap()
248//!         .then_swapchain_present(
249//!             queue.clone(),
250//!             SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
251//!         )
252//!         .then_signal_fence_and_flush()
253//!         .unwrap();
254//! }
255//! ```
256//!
257//! ## Recreating a swapchain
258//!
259//! In some situations, the swapchain will become invalid by itself. This includes for example when
260//! the window is resized (as the images of the swapchain will no longer match the window's) or,
261//! on Android, when the application went to the background and goes back to the foreground.
262//!
263//! In this situation, acquiring a swapchain image or presenting it will return an error. Rendering
264//! to an image of that swapchain will not produce any error, but may or may not work. To continue
265//! rendering, you will need to *recreate* the swapchain by creating a new swapchain and passing
266//! as last parameter the old swapchain.
267//!
268//! ```
269//! use vulkano::{
270//!     swapchain::{self, SwapchainCreateInfo, SwapchainPresentInfo},
271//!     sync::GpuFuture,
272//!     Validated, VulkanError,
273//! };
274//!
275//! // let (swapchain, images) = Swapchain::new(...);
276//! # let mut swapchain: ::std::sync::Arc<::vulkano::swapchain::Swapchain> = return;
277//! # let mut images: Vec<::std::sync::Arc<::vulkano::image::Image>> = return;
278//! # let queue: ::std::sync::Arc<::vulkano::device::Queue> = return;
279//! let mut recreate_swapchain = false;
280//!
281//! loop {
282//!     if recreate_swapchain {
283//!         let (new_swapchain, new_images) = swapchain
284//!             .recreate(SwapchainCreateInfo {
285//!                 image_extent: [1024, 768],
286//!                 ..swapchain.create_info()
287//!             })
288//!             .unwrap();
289//!         swapchain = new_swapchain;
290//!         images = new_images;
291//!         recreate_swapchain = false;
292//!     }
293//!
294//!     let (image_index, suboptimal, acq_future) =
295//!         match swapchain::acquire_next_image(swapchain.clone(), None).map_err(Validated::unwrap)
296//!         {
297//!             Ok(r) => r,
298//!             Err(VulkanError::OutOfDate) => {
299//!                 recreate_swapchain = true;
300//!                 continue;
301//!             }
302//!             Err(err) => panic!("{:?}", err),
303//!         };
304//!
305//!     // ...
306//!
307//!     let final_future = acq_future
308//!         // .then_execute(...)
309//!         .then_swapchain_present(
310//!             queue.clone(),
311//!             SwapchainPresentInfo::swapchain_image_index(swapchain.clone(), image_index),
312//!         )
313//!         .then_signal_fence_and_flush()
314//!         .unwrap(); // TODO: PresentError?
315//!
316//!     if suboptimal {
317//!         recreate_swapchain = true;
318//!     }
319//! }
320//! ```
321
322pub use self::{acquire_present::*, surface::*};
323
324mod acquire_present;
325mod surface;
326
327use crate::{
328    device::{Device, DeviceOwned},
329    format::Format,
330    image::{Image, ImageCreateFlags, ImageFormatInfo, ImageTiling, ImageType, ImageUsage},
331    instance::InstanceOwnedDebugWrapper,
332    macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum},
333    sync::Sharing,
334    Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
335    VulkanObject,
336};
337use parking_lot::Mutex;
338use smallvec::SmallVec;
339use std::{
340    fmt::Debug,
341    mem::MaybeUninit,
342    num::NonZeroU64,
343    ptr,
344    sync::{
345        atomic::{AtomicBool, AtomicU64, Ordering},
346        Arc,
347    },
348    time::Duration,
349};
350
351/// Contains the swapping system and the images that can be shown on a surface.
352#[derive(Debug)]
353pub struct Swapchain {
354    handle: ash::vk::SwapchainKHR,
355    device: InstanceOwnedDebugWrapper<Arc<Device>>,
356    surface: InstanceOwnedDebugWrapper<Arc<Surface>>,
357    id: NonZeroU64,
358
359    flags: SwapchainCreateFlags,
360    min_image_count: u32,
361    image_format: Format,
362    image_view_formats: Vec<Format>,
363    image_color_space: ColorSpace,
364    image_extent: [u32; 2],
365    image_array_layers: u32,
366    image_usage: ImageUsage,
367    image_sharing: Sharing<SmallVec<[u32; 4]>>,
368    pre_transform: SurfaceTransform,
369    composite_alpha: CompositeAlpha,
370    present_mode: PresentMode,
371    present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>,
372    clipped: bool,
373    scaling_behavior: Option<PresentScaling>,
374    present_gravity: Option<[PresentGravity; 2]>,
375    full_screen_exclusive: FullScreenExclusive,
376    win32_monitor: Option<Win32Monitor>,
377
378    prev_present_id: AtomicU64,
379
380    // Whether full-screen exclusive is currently held.
381    full_screen_exclusive_held: AtomicBool,
382
383    // The images of this swapchain.
384    images: Vec<ImageEntry>,
385
386    // If true, that means we have tried to use this swapchain to recreate a new swapchain. The
387    // current swapchain can no longer be used for anything except presenting already-acquired
388    // images.
389    //
390    // We use a `Mutex` instead of an `AtomicBool` because we want to keep that locked while
391    // we acquire the image.
392    is_retired: Mutex<bool>,
393}
394
395#[derive(Debug)]
396struct ImageEntry {
397    handle: ash::vk::Image,
398    layout_initialized: AtomicBool,
399}
400
401impl Swapchain {
402    /// Creates a new `Swapchain`.
403    ///
404    /// This function returns the swapchain plus a list of the images that belong to the
405    /// swapchain. The order in which the images are returned is important for the
406    /// `acquire_next_image` and `present` functions.
407    ///
408    /// # Panics
409    ///
410    /// - Panics if the device and the surface don't belong to the same instance.
411    /// - Panics if `create_info.usage` is empty.
412    #[inline]
413    pub fn new(
414        device: Arc<Device>,
415        surface: Arc<Surface>,
416        create_info: SwapchainCreateInfo,
417    ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Validated<VulkanError>> {
418        Self::validate_new_inner(&device, &surface, &create_info)?;
419
420        Ok(unsafe { Self::new_unchecked(device, surface, create_info) }?)
421    }
422
423    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
424    #[inline]
425    pub unsafe fn new_unchecked(
426        device: Arc<Device>,
427        surface: Arc<Surface>,
428        create_info: SwapchainCreateInfo,
429    ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
430        let (handle, image_handles) =
431            unsafe { Self::new_inner_unchecked(&device, &surface, &create_info, None) }?;
432
433        unsafe { Self::from_handle(device, handle, image_handles, surface, create_info) }
434    }
435
436    /// Creates a new swapchain from this one.
437    ///
438    /// Use this when a swapchain has become invalidated, such as due to window resizes.
439    ///
440    /// # Panics
441    ///
442    /// - Panics if `create_info.usage` is empty.
443    pub fn recreate(
444        self: &Arc<Self>,
445        create_info: SwapchainCreateInfo,
446    ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Validated<VulkanError>> {
447        Self::validate_new_inner(&self.device, &self.surface, &create_info)?;
448
449        {
450            let mut is_retired = self.is_retired.lock();
451
452            // The swapchain has already been used to create a new one.
453            if *is_retired {
454                return Err(Box::new(ValidationError {
455                    context: "self".into(),
456                    problem: "has already been used to recreate a swapchain".into(),
457                    vuids: &["VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933"],
458                    ..Default::default()
459                })
460                .into());
461            } else {
462                // According to the documentation of VkSwapchainCreateInfoKHR:
463                //
464                // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE,
465                // > any images not acquired by the application may be freed by the implementation,
466                // > which may occur even if creation of the new swapchain fails.
467                //
468                // Therefore, we set retired to true and keep it to true even if the call to `vkCreateSwapchainKHR` below fails.
469                *is_retired = true;
470            }
471        }
472
473        Ok(unsafe { self.recreate_unchecked(create_info) }?)
474    }
475
476    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
477    pub unsafe fn recreate_unchecked(
478        self: &Arc<Self>,
479        create_info: SwapchainCreateInfo,
480    ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
481        // According to the documentation of VkSwapchainCreateInfoKHR:
482        //
483        // > Upon calling vkCreateSwapchainKHR with a oldSwapchain that is not VK_NULL_HANDLE,
484        // > any images not acquired by the application may be freed by the implementation,
485        // > which may occur even if creation of the new swapchain fails.
486        //
487        // Therefore, we set retired to true and keep it to true,
488        // even if the call to `vkCreateSwapchainKHR` below fails.
489        *self.is_retired.lock() = true;
490
491        let (handle, image_handles) = unsafe {
492            Self::new_inner_unchecked(&self.device, &self.surface, &create_info, Some(self))
493        }?;
494
495        let (swapchain, swapchain_images) = unsafe {
496            Self::from_handle(
497                self.device.clone(),
498                handle,
499                image_handles,
500                self.surface.clone(),
501                create_info,
502            )
503        }?;
504
505        if self.full_screen_exclusive == FullScreenExclusive::ApplicationControlled {
506            swapchain.full_screen_exclusive_held.store(
507                self.full_screen_exclusive_held.load(Ordering::Relaxed),
508                Ordering::Relaxed,
509            );
510        };
511
512        Ok((swapchain, swapchain_images))
513    }
514
515    fn validate_new_inner(
516        device: &Device,
517        surface: &Surface,
518        create_info: &SwapchainCreateInfo,
519    ) -> Result<(), Box<ValidationError>> {
520        if !device.enabled_extensions().khr_swapchain {
521            return Err(Box::new(ValidationError {
522                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
523                    "khr_swapchain",
524                )])]),
525                ..Default::default()
526            }));
527        }
528
529        create_info
530            .validate(device)
531            .map_err(|err| err.add_context("create_info"))?;
532
533        let &SwapchainCreateInfo {
534            flags: _,
535            min_image_count,
536            image_format,
537            image_view_formats: _,
538            image_color_space,
539            image_extent,
540            image_array_layers,
541            image_usage,
542            image_sharing: _,
543            pre_transform,
544            composite_alpha,
545            present_mode,
546            ref present_modes,
547            clipped: _,
548            scaling_behavior,
549            present_gravity,
550            full_screen_exclusive,
551            win32_monitor,
552            _ne: _,
553        } = create_info;
554
555        assert_eq!(device.instance(), surface.instance());
556
557        // VUID-VkSwapchainCreateInfoKHR-surface-01270
558        if !device.active_queue_family_indices().iter().any(|&index| {
559            unsafe {
560                device
561                    .physical_device()
562                    .surface_support_unchecked(index, surface)
563            }
564            .unwrap_or_default()
565        }) {
566            return Err(Box::new(ValidationError {
567                context: "surface".into(),
568                problem: "is not supported by the physical device".into(),
569                vuids: &["VUID-VkSwapchainCreateInfoKHR-surface-01270"],
570                ..Default::default()
571            }));
572        }
573
574        let surface_capabilities = unsafe {
575            device.physical_device().surface_capabilities_unchecked(
576                surface,
577                SurfaceInfo {
578                    present_mode: device
579                        .enabled_extensions()
580                        .ext_swapchain_maintenance1
581                        .then_some(present_mode),
582                    full_screen_exclusive,
583                    win32_monitor: win32_monitor
584                        .filter(|_| full_screen_exclusive != FullScreenExclusive::Default),
585                    ..Default::default()
586                },
587            )
588        }
589        .map_err(|_err| {
590            Box::new(ValidationError {
591                problem: "`PhysicalDevice::surface_capabilities` \
592                            returned an error"
593                    .into(),
594                ..Default::default()
595            })
596        })?;
597        let surface_formats = unsafe {
598            device.physical_device().surface_formats_unchecked(
599                surface,
600                SurfaceInfo {
601                    present_mode: device
602                        .enabled_extensions()
603                        .ext_swapchain_maintenance1
604                        .then_some(present_mode),
605                    full_screen_exclusive,
606                    win32_monitor: win32_monitor.filter(|_| {
607                        surface.api() == SurfaceApi::Win32
608                            && full_screen_exclusive == FullScreenExclusive::ApplicationControlled
609                    }),
610                    ..Default::default()
611                },
612            )
613        }
614        .map_err(|_err| {
615            Box::new(ValidationError {
616                problem: "`PhysicalDevice::surface_formats` \
617                            returned an error"
618                    .into(),
619                ..Default::default()
620            })
621        })?;
622        let surface_present_modes = unsafe {
623            device.physical_device().surface_present_modes_unchecked(
624                surface,
625                SurfaceInfo {
626                    full_screen_exclusive,
627                    win32_monitor: win32_monitor.filter(|_| {
628                        surface.api() == SurfaceApi::Win32
629                            && full_screen_exclusive == FullScreenExclusive::ApplicationControlled
630                    }),
631                    ..Default::default()
632                },
633            )
634        }
635        .map_err(|_err| {
636            Box::new(ValidationError {
637                problem: "`PhysicalDevice::surface_present_modes` \
638                            returned an error"
639                    .into(),
640                ..Default::default()
641            })
642        })?;
643
644        if surface_capabilities
645            .max_image_count
646            .is_some_and(|c| min_image_count > c)
647        {
648            return Err(Box::new(ValidationError {
649                problem: "`create_info.min_image_count` is greater than the `max_image_count` \
650                    value of the capabilities of `surface`"
651                    .into(),
652                vuids: &["VUID-VkSwapchainCreateInfoKHR-minImageCount-01272"],
653                ..Default::default()
654            }));
655        }
656
657        if min_image_count < surface_capabilities.min_image_count {
658            return Err(Box::new(ValidationError {
659                problem: "`create_info.min_image_count` is less than the `min_image_count` \
660                    value of the capabilities of `surface`"
661                    .into(),
662                vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-02839"],
663                ..Default::default()
664            }));
665        }
666
667        if !surface_formats
668            .iter()
669            .any(|&fc| fc == (image_format, image_color_space))
670        {
671            return Err(Box::new(ValidationError {
672                problem: "the combination of `create_info.image_format` and \
673                    `create_info.image_color_space` is not supported for `surface`"
674                    .into(),
675                vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01273"],
676                ..Default::default()
677            }));
678        }
679
680        if image_array_layers > surface_capabilities.max_image_array_layers {
681            return Err(Box::new(ValidationError {
682                problem: "`create_info.image_array_layers` is greater than the \
683                    `max_image_array_layers` value of the capabilities of `surface`"
684                    .into(),
685                vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"],
686                ..Default::default()
687            }));
688        }
689
690        if matches!(
691            present_mode,
692            PresentMode::Immediate
693                | PresentMode::Mailbox
694                | PresentMode::Fifo
695                | PresentMode::FifoRelaxed
696        ) && !surface_capabilities
697            .supported_usage_flags
698            .contains(image_usage)
699        {
700            return Err(Box::new(ValidationError {
701                problem: "`create_info.present_mode` is `PresentMode::Immediate`, \
702                    `PresentMode::Mailbox`, `PresentMode::Fifo` or `PresentMode::FifoRelaxed`, \
703                    and `create_info.image_usage` contains flags that are not set in \
704                    the `supported_usage_flags` value of the capabilities of `surface`"
705                    .into(),
706                vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01427"],
707                ..Default::default()
708            }));
709        }
710
711        if !surface_capabilities
712            .supported_transforms
713            .contains_enum(pre_transform)
714        {
715            return Err(Box::new(ValidationError {
716                problem: "`create_info.pre_transform` is not present in the \
717                    `supported_transforms` value of the capabilities of `surface`"
718                    .into(),
719                vuids: &["VUID-VkSwapchainCreateInfoKHR-preTransform-01279"],
720                ..Default::default()
721            }));
722        }
723
724        if !surface_capabilities
725            .supported_composite_alpha
726            .contains_enum(composite_alpha)
727        {
728            return Err(Box::new(ValidationError {
729                problem: "`create_info.composite_alpha` is not present in the \
730                    `supported_composite_alpha` value of the capabilities of `surface`"
731                    .into(),
732                vuids: &["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280"],
733                ..Default::default()
734            }));
735        }
736
737        if !surface_present_modes.contains(&present_mode) {
738            return Err(Box::new(ValidationError {
739                problem: "`create_info.present_mode` is not supported for `surface`".into(),
740                vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01281"],
741                ..Default::default()
742            }));
743        }
744
745        if present_modes.is_empty() {
746            if let Some(scaling_behavior) = scaling_behavior {
747                if !surface_capabilities
748                    .supported_present_scaling
749                    .contains_enum(scaling_behavior)
750                {
751                    return Err(Box::new(ValidationError {
752                        problem: "`create_info.scaling_behavior` is not present in the \
753                            `supported_present_scaling` value of the \
754                            capabilities of `surface`"
755                            .into(),
756                        vuids: &[
757                            "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07770",
758                        ],
759                        ..Default::default()
760                    }));
761                }
762            }
763
764            if let Some(present_gravity) = present_gravity {
765                for (axis_index, (present_gravity, supported_present_gravity)) in present_gravity
766                    .into_iter()
767                    .zip(surface_capabilities.supported_present_gravity)
768                    .enumerate()
769                {
770                    if !supported_present_gravity.contains_enum(present_gravity) {
771                        return Err(Box::new(ValidationError {
772                            problem: format!(
773                                "`create_info.present_gravity[{0}]` is not present in the \
774                                `supported_present_gravity[{0}]` value of the \
775                                capabilities of `surface`",
776                                axis_index,
777                            )
778                            .into(),
779                            vuids: &[
780                                "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07772",
781                                "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07774",
782                            ],
783                            ..Default::default()
784                        }));
785                    }
786                }
787            }
788        } else {
789            for (index, &present_mode) in present_modes.iter().enumerate() {
790                if !surface_present_modes.contains(&present_mode) {
791                    return Err(Box::new(ValidationError {
792                        problem: format!(
793                            "`create_info.present_modes[{}]` is not supported for `surface`",
794                            index,
795                        )
796                        .into(),
797                        vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-None-07762"],
798                        ..Default::default()
799                    }));
800                }
801
802                if !surface_capabilities
803                    .compatible_present_modes
804                    .contains(&present_mode)
805                {
806                    return Err(Box::new(ValidationError {
807                        problem: format!(
808                            "`create_info.present_modes[{}]` is not present in the \
809                            `compatible_present_modes` value of the \
810                            capabilities of `surface`",
811                            index,
812                        )
813                        .into(),
814                        vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-07763"],
815                        ..Default::default()
816                    }));
817                }
818
819                if scaling_behavior.is_some() || present_gravity.is_some() {
820                    let surface_capabilities = unsafe {
821                        device.physical_device().surface_capabilities_unchecked(
822                            surface,
823                            SurfaceInfo {
824                                present_mode: Some(present_mode),
825                                full_screen_exclusive,
826                                win32_monitor,
827                                ..Default::default()
828                            },
829                        )
830                    }
831                    .map_err(|_err| {
832                        Box::new(ValidationError {
833                            problem: "`PhysicalDevice::surface_capabilities` \
834                                        returned an error"
835                                .into(),
836                            ..Default::default()
837                        })
838                    })?;
839
840                    if let Some(scaling_behavior) = scaling_behavior {
841                        if !surface_capabilities
842                            .supported_present_scaling
843                            .contains_enum(scaling_behavior)
844                        {
845                            return Err(Box::new(ValidationError {
846                                problem: format!(
847                                    "`create_info.scaling_behavior` is not present in the \
848                                    `supported_present_scaling` value of the \
849                                    capabilities of `surface` for \
850                                    `create_info.present_modes[{}]`",
851                                    index,
852                                )
853                                .into(),
854                                vuids: &[
855                                    "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07771",
856                                ],
857                                ..Default::default()
858                            }));
859                        }
860                    }
861
862                    if let Some(present_gravity) = present_gravity {
863                        for (axis_index, (present_gravity, supported_present_gravity)) in
864                            present_gravity
865                                .into_iter()
866                                .zip(surface_capabilities.supported_present_gravity)
867                                .enumerate()
868                        {
869                            if !supported_present_gravity.contains_enum(present_gravity) {
870                                return Err(Box::new(ValidationError {
871                                    problem: format!(
872                                        "`create_info.present_gravity[{0}]` is not present in the \
873                                        `supported_present_gravity[{0}]` value of the \
874                                        capabilities of `surface` for \
875                                        `create_info.present_modes[{1}]`",
876                                        axis_index, index,
877                                    )
878                                    .into(),
879                                    vuids: &[
880                                        "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07773",
881                                        "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07775",
882                                    ],
883                                    ..Default::default()
884                                }));
885                            }
886                        }
887                    }
888                }
889            }
890        }
891
892        if scaling_behavior.is_some() {
893            if let Some(min_scaled_image_extent) = surface_capabilities.min_scaled_image_extent {
894                if image_extent[0] < min_scaled_image_extent[0]
895                    || image_extent[1] < min_scaled_image_extent[1]
896                {
897                    return Err(Box::new(ValidationError {
898                        problem: "`scaling_behavior` is `Some`, and an element of \
899                            `create_info.image_extent` is less than the corresponding element \
900                            of the `min_scaled_image_extent` value of the \
901                            capabilities of `surface`"
902                            .into(),
903                        vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"],
904                        ..Default::default()
905                    }));
906                }
907            }
908
909            if let Some(max_scaled_image_extent) = surface_capabilities.max_scaled_image_extent {
910                if image_extent[0] > max_scaled_image_extent[0]
911                    || image_extent[1] > max_scaled_image_extent[1]
912                {
913                    return Err(Box::new(ValidationError {
914                        problem: "`scaling_behavior` is `Some`, and an element of \
915                            `create_info.image_extent` is greater than the corresponding element \
916                            of the `max_scaled_image_extent` value of the \
917                            capabilities of `surface`"
918                            .into(),
919                        vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"],
920                        ..Default::default()
921                    }));
922                }
923            }
924        } else {
925            /*
926            This check is in the spec, but in practice leads to race conditions.
927            The window can be resized between calling `surface_capabilities` to get the
928            min/max extent, and then creating the swapchain.
929
930            See this discussion:
931            https://github.com/KhronosGroup/Vulkan-Docs/issues/1144
932
933            if image_extent[0] < surface_capabilities.min_image_extent[0]
934                || image_extent[1] < surface_capabilities.min_image_extent[1]
935            {
936                return Err(Box::new(ValidationError {
937                    problem: "`scaling_behavior` is `Some`, and an element of \
938                        `create_info.image_extent` is less than the corresponding element \
939                        of the `min_image_extent` value of the \
940                        capabilities of `surface`"
941                        .into(),
942                    vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07781"],
943                    ..Default::default()
944                }));
945            }
946
947            if image_extent[0] > surface_capabilities.max_image_extent[0]
948                || image_extent[1] > surface_capabilities.max_image_extent[1]
949            {
950                return Err(Box::new(ValidationError {
951                    problem: "`scaling_behavior` is `Some`, and an element of \
952                        `create_info.image_extent` is greater than the corresponding element \
953                        of the `max_image_extent` value of the \
954                        capabilities of `surface`"
955                        .into(),
956                    vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07781"],
957                    ..Default::default()
958                }));
959            }
960            */
961        }
962
963        Ok(())
964    }
965
966    unsafe fn new_inner_unchecked(
967        device: &Device,
968        surface: &Surface,
969        create_info: &SwapchainCreateInfo,
970        old_swapchain: Option<&Swapchain>,
971    ) -> Result<(ash::vk::SwapchainKHR, Vec<ash::vk::Image>), VulkanError> {
972        let create_info_fields1_vk = create_info.to_vk_fields1();
973        let mut create_info_extensions_vk = create_info.to_vk_extensions(&create_info_fields1_vk);
974        let create_info_vk = create_info.to_vk(
975            surface.handle(),
976            old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle),
977            &mut create_info_extensions_vk,
978        );
979
980        let fns = device.fns();
981
982        let handle = {
983            let mut output = MaybeUninit::uninit();
984            unsafe {
985                (fns.khr_swapchain.create_swapchain_khr)(
986                    device.handle(),
987                    &create_info_vk,
988                    ptr::null(),
989                    output.as_mut_ptr(),
990                )
991            }
992            .result()
993            .map_err(VulkanError::from)?;
994            unsafe { output.assume_init() }
995        };
996
997        let image_handles = loop {
998            let mut count = 0;
999            unsafe {
1000                (fns.khr_swapchain.get_swapchain_images_khr)(
1001                    device.handle(),
1002                    handle,
1003                    &mut count,
1004                    ptr::null_mut(),
1005                )
1006            }
1007            .result()
1008            .map_err(VulkanError::from)?;
1009
1010            let mut images = Vec::with_capacity(count as usize);
1011            let result = unsafe {
1012                (fns.khr_swapchain.get_swapchain_images_khr)(
1013                    device.handle(),
1014                    handle,
1015                    &mut count,
1016                    images.as_mut_ptr(),
1017                )
1018            };
1019
1020            match result {
1021                ash::vk::Result::SUCCESS => {
1022                    unsafe { images.set_len(count as usize) };
1023                    break images;
1024                }
1025                ash::vk::Result::INCOMPLETE => (),
1026                err => return Err(VulkanError::from(err)),
1027            }
1028        };
1029
1030        Ok((handle, image_handles))
1031    }
1032
1033    /// Creates a new `Swapchain` from a raw object handle.
1034    ///
1035    /// # Safety
1036    ///
1037    /// - `handle` and `image_handles` must be valid Vulkan object handles created from `device`.
1038    /// - `handle` must not be retired.
1039    /// - `image_handles` must be swapchain images owned by `handle`, in the same order as they
1040    ///   were returned by `vkGetSwapchainImagesKHR`.
1041    /// - `surface` and `create_info` must match the info used to create the object.
1042    pub unsafe fn from_handle(
1043        device: Arc<Device>,
1044        handle: ash::vk::SwapchainKHR,
1045        image_handles: impl IntoIterator<Item = ash::vk::Image>,
1046        surface: Arc<Surface>,
1047        create_info: SwapchainCreateInfo,
1048    ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
1049        let SwapchainCreateInfo {
1050            flags,
1051            min_image_count,
1052            image_format,
1053            image_view_formats,
1054            image_color_space,
1055            image_extent,
1056            image_array_layers,
1057            image_usage,
1058            image_sharing,
1059            pre_transform,
1060            composite_alpha,
1061            present_mode,
1062            present_modes,
1063            clipped,
1064            scaling_behavior,
1065            present_gravity,
1066            full_screen_exclusive,
1067            win32_monitor,
1068            _ne: _,
1069        } = create_info;
1070
1071        let swapchain = Arc::new(Swapchain {
1072            handle,
1073            device: InstanceOwnedDebugWrapper(device),
1074            surface: InstanceOwnedDebugWrapper(surface),
1075            id: Self::next_id(),
1076
1077            flags,
1078            min_image_count,
1079            image_format,
1080            image_view_formats,
1081            image_color_space,
1082            image_extent,
1083            image_array_layers,
1084            image_usage,
1085            image_sharing,
1086            pre_transform,
1087            composite_alpha,
1088            present_mode,
1089            present_modes,
1090            clipped,
1091            scaling_behavior,
1092            present_gravity,
1093            full_screen_exclusive,
1094            win32_monitor,
1095
1096            prev_present_id: Default::default(),
1097            full_screen_exclusive_held: AtomicBool::new(false),
1098            images: image_handles
1099                .into_iter()
1100                .map(|handle| ImageEntry {
1101                    handle,
1102                    layout_initialized: AtomicBool::new(false),
1103                })
1104                .collect(),
1105            is_retired: Mutex::new(false),
1106        });
1107
1108        let swapchain_images = swapchain
1109            .images
1110            .iter()
1111            .enumerate()
1112            .map(|(image_index, entry)| {
1113                Ok(Arc::new(unsafe {
1114                    Image::from_swapchain(entry.handle, swapchain.clone(), image_index as u32)
1115                }?))
1116            })
1117            .collect::<Result<_, VulkanError>>()?;
1118
1119        Ok((swapchain, swapchain_images))
1120    }
1121
1122    /// Returns the creation parameters of the swapchain.
1123    #[inline]
1124    pub fn create_info(&self) -> SwapchainCreateInfo {
1125        SwapchainCreateInfo {
1126            flags: self.flags,
1127            min_image_count: self.min_image_count,
1128            image_format: self.image_format,
1129            image_view_formats: self.image_view_formats.clone(),
1130            image_color_space: self.image_color_space,
1131            image_extent: self.image_extent,
1132            image_array_layers: self.image_array_layers,
1133            image_usage: self.image_usage,
1134            image_sharing: self.image_sharing.clone(),
1135            pre_transform: self.pre_transform,
1136            composite_alpha: self.composite_alpha,
1137            present_mode: self.present_mode,
1138            present_modes: self.present_modes.clone(),
1139            clipped: self.clipped,
1140            scaling_behavior: self.scaling_behavior,
1141            present_gravity: self.present_gravity,
1142            full_screen_exclusive: self.full_screen_exclusive,
1143            win32_monitor: self.win32_monitor,
1144            _ne: crate::NonExhaustive(()),
1145        }
1146    }
1147
1148    /// Returns the surface that the swapchain was created from.
1149    #[inline]
1150    pub fn surface(&self) -> &Arc<Surface> {
1151        &self.surface
1152    }
1153
1154    /// Returns the flags that the swapchain was created with.
1155    #[inline]
1156    pub fn flags(&self) -> SwapchainCreateFlags {
1157        self.flags
1158    }
1159
1160    /// If `image` is one of the images of this swapchain, returns its index within the swapchain.
1161    #[inline]
1162    pub fn index_of_image(&self, image: &Image) -> Option<u32> {
1163        self.images
1164            .iter()
1165            .position(|entry| entry.handle == image.handle())
1166            .map(|i| i as u32)
1167    }
1168
1169    /// Returns the number of images of the swapchain.
1170    #[inline]
1171    pub fn image_count(&self) -> u32 {
1172        self.images.len() as u32
1173    }
1174
1175    /// Returns the format of the images of the swapchain.
1176    #[inline]
1177    pub fn image_format(&self) -> Format {
1178        self.image_format
1179    }
1180
1181    /// Returns the formats that an image view created from a swapchain image can have.
1182    #[inline]
1183    pub fn image_view_formats(&self) -> &[Format] {
1184        &self.image_view_formats
1185    }
1186
1187    /// Returns the color space of the images of the swapchain.
1188    #[inline]
1189    pub fn image_color_space(&self) -> ColorSpace {
1190        self.image_color_space
1191    }
1192
1193    /// Returns the extent of the images of the swapchain.
1194    #[inline]
1195    pub fn image_extent(&self) -> [u32; 2] {
1196        self.image_extent
1197    }
1198
1199    /// Returns the number of array layers of the images of the swapchain.
1200    #[inline]
1201    pub fn image_array_layers(&self) -> u32 {
1202        self.image_array_layers
1203    }
1204
1205    /// Returns the usage of the images of the swapchain.
1206    #[inline]
1207    pub fn image_usage(&self) -> ImageUsage {
1208        self.image_usage
1209    }
1210
1211    /// Returns the sharing of the images of the swapchain.
1212    #[inline]
1213    pub fn image_sharing(&self) -> &Sharing<SmallVec<[u32; 4]>> {
1214        &self.image_sharing
1215    }
1216
1217    /// Returns the pre-transform that was passed when creating the swapchain.
1218    #[inline]
1219    pub fn pre_transform(&self) -> SurfaceTransform {
1220        self.pre_transform
1221    }
1222
1223    /// Returns the alpha mode that was passed when creating the swapchain.
1224    #[inline]
1225    pub fn composite_alpha(&self) -> CompositeAlpha {
1226        self.composite_alpha
1227    }
1228
1229    /// Returns the present mode that was passed when creating the swapchain.
1230    #[inline]
1231    pub fn present_mode(&self) -> PresentMode {
1232        self.present_mode
1233    }
1234
1235    /// Returns the alternative present modes that were passed when creating the swapchain.
1236    #[inline]
1237    pub fn present_modes(&self) -> &[PresentMode] {
1238        &self.present_modes
1239    }
1240
1241    /// Returns the value of `clipped` that was passed when creating the swapchain.
1242    #[inline]
1243    pub fn clipped(&self) -> bool {
1244        self.clipped
1245    }
1246
1247    /// Returns the scaling behavior that was passed when creating the swapchain.
1248    #[inline]
1249    pub fn scaling_behavior(&self) -> Option<PresentScaling> {
1250        self.scaling_behavior
1251    }
1252
1253    /// Returns the scaling behavior that was passed when creating the swapchain.
1254    #[inline]
1255    pub fn present_gravity(&self) -> Option<[PresentGravity; 2]> {
1256        self.present_gravity
1257    }
1258
1259    /// Returns the value of 'full_screen_exclusive` that was passed when creating the swapchain.
1260    #[inline]
1261    pub fn full_screen_exclusive(&self) -> FullScreenExclusive {
1262        self.full_screen_exclusive
1263    }
1264
1265    /// Acquires temporary ownership of a swapchain image.
1266    ///
1267    /// The function returns the index of the image in the array of images that was returned
1268    /// when creating the swapchain. The image will not be available immediately after the function
1269    /// returns successfully.
1270    ///
1271    /// When the image becomes available, a semaphore or fence will be signaled. The image can then
1272    /// be accessed by the host or device. After this, the image must be *presented* back to the
1273    /// swapchain, using the [`present`] queue command.
1274    ///
1275    /// # Safety
1276    ///
1277    /// - `self` must be kept alive until either `acquire_info.semaphore` or `acquire_info.fence`
1278    ///   is signaled.
1279    /// - If all images from `self` are currently acquired, and have not been presented yet, then
1280    ///   `acquire_info.timeout` must not be `None`.
1281    ///
1282    /// If `acquire_info.semaphore` is `Some`:
1283    /// - The semaphore must be kept alive until it is signaled.
1284    /// - When the signal operation is executed, the semaphore must be in the unsignaled state.
1285    ///
1286    /// If `acquire_info.fence` is `Some`:
1287    /// - The fence must be kept alive until it is signaled.
1288    /// - The fence must be unsignaled and must not be associated with any other command that is
1289    ///   still executing.
1290    ///
1291    /// [`present`]: crate::device::QueueGuard::present
1292    #[inline]
1293    pub unsafe fn acquire_next_image(
1294        &self,
1295        acquire_info: &AcquireNextImageInfo,
1296    ) -> Result<AcquiredImage, Validated<VulkanError>> {
1297        let is_retired_lock = self.is_retired.lock();
1298        self.validate_acquire_next_image(acquire_info, *is_retired_lock)?;
1299
1300        Ok(unsafe { self.acquire_next_image_unchecked(acquire_info) }?)
1301    }
1302
1303    fn validate_acquire_next_image(
1304        &self,
1305        acquire_info: &AcquireNextImageInfo,
1306        is_retired: bool,
1307    ) -> Result<(), Box<ValidationError>> {
1308        acquire_info
1309            .validate(&self.device)
1310            .map_err(|err| err.add_context("acquire_info"))?;
1311
1312        if is_retired {
1313            return Err(Box::new(ValidationError {
1314                problem: "this swapchain is retired".into(),
1315                vuids: &["VUID-VkAcquireNextImageInfoKHR-swapchain-01675"],
1316                ..Default::default()
1317            }));
1318        }
1319
1320        // unsafe
1321        // VUID-vkAcquireNextImage2KHR-surface-07784
1322        // VUID-VkAcquireNextImageInfoKHR-semaphore-01288
1323        // VUID-VkAcquireNextImageInfoKHR-semaphore-01781
1324        // VUID-VkAcquireNextImageInfoKHR-fence-01289
1325
1326        Ok(())
1327    }
1328
1329    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1330    pub unsafe fn acquire_next_image_unchecked(
1331        &self,
1332        acquire_info: &AcquireNextImageInfo,
1333    ) -> Result<AcquiredImage, VulkanError> {
1334        let acquire_info_vk = acquire_info.to_vk(self.handle(), self.device.device_mask());
1335
1336        let (image_index, is_suboptimal) = {
1337            let fns = self.device.fns();
1338            let mut output = MaybeUninit::uninit();
1339
1340            let result = if self.device.api_version() >= Version::V1_1
1341                || self.device.enabled_extensions().khr_device_group
1342            {
1343                unsafe {
1344                    (fns.khr_swapchain.acquire_next_image2_khr)(
1345                        self.device.handle(),
1346                        &acquire_info_vk,
1347                        output.as_mut_ptr(),
1348                    )
1349                }
1350            } else {
1351                debug_assert!(acquire_info_vk.p_next.is_null());
1352                unsafe {
1353                    (fns.khr_swapchain.acquire_next_image_khr)(
1354                        self.device.handle(),
1355                        acquire_info_vk.swapchain,
1356                        acquire_info_vk.timeout,
1357                        acquire_info_vk.semaphore,
1358                        acquire_info_vk.fence,
1359                        output.as_mut_ptr(),
1360                    )
1361                }
1362            };
1363
1364            match result {
1365                ash::vk::Result::SUCCESS => (unsafe { output.assume_init() }, false),
1366                ash::vk::Result::SUBOPTIMAL_KHR => (unsafe { output.assume_init() }, true),
1367                ash::vk::Result::NOT_READY => return Err(VulkanError::NotReady),
1368                ash::vk::Result::TIMEOUT => return Err(VulkanError::Timeout),
1369                err => {
1370                    let err = VulkanError::from(err);
1371
1372                    if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
1373                        self.full_screen_exclusive_held
1374                            .store(false, Ordering::SeqCst);
1375                    }
1376
1377                    return Err(err);
1378                }
1379            }
1380        };
1381
1382        Ok(AcquiredImage {
1383            image_index,
1384            is_suboptimal,
1385        })
1386    }
1387
1388    /// Waits for a swapchain image with a specific present ID to be presented to the user.
1389    ///
1390    /// For this to work, you must set [`SwapchainPresentInfo::present_id`] to `Some` when
1391    /// presenting. This function will then wait until the swapchain image with the specified ID is
1392    /// presented.
1393    ///
1394    /// Returns whether the presentation was suboptimal. This has the same meaning as in
1395    /// [`AcquiredImage::is_suboptimal`].
1396    #[inline]
1397    pub fn wait_for_present(
1398        &self,
1399        present_id: NonZeroU64,
1400        timeout: Option<Duration>,
1401    ) -> Result<bool, Validated<VulkanError>> {
1402        let is_retired_lock = self.is_retired.lock();
1403        self.validate_wait_for_present(present_id, timeout, *is_retired_lock)?;
1404
1405        Ok(unsafe { self.wait_for_present_unchecked(present_id, timeout) }?)
1406    }
1407
1408    fn validate_wait_for_present(
1409        &self,
1410        _present_id: NonZeroU64,
1411        timeout: Option<Duration>,
1412        is_retired: bool,
1413    ) -> Result<(), Box<ValidationError>> {
1414        if !self.device.enabled_features().present_wait {
1415            return Err(Box::new(ValidationError {
1416                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
1417                    "present_wait",
1418                )])]),
1419                vuids: &["VUID-vkWaitForPresentKHR-presentWait-06234"],
1420                ..Default::default()
1421            }));
1422        }
1423
1424        if is_retired {
1425            return Err(Box::new(ValidationError {
1426                problem: "this swapchain is retired".into(),
1427                vuids: &["VUID-vkWaitForPresentKHR-swapchain-04997"],
1428                ..Default::default()
1429            }));
1430        }
1431
1432        if let Some(timeout) = timeout {
1433            if timeout.as_nanos() >= u64::MAX as u128 {
1434                return Err(Box::new(ValidationError {
1435                    context: "timeout".into(),
1436                    problem: "is not less than `u64::MAX` nanoseconds".into(),
1437                    ..Default::default()
1438                }));
1439            }
1440        }
1441
1442        Ok(())
1443    }
1444
1445    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1446    pub unsafe fn wait_for_present_unchecked(
1447        &self,
1448        present_id: NonZeroU64,
1449        timeout: Option<Duration>,
1450    ) -> Result<bool, VulkanError> {
1451        let result = {
1452            let fns = self.device.fns();
1453            unsafe {
1454                (fns.khr_present_wait.wait_for_present_khr)(
1455                    self.device.handle(),
1456                    self.handle,
1457                    present_id.get(),
1458                    timeout.map_or(u64::MAX, |duration| {
1459                        u64::try_from(duration.as_nanos()).unwrap()
1460                    }),
1461                )
1462            }
1463        };
1464
1465        match result {
1466            ash::vk::Result::SUCCESS => Ok(false),
1467            ash::vk::Result::SUBOPTIMAL_KHR => Ok(true),
1468            ash::vk::Result::TIMEOUT => Err(VulkanError::Timeout),
1469            err => {
1470                let err = VulkanError::from(err);
1471
1472                if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
1473                    self.full_screen_exclusive_held
1474                        .store(false, Ordering::SeqCst);
1475                }
1476
1477                Err(err)
1478            }
1479        }
1480    }
1481
1482    /// Acquires full-screen exclusivity.
1483    ///
1484    /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`],
1485    /// and must not already hold full-screen exclusivity. Full-screen exclusivity is held until
1486    /// either the `release_full_screen_exclusive` is called, or if any of the the other
1487    /// `Swapchain` functions return `FullScreenExclusiveLost`.
1488    #[inline]
1489    pub fn acquire_full_screen_exclusive_mode(&self) -> Result<(), Validated<VulkanError>> {
1490        self.validate_acquire_full_screen_exclusive_mode()?;
1491
1492        Ok(unsafe { self.acquire_full_screen_exclusive_mode_unchecked() }?)
1493    }
1494
1495    fn validate_acquire_full_screen_exclusive_mode(&self) -> Result<(), Box<ValidationError>> {
1496        if *self.is_retired.lock() {
1497            return Err(Box::new(ValidationError {
1498                problem: "this swapchain is retired".into(),
1499                vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02674"],
1500                ..Default::default()
1501            }));
1502        }
1503
1504        if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
1505            return Err(Box::new(ValidationError {
1506                context: "self.full_screen_exclusive()".into(),
1507                problem: "is not `FullScreenExclusive::ApplicationControlled`".into(),
1508                vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02675"],
1509                ..Default::default()
1510            }));
1511        }
1512
1513        // This must be the last check in this function.
1514        if self
1515            .full_screen_exclusive_held
1516            .swap(true, Ordering::Acquire)
1517        {
1518            return Err(Box::new(ValidationError {
1519                problem: "this swapchain already holds full-screen exclusive access".into(),
1520                vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02676"],
1521                ..Default::default()
1522            }));
1523        }
1524
1525        Ok(())
1526    }
1527
1528    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1529    pub unsafe fn acquire_full_screen_exclusive_mode_unchecked(&self) -> Result<(), VulkanError> {
1530        self.full_screen_exclusive_held
1531            .store(true, Ordering::Relaxed);
1532
1533        let fns = self.device.fns();
1534        unsafe {
1535            (fns.ext_full_screen_exclusive
1536                .acquire_full_screen_exclusive_mode_ext)(
1537                self.device.handle(), self.handle
1538            )
1539        }
1540        .result()
1541        .map_err(VulkanError::from)?;
1542
1543        Ok(())
1544    }
1545
1546    /// Releases full-screen exclusivity.
1547    ///
1548    /// The swapchain must have been created with [`FullScreenExclusive::ApplicationControlled`],
1549    /// and must currently hold full-screen exclusivity.
1550    #[inline]
1551    pub fn release_full_screen_exclusive_mode(&self) -> Result<(), Validated<VulkanError>> {
1552        self.validate_release_full_screen_exclusive_mode()?;
1553
1554        Ok(unsafe { self.release_full_screen_exclusive_mode_unchecked() }?)
1555    }
1556
1557    fn validate_release_full_screen_exclusive_mode(&self) -> Result<(), Box<ValidationError>> {
1558        if *self.is_retired.lock() {
1559            return Err(Box::new(ValidationError {
1560                problem: "this swapchain is retired".into(),
1561                vuids: &["VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02677"],
1562                ..Default::default()
1563            }));
1564        }
1565
1566        if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
1567            return Err(Box::new(ValidationError {
1568                context: "self.full_screen_exclusive()".into(),
1569                problem: "is not `FullScreenExclusive::ApplicationControlled`".into(),
1570                vuids: &["VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02678"],
1571                ..Default::default()
1572            }));
1573        }
1574
1575        Ok(())
1576    }
1577
1578    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1579    pub unsafe fn release_full_screen_exclusive_mode_unchecked(&self) -> Result<(), VulkanError> {
1580        self.full_screen_exclusive_held
1581            .store(false, Ordering::Release);
1582
1583        let fns = self.device.fns();
1584        unsafe {
1585            (fns.ext_full_screen_exclusive
1586                .release_full_screen_exclusive_mode_ext)(
1587                self.device.handle(), self.handle
1588            )
1589        }
1590        .result()
1591        .map_err(VulkanError::from)?;
1592
1593        Ok(())
1594    }
1595
1596    /// `FullScreenExclusive::AppControlled` is not the active full-screen exclusivity mode,
1597    /// then this function will always return false. If true is returned the swapchain
1598    /// is in `FullScreenExclusive::AppControlled` full-screen exclusivity mode and exclusivity
1599    /// is currently acquired.
1600    #[inline]
1601    pub fn is_full_screen_exclusive(&self) -> bool {
1602        if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
1603            false
1604        } else {
1605            self.full_screen_exclusive_held.load(Ordering::SeqCst)
1606        }
1607    }
1608
1609    // This method is necessary to allow `SwapchainImage`s to signal when they have been
1610    // transitioned out of their initial `undefined` image layout.
1611    //
1612    // See the `ImageAccess::layout_initialized` method documentation for more details.
1613    pub(crate) fn image_layout_initialized(&self, image_index: u32) {
1614        let image_entry = self.images.get(image_index as usize);
1615        if let Some(image_entry) = image_entry {
1616            image_entry
1617                .layout_initialized
1618                .store(true, Ordering::Relaxed);
1619        }
1620    }
1621
1622    pub(crate) fn is_image_layout_initialized(&self, image_index: u32) -> bool {
1623        let image_entry = self.images.get(image_index as usize);
1624        if let Some(image_entry) = image_entry {
1625            image_entry.layout_initialized.load(Ordering::Relaxed)
1626        } else {
1627            false
1628        }
1629    }
1630
1631    #[inline]
1632    pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool {
1633        &self.full_screen_exclusive_held
1634    }
1635
1636    #[inline]
1637    pub(crate) unsafe fn try_claim_present_id(&self, present_id: NonZeroU64) -> bool {
1638        let present_id = u64::from(present_id);
1639        self.prev_present_id.fetch_max(present_id, Ordering::SeqCst) < present_id
1640    }
1641}
1642
1643impl Drop for Swapchain {
1644    #[inline]
1645    fn drop(&mut self) {
1646        let fns = self.device.fns();
1647        unsafe {
1648            (fns.khr_swapchain.destroy_swapchain_khr)(
1649                self.device.handle(),
1650                self.handle,
1651                ptr::null(),
1652            )
1653        };
1654    }
1655}
1656
1657unsafe impl VulkanObject for Swapchain {
1658    type Handle = ash::vk::SwapchainKHR;
1659
1660    #[inline]
1661    fn handle(&self) -> Self::Handle {
1662        self.handle
1663    }
1664}
1665
1666unsafe impl DeviceOwned for Swapchain {
1667    #[inline]
1668    fn device(&self) -> &Arc<Device> {
1669        &self.device
1670    }
1671}
1672
1673impl_id_counter!(Swapchain);
1674
1675/// Parameters to create a new `Swapchain`.
1676///
1677/// Many of the values here must be supported by the physical device.
1678/// [`PhysicalDevice`](crate::device::physical::PhysicalDevice) has several
1679/// methods to query what is supported.
1680#[derive(Clone, Debug)]
1681pub struct SwapchainCreateInfo {
1682    /// Additional properties of the swapchain.
1683    ///
1684    /// The default value is empty.
1685    pub flags: SwapchainCreateFlags,
1686
1687    /// The minimum number of images that will be created.
1688    ///
1689    /// The implementation is allowed to create more than this number, but never less.
1690    ///
1691    /// The default value is `2`.
1692    pub min_image_count: u32,
1693
1694    /// The format of the created images.
1695    ///
1696    /// The default value is `Format::UNDEFINED`.
1697    pub image_format: Format,
1698
1699    /// The formats that an image view can have when it is created from one of the swapchain
1700    /// images.
1701    ///
1702    /// If the list is not empty, and `flags` does not contain
1703    /// [`SwapchainCreateFlags::MUTABLE_FORMAT`], then the list must contain at most one element,
1704    /// otherwise any number of elements are allowed.
1705    /// The view formats must be compatible with `format`.
1706    ///
1707    /// If `flags` contains [`SwapchainCreateFlags::MUTABLE_FORMAT`], then the list must contain
1708    /// `image_format.
1709    ///
1710    /// If this is not empty, then the device API version must be at least 1.2, or the
1711    /// [`khr_image_format_list`] extension must be enabled on the device.
1712    ///
1713    /// The default value is empty.
1714    ///
1715    /// [`khr_image_format_list`]: crate::device::DeviceExtensions::khr_image_format_list
1716    pub image_view_formats: Vec<Format>,
1717
1718    /// The color space of the created images.
1719    ///
1720    /// The default value is [`ColorSpace::SrgbNonLinear`].
1721    pub image_color_space: ColorSpace,
1722
1723    /// The extent of the created images.
1724    ///
1725    /// Both values must be greater than zero. Note that on some platforms,
1726    /// [`SurfaceCapabilities::current_extent`] will be zero if the surface is minimized.
1727    /// Care must be taken to check for this, to avoid trying to create a zero-size swapchain.
1728    ///
1729    /// The default value is `[0, 0]`, which must be overridden.
1730    pub image_extent: [u32; 2],
1731
1732    /// The number of array layers of the created images.
1733    ///
1734    /// The default value is `1`.
1735    pub image_array_layers: u32,
1736
1737    /// How the created images will be used.
1738    ///
1739    /// The default value is [`ImageUsage::empty()`], which must be overridden.
1740    pub image_usage: ImageUsage,
1741
1742    /// Whether the created images can be shared across multiple queues, or are limited to a single
1743    /// queue.
1744    ///
1745    /// The default value is [`Sharing::Exclusive`].
1746    pub image_sharing: Sharing<SmallVec<[u32; 4]>>,
1747
1748    /// The transform that should be applied to an image before it is presented.
1749    ///
1750    /// The default value is [`SurfaceTransform::Identity`].
1751    pub pre_transform: SurfaceTransform,
1752
1753    /// How alpha values of the pixels in the image are to be treated.
1754    ///
1755    /// The default value is [`CompositeAlpha::Opaque`].
1756    pub composite_alpha: CompositeAlpha,
1757
1758    /// How the swapchain should behave when multiple images are waiting in the queue to be
1759    /// presented.
1760    ///
1761    /// The default value is [`PresentMode::Fifo`].
1762    pub present_mode: PresentMode,
1763
1764    /// Alternative present modes that can be used with this swapchain. The mode specified in
1765    /// `present_mode` is the default mode, but can be changed for future present operations by
1766    /// specifying it when presenting.
1767    ///
1768    /// If this is not empty, then the
1769    /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1)
1770    /// extension must be enabled on the device.
1771    /// It must always contain the mode specified in `present_mode`.
1772    ///
1773    /// The default value is empty.
1774    pub present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>,
1775
1776    /// Whether the implementation is allowed to discard rendering operations that affect regions
1777    /// of the surface which aren't visible. This is important to take into account if your
1778    /// fragment shader has side-effects or if you want to read back the content of the image
1779    /// afterwards.
1780    ///
1781    /// The default value is `true`.
1782    pub clipped: bool,
1783
1784    /// The scaling method to use when the surface is not the same size as the swapchain image.
1785    ///
1786    /// `None` means the behavior is implementation defined.
1787    ///
1788    /// If this is `Some`, then the
1789    /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1)
1790    /// extension must be enabled on the device.
1791    ///
1792    /// The default value is `None`.
1793    pub scaling_behavior: Option<PresentScaling>,
1794
1795    /// The horizontal and vertical alignment to use when the swapchain image, after applying
1796    /// scaling, does not fill the whole surface.
1797    ///
1798    /// `None` means the behavior is implementation defined.
1799    ///
1800    /// If this is `Some`, then the
1801    /// [`ext_swapchain_maintenance1`](crate::device::DeviceExtensions::ext_swapchain_maintenance1)
1802    /// extension must be enabled on the device.
1803    ///
1804    /// The default value is `None`.
1805    pub present_gravity: Option<[PresentGravity; 2]>,
1806
1807    /// How full-screen exclusivity is to be handled.
1808    ///
1809    /// If set to anything other than [`FullScreenExclusive::Default`], then the
1810    /// [`ext_full_screen_exclusive`](crate::device::DeviceExtensions::ext_full_screen_exclusive)
1811    /// extension must be enabled on the device.
1812    ///
1813    /// The default value is [`FullScreenExclusive::Default`].
1814    pub full_screen_exclusive: FullScreenExclusive,
1815
1816    /// If `full_screen_exclusive` is not [`FullScreenExclusive::Default`], this specifies the
1817    /// monitor on which full-screen exclusivity should be used.
1818    ///
1819    /// For this case, the value must be `Some`, and for all others it must be `None`.
1820    ///
1821    /// The default value is `None`.
1822    pub win32_monitor: Option<Win32Monitor>,
1823
1824    pub _ne: crate::NonExhaustive,
1825}
1826
1827impl Default for SwapchainCreateInfo {
1828    #[inline]
1829    fn default() -> Self {
1830        Self {
1831            flags: SwapchainCreateFlags::empty(),
1832            min_image_count: 2,
1833            image_format: Format::UNDEFINED,
1834            image_view_formats: Vec::new(),
1835            image_color_space: ColorSpace::SrgbNonLinear,
1836            image_extent: [0, 0],
1837            image_array_layers: 1,
1838            image_usage: ImageUsage::empty(),
1839            image_sharing: Sharing::Exclusive,
1840            pre_transform: SurfaceTransform::Identity,
1841            composite_alpha: CompositeAlpha::Opaque,
1842            present_mode: PresentMode::Fifo,
1843            present_modes: SmallVec::new(),
1844            clipped: true,
1845            scaling_behavior: None,
1846            present_gravity: None,
1847            full_screen_exclusive: FullScreenExclusive::Default,
1848            win32_monitor: None,
1849            _ne: crate::NonExhaustive(()),
1850        }
1851    }
1852}
1853
1854impl SwapchainCreateInfo {
1855    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
1856        let &Self {
1857            flags,
1858            min_image_count: _,
1859            image_format,
1860            ref image_view_formats,
1861            image_color_space,
1862            image_extent,
1863            image_array_layers,
1864            image_usage,
1865            ref image_sharing,
1866            pre_transform,
1867            composite_alpha,
1868            present_mode,
1869            ref present_modes,
1870            clipped: _,
1871            scaling_behavior,
1872            present_gravity,
1873            full_screen_exclusive,
1874            win32_monitor,
1875            _ne: _,
1876        } = self;
1877
1878        flags.validate_device(device).map_err(|err| {
1879            err.add_context("flags")
1880                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-flags-parameter"])
1881        })?;
1882
1883        image_format.validate_device(device).map_err(|err| {
1884            err.add_context("image_format")
1885                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter"])
1886        })?;
1887
1888        image_color_space.validate_device(device).map_err(|err| {
1889            err.add_context("image_color_space")
1890                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter"])
1891        })?;
1892
1893        image_usage.validate_device(device).map_err(|err| {
1894            err.add_context("image_usage")
1895                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter"])
1896        })?;
1897
1898        if image_usage.is_empty() {
1899            return Err(Box::new(ValidationError {
1900                context: "image_usage".into(),
1901                problem: "is empty".into(),
1902                vuids: &["VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask"],
1903                ..Default::default()
1904            }));
1905        }
1906
1907        pre_transform.validate_device(device).map_err(|err| {
1908            err.add_context("pre_transform")
1909                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-preTransform-parameter"])
1910        })?;
1911
1912        composite_alpha.validate_device(device).map_err(|err| {
1913            err.add_context("composite_alpha")
1914                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter"])
1915        })?;
1916
1917        present_mode.validate_device(device).map_err(|err| {
1918            err.add_context("present_mode")
1919                .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-presentMode-parameter"])
1920        })?;
1921
1922        if image_extent.contains(&0) {
1923            return Err(Box::new(ValidationError {
1924                context: "image_extent".into(),
1925                problem: "one or more elements are zero".into(),
1926                vuids: &["VUID-VkSwapchainCreateInfoKHR-imageExtent-01689"],
1927                ..Default::default()
1928            }));
1929        }
1930
1931        if image_array_layers == 0 {
1932            return Err(Box::new(ValidationError {
1933                context: "image_array_layers".into(),
1934                problem: "is zero".into(),
1935                vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"],
1936                ..Default::default()
1937            }));
1938        }
1939
1940        match image_sharing {
1941            Sharing::Exclusive => (),
1942            Sharing::Concurrent(queue_family_indices) => {
1943                if queue_family_indices.len() < 2 {
1944                    return Err(Box::new(ValidationError {
1945                        context: "image_sharing".into(),
1946                        problem: "is `Sharing::Concurrent`, and contains less than 2 \
1947                            queue family indices"
1948                            .into(),
1949                        vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278"],
1950                        ..Default::default()
1951                    }));
1952                }
1953
1954                let queue_family_count =
1955                    device.physical_device().queue_family_properties().len() as u32;
1956
1957                for (index, &queue_family_index) in queue_family_indices.iter().enumerate() {
1958                    if queue_family_indices[..index].contains(&queue_family_index) {
1959                        return Err(Box::new(ValidationError {
1960                            context: "queue_family_indices".into(),
1961                            problem: format!(
1962                                "the queue family index in the list at index {} is contained in \
1963                                the list more than once",
1964                                index,
1965                            )
1966                            .into(),
1967                            vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"],
1968                            ..Default::default()
1969                        }));
1970                    }
1971
1972                    if queue_family_index >= queue_family_count {
1973                        return Err(Box::new(ValidationError {
1974                            context: format!("queue_family_indices[{}]", index).into(),
1975                            problem: "is not less than the number of queue families in the \
1976                                physical device"
1977                                .into(),
1978                            vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"],
1979                            ..Default::default()
1980                        }));
1981                    }
1982                }
1983            }
1984        };
1985
1986        let image_format_properties = unsafe {
1987            device
1988                .physical_device()
1989                .image_format_properties_unchecked(ImageFormatInfo {
1990                    format: image_format,
1991                    image_type: ImageType::Dim2d,
1992                    tiling: ImageTiling::Optimal,
1993                    usage: image_usage,
1994                    ..Default::default()
1995                })
1996        }
1997        .map_err(|_err| {
1998            Box::new(ValidationError {
1999                problem: "`PhysicalDevice::image_format_properties` \
2000                            returned an error"
2001                    .into(),
2002                ..Default::default()
2003            })
2004        })?;
2005
2006        if image_format_properties.is_none() {
2007            return Err(Box::new(ValidationError {
2008                problem: "the combination of `image_format` and `image_usage` is not supported \
2009                    for images by the physical device"
2010                    .into(),
2011                vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01778"],
2012                ..Default::default()
2013            }));
2014        }
2015
2016        if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT)
2017            && !image_view_formats.contains(&image_format)
2018        {
2019            return Err(Box::new(ValidationError {
2020                problem: "`flags` contains `SwapchainCreateFlags::MUTABLE_FORMAT`, but \
2021                    `image_view_formats` does not contain `image_format`"
2022                    .into(),
2023                vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-03168"],
2024                ..Default::default()
2025            }));
2026        }
2027
2028        if !image_view_formats.is_empty() {
2029            if !(device.api_version() >= Version::V1_2
2030                || device.enabled_extensions().khr_image_format_list)
2031            {
2032                return Err(Box::new(ValidationError {
2033                    context: "image_view_formats".into(),
2034                    problem: "is not empty".into(),
2035                    requires_one_of: RequiresOneOf(&[
2036                        RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
2037                        RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]),
2038                    ]),
2039                    ..Default::default()
2040                }));
2041            }
2042
2043            if !flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT)
2044                && image_view_formats.len() != 1
2045            {
2046                return Err(Box::new(ValidationError {
2047                    problem: "`flags` does not contain `SwapchainCreateFlags::MUTABLE_FORMAT`, \
2048                        but `image_view_formats` contains more than one element"
2049                        .into(),
2050                    vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-04100"],
2051                    ..Default::default()
2052                }));
2053            }
2054
2055            for (index, &image_view_format) in image_view_formats.iter().enumerate() {
2056                image_view_format.validate_device(device).map_err(|err| {
2057                    err.add_context(format!("image_view_formats[{}]", index))
2058                        .set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"])
2059                })?;
2060
2061                if image_view_format.compatibility() != image_format.compatibility() {
2062                    return Err(Box::new(ValidationError {
2063                        problem: format!(
2064                            "`image_view_formats[{}]` is not compatible with `image_format`",
2065                            index
2066                        )
2067                        .into(),
2068                        vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-04099"],
2069                        ..Default::default()
2070                    }));
2071                }
2072            }
2073        }
2074
2075        if !present_modes.is_empty() {
2076            if !device.enabled_extensions().ext_swapchain_maintenance1 {
2077                return Err(Box::new(ValidationError {
2078                    context: "present_modes".into(),
2079                    problem: "is not empty".into(),
2080                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2081                        "ext_swapchain_maintenance1",
2082                    )])]),
2083                    ..Default::default()
2084                }));
2085            }
2086
2087            for (index, &present_mode) in present_modes.iter().enumerate() {
2088                present_mode.validate_device(device).map_err(|err| {
2089                    err.add_context(format!("present_modes[{}]", index))
2090                        .set_vuids(&[
2091                            "VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-parameter",
2092                        ])
2093                })?;
2094            }
2095
2096            if !present_modes.contains(&present_mode) {
2097                return Err(Box::new(ValidationError {
2098                    problem: "`present_modes` is not empty, but does not contain `present_mode`"
2099                        .into(),
2100                    vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-presentMode-07764"],
2101                    ..Default::default()
2102                }));
2103            }
2104        }
2105
2106        if let Some(scaling_behavior) = scaling_behavior {
2107            if !device.enabled_extensions().ext_swapchain_maintenance1 {
2108                return Err(Box::new(ValidationError {
2109                    context: "scaling_behavior".into(),
2110                    problem: "is `Some`".into(),
2111                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2112                        "ext_swapchain_maintenance1",
2113                    )])]),
2114                    ..Default::default()
2115                }));
2116            }
2117
2118            scaling_behavior.validate_device(device).map_err(|err| {
2119                err.add_context("scaling_behavior").set_vuids(&[
2120                    "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-parameter",
2121                ])
2122            })?;
2123
2124            // VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07767
2125            // Ensured by the use of an enum.
2126        }
2127
2128        if let Some(present_gravity) = present_gravity {
2129            if !device.enabled_extensions().ext_swapchain_maintenance1 {
2130                return Err(Box::new(ValidationError {
2131                    context: "present_gravity".into(),
2132                    problem: "is `Some`".into(),
2133                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2134                        "ext_swapchain_maintenance1",
2135                    )])]),
2136                    ..Default::default()
2137                }));
2138            }
2139
2140            for (axis_index, present_gravity) in present_gravity.into_iter().enumerate() {
2141                present_gravity.validate_device(device).map_err(|err| {
2142                    err.add_context(format!("present_gravity[{}]", axis_index))
2143                        .set_vuids(&[
2144                            "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-parameter",
2145                            "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-parameter",
2146                        ])
2147                })?;
2148            }
2149
2150            // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07765
2151            // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07766
2152            // Ensured by the use of an array of enums wrapped in `Option`.
2153
2154            // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07768
2155            // VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07769
2156            // Ensured by the use of an enum.
2157        }
2158
2159        if full_screen_exclusive != FullScreenExclusive::Default {
2160            if !device.enabled_extensions().ext_full_screen_exclusive {
2161                return Err(Box::new(ValidationError {
2162                    context: "full_screen_exclusive".into(),
2163                    problem: "is not `FullScreenExclusive::Default`".into(),
2164                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2165                        "ext_full_screen_exclusive",
2166                    )])]),
2167                    ..Default::default()
2168                }));
2169            }
2170
2171            full_screen_exclusive
2172                .validate_device(device)
2173                .map_err(|err| {
2174                    err.add_context("full_screen_exclusive").set_vuids(&[
2175                        "VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter",
2176                    ])
2177                })?;
2178
2179            if win32_monitor.is_none() {
2180                return Err(Box::new(ValidationError {
2181                    problem: "`full_screen_exclusive` is not `FullScreenExclusive::Default`, but \
2182                        `win32_monitor` is `None`"
2183                        .into(),
2184                    vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-02679"],
2185                    ..Default::default()
2186                }));
2187            }
2188        } else if win32_monitor.is_some() {
2189            return Err(Box::new(ValidationError {
2190                problem: "`full_screen_exclusive` is `FullScreenExclusive::Default`, but \
2191                    `win32_monitor` is `Some`"
2192                    .into(),
2193                ..Default::default()
2194            }));
2195        }
2196
2197        Ok(())
2198    }
2199
2200    pub(crate) fn to_vk<'a>(
2201        &'a self,
2202        surface_vk: ash::vk::SurfaceKHR,
2203        old_swapchain_vk: ash::vk::SwapchainKHR,
2204        extensions_vk: &'a mut SwapchainCreateInfoExtensionsVk<'_>,
2205    ) -> ash::vk::SwapchainCreateInfoKHR<'a> {
2206        let &Self {
2207            flags,
2208            min_image_count,
2209            image_format,
2210            image_view_formats: _,
2211            image_color_space,
2212            image_extent,
2213            image_array_layers,
2214            image_usage,
2215            ref image_sharing,
2216            pre_transform,
2217            composite_alpha,
2218            present_mode,
2219            present_modes: _,
2220            clipped,
2221            scaling_behavior: _,
2222            present_gravity: _,
2223            full_screen_exclusive: _,
2224            win32_monitor: _,
2225            _ne: _,
2226        } = self;
2227
2228        let (image_sharing_mode_vk, queue_family_indices_vk) = match image_sharing {
2229            Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, [].as_slice()),
2230            Sharing::Concurrent(ids) => (ash::vk::SharingMode::CONCURRENT, ids.as_slice()),
2231        };
2232
2233        let mut val_vk = ash::vk::SwapchainCreateInfoKHR::default()
2234            .flags(flags.into())
2235            .surface(surface_vk)
2236            .min_image_count(min_image_count)
2237            .image_format(image_format.into())
2238            .image_color_space(image_color_space.into())
2239            .image_extent(ash::vk::Extent2D {
2240                width: image_extent[0],
2241                height: image_extent[1],
2242            })
2243            .image_array_layers(image_array_layers)
2244            .image_usage(image_usage.into())
2245            .image_sharing_mode(image_sharing_mode_vk)
2246            .queue_family_indices(queue_family_indices_vk)
2247            .pre_transform(pre_transform.into())
2248            .composite_alpha(composite_alpha.into())
2249            .present_mode(present_mode.into())
2250            .clipped(clipped)
2251            .old_swapchain(old_swapchain_vk);
2252
2253        let SwapchainCreateInfoExtensionsVk {
2254            full_screen_exclusive_vk,
2255            full_screen_exclusive_win32_vk,
2256            image_format_list_vk,
2257            present_modes_vk,
2258            present_scaling_vk,
2259        } = extensions_vk;
2260
2261        if let Some(next) = full_screen_exclusive_vk {
2262            val_vk = val_vk.push_next(next);
2263        }
2264
2265        if let Some(next) = full_screen_exclusive_win32_vk {
2266            val_vk = val_vk.push_next(next);
2267        }
2268
2269        if let Some(next) = image_format_list_vk {
2270            val_vk = val_vk.push_next(next);
2271        }
2272
2273        if let Some(next) = present_modes_vk {
2274            val_vk = val_vk.push_next(next);
2275        }
2276
2277        if let Some(next) = present_scaling_vk {
2278            val_vk = val_vk.push_next(next);
2279        }
2280
2281        val_vk
2282    }
2283
2284    pub(crate) fn to_vk_extensions<'a>(
2285        &self,
2286        fields1_vk: &'a SwapchainCreateInfoFields1Vk,
2287    ) -> SwapchainCreateInfoExtensionsVk<'a> {
2288        let &Self {
2289            flags: _,
2290            min_image_count: _,
2291            image_format: _,
2292            image_view_formats: _,
2293            image_color_space: _,
2294            image_extent: _,
2295            image_array_layers: _,
2296            image_usage: _,
2297            image_sharing: _,
2298            pre_transform: _,
2299            composite_alpha: _,
2300            present_mode: _,
2301            present_modes: _,
2302            clipped: _,
2303            scaling_behavior,
2304            present_gravity,
2305            full_screen_exclusive,
2306            win32_monitor,
2307            _ne: _,
2308        } = self;
2309        let SwapchainCreateInfoFields1Vk {
2310            present_modes_vk,
2311            view_formats_vk,
2312        } = fields1_vk;
2313
2314        let full_screen_exclusive_vk = (full_screen_exclusive != FullScreenExclusive::Default)
2315            .then(|| {
2316                ash::vk::SurfaceFullScreenExclusiveInfoEXT::default()
2317                    .full_screen_exclusive(full_screen_exclusive.into())
2318            });
2319
2320        let full_screen_exclusive_win32_vk = win32_monitor.map(|win32_monitor| {
2321            ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT::default().hmonitor(win32_monitor.0)
2322        });
2323
2324        let image_format_list_vk = (!view_formats_vk.is_empty())
2325            .then(|| ash::vk::ImageFormatListCreateInfo::default().view_formats(view_formats_vk));
2326
2327        let present_modes_vk = (!present_modes_vk.is_empty()).then(|| {
2328            ash::vk::SwapchainPresentModesCreateInfoEXT::default().present_modes(present_modes_vk)
2329        });
2330
2331        let present_scaling_vk =
2332            (scaling_behavior.is_some() || present_gravity.is_some()).then(|| {
2333                let [present_gravity_x, present_gravity_y] =
2334                    present_gravity.map_or_else(Default::default, |pg| pg.map(Into::into));
2335                ash::vk::SwapchainPresentScalingCreateInfoEXT::default()
2336                    .scaling_behavior(scaling_behavior.map_or_else(Default::default, Into::into))
2337                    .present_gravity_x(present_gravity_x)
2338                    .present_gravity_y(present_gravity_y)
2339            });
2340
2341        SwapchainCreateInfoExtensionsVk {
2342            full_screen_exclusive_vk,
2343            full_screen_exclusive_win32_vk,
2344            image_format_list_vk,
2345            present_modes_vk,
2346            present_scaling_vk,
2347        }
2348    }
2349
2350    pub(crate) fn to_vk_fields1(&self) -> SwapchainCreateInfoFields1Vk {
2351        let present_modes_vk = self.present_modes.iter().copied().map(Into::into).collect();
2352        let view_formats_vk = self
2353            .image_view_formats
2354            .iter()
2355            .copied()
2356            .map(ash::vk::Format::from)
2357            .collect();
2358
2359        SwapchainCreateInfoFields1Vk {
2360            present_modes_vk,
2361            view_formats_vk,
2362        }
2363    }
2364}
2365
2366pub(crate) struct SwapchainCreateInfoExtensionsVk<'a> {
2367    pub(crate) full_screen_exclusive_vk:
2368        Option<ash::vk::SurfaceFullScreenExclusiveInfoEXT<'static>>,
2369    pub(crate) full_screen_exclusive_win32_vk:
2370        Option<ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT<'static>>,
2371    pub(crate) image_format_list_vk: Option<ash::vk::ImageFormatListCreateInfo<'a>>,
2372    pub(crate) present_modes_vk: Option<ash::vk::SwapchainPresentModesCreateInfoEXT<'a>>,
2373    pub(crate) present_scaling_vk: Option<ash::vk::SwapchainPresentScalingCreateInfoEXT<'static>>,
2374}
2375
2376pub(crate) struct SwapchainCreateInfoFields1Vk {
2377    pub(crate) present_modes_vk: SmallVec<[ash::vk::PresentModeKHR; PresentMode::COUNT]>,
2378    pub(crate) view_formats_vk: Vec<ash::vk::Format>,
2379}
2380
2381vulkan_bitflags! {
2382    #[non_exhaustive]
2383
2384    /// Flags specifying additional properties of a swapchain.
2385    SwapchainCreateFlags = SwapchainCreateFlagsKHR(u32);
2386
2387    /* TODO: enable
2388    /// Creates swapchain images with the [`ImageCreateFlags::SPLIT_INSTANCE_BIND_REGIONS`] flag.
2389    SPLIT_INSTANCE_BIND_REGIONS = SPLIT_INSTANCE_BIND_REGIONS
2390    RequiresOneOf([
2391        RequiresAllOf([APIVersion(V1_1)]),
2392        RequiresAllOf([DeviceExtension(khr_device_group)]),
2393    ]),*/
2394
2395    /* TODO: enable
2396    /// Creates swapchain images with the [`ImageCreateFlags::PROTECTED`] flag.
2397    PROTECTED = PROTECTED
2398    RequiresOneOf([
2399        RequiresAllOf([APIVersion(V1_1)]),
2400    ]),*/
2401
2402    /// Creates swapchain images with both the [`ImageCreateFlags::MUTABLE_FORMAT`] and
2403    /// [`ImageCreateFlags::EXTENDED_USAGE`] flags.
2404    MUTABLE_FORMAT = MUTABLE_FORMAT
2405    RequiresOneOf([
2406        RequiresAllOf([DeviceExtension(khr_swapchain_mutable_format)]),
2407    ]),
2408
2409    /* TODO: enable
2410    // TODO: document
2411    DEFERRED_MEMORY_ALLOCATION = DEFERRED_MEMORY_ALLOCATION_EXT {
2412        device_extensions: [ext_swapchain_maintenance1],
2413    },*/
2414}
2415
2416impl From<SwapchainCreateFlags> for ImageCreateFlags {
2417    #[inline]
2418    fn from(flags: SwapchainCreateFlags) -> Self {
2419        // Per https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCreateSwapchainKHR.html#_description
2420        let mut result = ImageCreateFlags::empty();
2421
2422        /* TODO: enable
2423        if flags.intersects(SwapchainCreateFlags::SPLIT_INSTANCE_BIND_REGIONS) {
2424            result |= ImageCreateFlags::SPLIT_INSTANCE_BIND_REGIONS;
2425        } */
2426
2427        /* TODO: enable
2428        if flags.intersects(SwapchainCreateFlags::PROTECTED) {
2429            result |= ImageCreateFlags::PROTECTED;
2430        } */
2431
2432        if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT) {
2433            result |= ImageCreateFlags::MUTABLE_FORMAT | ImageCreateFlags::EXTENDED_USAGE;
2434        }
2435
2436        result
2437    }
2438}
2439
2440vulkan_bitflags_enum! {
2441    #[non_exhaustive]
2442
2443    /// A set of [`PresentScaling`] values.
2444    PresentScalingFlags,
2445
2446    /// The way a swapchain image is scaled, if it does not exactly fit the surface.
2447    PresentScaling,
2448
2449    = PresentScalingFlagsEXT(u32);
2450
2451    /// No scaling is performed; one swapchain image pixel maps to one surface pixel.
2452    ONE_TO_ONE, OneToOne = ONE_TO_ONE,
2453
2454    /// Both axes of the image are scaled equally, without changing the aspect ratio of the image,
2455    /// to the largest size in which both axes fit inside the surface.
2456    ASPECT_RATIO_STRETCH, AspectRatioStretch = ASPECT_RATIO_STRETCH,
2457
2458    /// Each axis of the image is scaled independently to fit the surface,
2459    /// which may change the aspect ratio of the image.
2460    STRETCH, Stretch = STRETCH,
2461}
2462
2463vulkan_bitflags_enum! {
2464    #[non_exhaustive]
2465
2466    /// A set of [`PresentGravity`] values.
2467    PresentGravityFlags,
2468
2469    /// The way a swapchain image is aligned, if it does not exactly fit the surface.
2470    PresentGravity,
2471
2472    = PresentGravityFlagsEXT(u32);
2473
2474    /// Aligned to the top or left side of the surface.
2475    MIN, Min = MIN,
2476
2477    /// Aligned to the bottom or right side of the surface.
2478    MAX, Max = MAX,
2479
2480    /// Aligned to the middle of the surface.
2481    CENTERED, Centered = CENTERED,
2482}
2483
2484vulkan_enum! {
2485    #[non_exhaustive]
2486
2487    /// The way full-screen exclusivity is handled.
2488    FullScreenExclusive = FullScreenExclusiveEXT(i32);
2489
2490    /// Indicates that the driver should determine the appropriate full-screen method
2491    /// by whatever means it deems appropriate.
2492    Default = DEFAULT,
2493
2494    /// Indicates that the driver may use full-screen exclusive mechanisms when available.
2495    /// Such mechanisms may result in better performance and/or the availability of
2496    /// different presentation capabilities, but may require a more disruptive transition
2497    // during swapchain initialization, first presentation and/or destruction.
2498    Allowed = ALLOWED,
2499
2500    /// Indicates that the driver should avoid using full-screen mechanisms which rely
2501    /// on disruptive transitions.
2502    Disallowed = DISALLOWED,
2503
2504    /// Indicates the application will manage full-screen exclusive mode by using the
2505    /// [`Swapchain::acquire_full_screen_exclusive_mode`] and
2506    /// [`Swapchain::release_full_screen_exclusive_mode`] functions.
2507    ApplicationControlled = APPLICATION_CONTROLLED,
2508}
2509
2510/// A wrapper around a Win32 monitor handle.
2511#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2512pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR);
2513
2514impl Win32Monitor {
2515    /// Wraps a Win32 monitor handle.
2516    ///
2517    /// # Safety
2518    ///
2519    /// - `hmonitor` must be a valid handle as returned by the Win32 API.
2520    pub unsafe fn new(hmonitor: ash::vk::HMONITOR) -> Self {
2521        Self(hmonitor)
2522    }
2523}
2524
2525// Winit's `MonitorHandle` is Send on Win32, so this seems safe.
2526unsafe impl Send for Win32Monitor {}
2527unsafe impl Sync for Win32Monitor {}