Skip to main content

vk_graph/driver/
swapchain.rs

1//! Native window presentation types.
2
3use {
4    super::{
5        DriverError, Surface,
6        device::Device,
7        fence::Fence,
8        image::{Image, ImageInfo},
9    },
10    ash::vk::{self, Handle as _},
11    derive_builder::Builder,
12    log::{Level, debug, error, info, log, trace, warn},
13    std::{
14        cell::RefCell,
15        error::Error,
16        fmt::{Display, Formatter},
17        iter::FusedIterator,
18        marker::PhantomData,
19        mem::replace,
20        ops::{Deref, Index},
21        slice,
22        thread::panicking,
23    },
24};
25
26#[cfg(feature = "checked")]
27use std::sync::atomic::{AtomicU64, Ordering};
28
29#[derive(Clone, Copy, Debug)]
30enum AcquireImageError {
31    OutOfDate,
32    Swapchain(SwapchainError),
33}
34
35impl From<SwapchainError> for AcquireImageError {
36    fn from(err: SwapchainError) -> Self {
37        Self::Swapchain(err)
38    }
39}
40
41/// Effective runtime information for a live [`Swapchain`].
42#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
43pub struct EffectiveSwapchainInfo {
44    /// Timeout in nanoseconds used when acquiring the next image.
45    pub acquire_timeout: u64,
46
47    /// Active alpha compositing mode used by the presentation engine.
48    pub composite_alpha: vk::CompositeAlphaFlagsKHR,
49
50    /// Whether pixels obscured by other windows on the native surface are clipped.
51    pub clipped: bool,
52
53    /// The live height of the surface.
54    pub height: u32,
55
56    /// The active number of array layers for each swapchain image.
57    pub image_array_layers: u32,
58
59    /// The number of images currently owned by the live swapchain.
60    pub image_count: u32,
61
62    /// The active sharing mode used for swapchain images.
63    pub image_sharing_mode: vk::SharingMode,
64
65    min_image_count: u32,
66
67    /// The active transform applied to presented images.
68    pub pre_transform: vk::SurfaceTransformFlagsKHR,
69
70    /// The active presentation mode.
71    pub present_mode: vk::PresentModeKHR,
72
73    /// Timeout in nanoseconds used when presenting an image.
74    pub present_timeout: u64,
75
76    /// The active format and color space of the surface.
77    pub surface: vk::SurfaceFormatKHR,
78
79    /// The live width of the surface.
80    pub width: u32,
81}
82
83impl EffectiveSwapchainInfo {
84    /// Creates a default `SwapchainInfoBuilder`.
85    pub fn builder() -> SwapchainInfoBuilder {
86        Default::default()
87    }
88
89    /// Converts an `EffectiveSwapchainInfo` into an `EffectiveSwapchainInfoBuilder`.
90    pub fn into_builder(self) -> SwapchainInfoBuilder {
91        let Self {
92            acquire_timeout,
93            clipped,
94            composite_alpha,
95            height,
96            image_array_layers,
97            image_count: _,
98            image_sharing_mode,
99            min_image_count,
100            pre_transform,
101            present_timeout,
102            present_mode,
103            surface,
104            width,
105        } = self;
106
107        SwapchainInfo {
108            acquire_timeout,
109            clipped,
110            composite_alpha,
111            height,
112            image_array_layers,
113            image_sharing_mode,
114            min_image_count,
115            pre_transform,
116            present_mode,
117            present_timeout,
118            surface,
119            width,
120        }
121        .into_builder()
122    }
123
124    /// Converts this effective runtime info back into requested swapchain info.
125    pub fn into_requested_info(self) -> SwapchainInfo {
126        SwapchainInfo::from(self)
127    }
128}
129
130#[derive(Debug)]
131struct Live;
132
133/// Describes one swapchain image to present.
134#[derive(Debug)]
135pub struct PresentInfo<'a> {
136    /// The acquired image to present.
137    pub image: SwapchainImage,
138
139    /// The swapchain that acquired `image`.
140    pub swapchain: &'a mut Swapchain,
141
142    /// Semaphores to wait on before presentation.
143    ///
144    /// Vulkan applies the flattened semaphore list to the whole present batch, not just this image.
145    pub wait_semaphores: &'a [vk::Semaphore],
146}
147
148/// Result of one image in a queue presentation batch.
149#[derive(Clone, Copy, Debug, Eq, PartialEq)]
150pub enum PresentResult {
151    /// The image was queued and the swapchain remained optimal.
152    QueuedOptimal,
153
154    /// The image was queued, but the swapchain should be recreated soon.
155    QueuedSuboptimal,
156
157    /// The image was not queued for presentation.
158    NotQueued(PresentError),
159}
160
161impl PresentResult {
162    /// Returns `true` if this image was queued for presentation.
163    pub fn queued(self) -> bool {
164        matches!(self, Self::QueuedOptimal | Self::QueuedSuboptimal)
165    }
166
167    /// Returns `true` if this present reported suboptimal state.
168    pub fn suboptimal(self) -> bool {
169        matches!(self, Self::QueuedSuboptimal)
170    }
171}
172
173/// Ordered results for a queue presentation batch.
174#[derive(Clone, Debug, Eq, PartialEq)]
175pub struct PresentBatch {
176    results: Box<[PresentResult]>,
177}
178
179impl PresentBatch {
180    fn new(results: Box<[PresentResult]>) -> Self {
181        Self { results }
182    }
183
184    /// Returns the per-input results in submission order.
185    pub fn as_slice(&self) -> &[PresentResult] {
186        &self.results
187    }
188
189    /// Returns `true` if there are no results.
190    pub fn is_empty(&self) -> bool {
191        self.results.is_empty()
192    }
193
194    /// Iterates over per-input results in submission order.
195    pub fn iter(&self) -> slice::Iter<'_, PresentResult> {
196        self.results.iter()
197    }
198
199    /// Returns the number of results.
200    pub fn len(&self) -> usize {
201        self.results.len()
202    }
203}
204
205impl AsRef<[PresentResult]> for PresentBatch {
206    fn as_ref(&self) -> &[PresentResult] {
207        self.as_slice()
208    }
209}
210
211impl Deref for PresentBatch {
212    type Target = [PresentResult];
213
214    fn deref(&self) -> &Self::Target {
215        self.as_slice()
216    }
217}
218
219impl Index<usize> for PresentBatch {
220    type Output = PresentResult;
221
222    fn index(&self, index: usize) -> &Self::Output {
223        &self.results[index]
224    }
225}
226
227impl IntoIterator for PresentBatch {
228    type IntoIter = PresentBatchIntoIter;
229    type Item = PresentResult;
230
231    fn into_iter(self) -> Self::IntoIter {
232        PresentBatchIntoIter {
233            inner: self.results.into_vec().into_iter(),
234        }
235    }
236}
237
238impl<'a> IntoIterator for &'a PresentBatch {
239    type IntoIter = slice::Iter<'a, PresentResult>;
240    type Item = &'a PresentResult;
241
242    fn into_iter(self) -> Self::IntoIter {
243        self.iter()
244    }
245}
246
247/// Error returned by a presentation batch.
248#[derive(Clone, Debug)]
249pub struct PresentBatchError {
250    /// Per-input results in the same order as the submitted [`PresentInfo`] values.
251    ///
252    /// Entries are `None` when presentation did not run far enough to determine that input's
253    /// result.
254    pub results: Box<[Option<PresentResult>]>,
255
256    /// The batch-level failure.
257    pub source: PresentFailure,
258}
259
260impl PresentBatchError {
261    fn new(source: PresentFailure, result_count: usize) -> Self {
262        Self {
263            results: vec![None; result_count].into_boxed_slice(),
264            source,
265        }
266    }
267
268    fn with_results(source: PresentFailure, results: &[PresentResult]) -> Self {
269        Self {
270            results: results.iter().copied().map(Some).collect(),
271            source,
272        }
273    }
274}
275
276impl Display for PresentBatchError {
277    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
278        write!(f, "unable to present batch: {}", self.source)
279    }
280}
281
282impl Error for PresentBatchError {
283    fn source(&self) -> Option<&(dyn Error + 'static)> {
284        match &self.source {
285            PresentFailure::Driver(err) => Some(err),
286            PresentFailure::DeviceLost
287            | PresentFailure::FullScreenExclusiveModeLost
288            | PresentFailure::OutOfDate
289            | PresentFailure::SurfaceLost => None,
290        }
291    }
292}
293
294/// Owning iterator over [`PresentBatch`] results.
295#[derive(Clone, Debug)]
296pub struct PresentBatchIntoIter {
297    inner: std::vec::IntoIter<PresentResult>,
298}
299
300impl DoubleEndedIterator for PresentBatchIntoIter {
301    fn next_back(&mut self) -> Option<Self::Item> {
302        self.inner.next_back()
303    }
304}
305
306impl ExactSizeIterator for PresentBatchIntoIter {
307    fn len(&self) -> usize {
308        self.inner.len()
309    }
310}
311
312impl FusedIterator for PresentBatchIntoIter {}
313
314impl Iterator for PresentBatchIntoIter {
315    type Item = PresentResult;
316
317    fn next(&mut self) -> Option<Self::Item> {
318        self.inner.next()
319    }
320
321    fn size_hint(&self) -> (usize, Option<usize>) {
322        self.inner.size_hint()
323    }
324}
325
326/// Reason an image was not queued for presentation.
327#[derive(Clone, Copy, Debug, Eq, PartialEq)]
328pub enum PresentError {
329    /// The device was lost.
330    DeviceLost,
331
332    /// Exclusive full-screen ownership was lost.
333    FullScreenExclusiveModeLost,
334
335    /// The swapchain is out of date and should be recreated.
336    OutOfDate,
337
338    /// The surface was lost and must be recreated.
339    SurfaceLost,
340}
341
342/// Batch-level presentation failure.
343#[derive(Clone, Copy, Debug)]
344pub enum PresentFailure {
345    /// The device was lost.
346    DeviceLost,
347
348    /// The driver rejected the request before item-level present results were available.
349    Driver(DriverError),
350
351    /// Exclusive full-screen ownership was lost.
352    FullScreenExclusiveModeLost,
353
354    /// The swapchain is out of date and should be recreated.
355    OutOfDate,
356
357    /// The surface was lost and must be recreated.
358    SurfaceLost,
359}
360
361impl Display for PresentFailure {
362    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
363        match self {
364            Self::DeviceLost => f.write_str("device lost"),
365            Self::Driver(err) => write!(f, "driver error: {err}"),
366            Self::FullScreenExclusiveModeLost => f.write_str("full-screen exclusive mode lost"),
367            Self::OutOfDate => f.write_str("out of date"),
368            Self::SurfaceLost => f.write_str("surface lost"),
369        }
370    }
371}
372
373impl Error for PresentFailure {
374    fn source(&self) -> Option<&(dyn Error + 'static)> {
375        match self {
376            Self::Driver(err) => Some(err),
377            Self::DeviceLost
378            | Self::FullScreenExclusiveModeLost
379            | Self::OutOfDate
380            | Self::SurfaceLost => None,
381        }
382    }
383}
384
385#[derive(Debug)]
386struct PresentRetirement {
387    next_present_id: u64,
388    image_present_ids: Box<[Option<u64>]>,
389    present_pending: bool,
390    queue: Option<QueueAddress>,
391    use_present_wait: bool,
392}
393
394impl PresentRetirement {
395    fn new(image_count: usize, use_present_wait: bool) -> Self {
396        Self {
397            // Present ID `0` means "no associated present ID".
398            next_present_id: 1,
399            image_present_ids: vec![None; image_count].into_boxed_slice(),
400            present_pending: false,
401            queue: None,
402            use_present_wait,
403        }
404    }
405}
406
407#[derive(Clone, Copy, Debug, Eq, PartialEq)]
408struct QueueAddress {
409    family_index: u32,
410    queue_index: u32,
411}
412
413#[derive(Debug, Default)]
414struct QueueFamily {
415    cmd_pool: vk::CommandPool,
416    queue_signals: Box<[QueueSignal]>,
417}
418
419#[derive(Debug)]
420struct QueueSignals {
421    device: Device,
422    families: Box<[QueueFamily]>,
423}
424
425impl QueueSignals {
426    fn create(surface: &Surface) -> Result<Self, DriverError> {
427        struct PartialQueueSignals<'a> {
428            device: &'a Device,
429            families: Vec<QueueFamily>,
430        }
431
432        impl Drop for PartialQueueSignals<'_> {
433            fn drop(&mut self) {
434                let mut queue_signals = QueueSignals {
435                    device: self.device.clone(),
436                    families: std::mem::take(&mut self.families).into_boxed_slice(),
437                };
438
439                queue_signals.destroy();
440            }
441        }
442
443        struct PartialQueueFamily<'a> {
444            device: &'a Device,
445            cmd_pool: vk::CommandPool,
446            queue_signals: Vec<QueueSignal>,
447        }
448
449        impl PartialQueueFamily<'_> {
450            fn finish(mut self) -> QueueFamily {
451                let family = QueueFamily {
452                    cmd_pool: self.cmd_pool,
453                    queue_signals: std::mem::take(&mut self.queue_signals).into_boxed_slice(),
454                };
455                self.cmd_pool = vk::CommandPool::null();
456
457                family
458            }
459        }
460
461        impl Drop for PartialQueueFamily<'_> {
462            fn drop(&mut self) {
463                if self.cmd_pool.is_null() {
464                    return;
465                }
466
467                thread_local! {
468                    static CMD_BUFS: RefCell<Vec<vk::CommandBuffer>> = Default::default();
469                }
470
471                CMD_BUFS.with_borrow_mut(|tls| {
472                    tls.clear();
473                    tls.extend(self.queue_signals.iter().map(|queue| queue.cmd_buf));
474
475                    unsafe {
476                        self.device.free_command_buffers(self.cmd_pool, tls);
477                        self.device.destroy_command_pool(self.cmd_pool, None);
478                    }
479                });
480            }
481        }
482
483        let device = &surface.device;
484        let all_queue_families = &device.physical.queue_families;
485        let mut partial = PartialQueueSignals {
486            device,
487            families: Vec::with_capacity(all_queue_families.len()),
488        };
489
490        for (idx, queue_family) in all_queue_families.iter().enumerate() {
491            let cmd_pool_info = vk::CommandPoolCreateInfo::default().queue_family_index(idx as _);
492            if !surface.physical_device_support(cmd_pool_info.queue_family_index)? {
493                partial.families.push(Default::default());
494
495                continue;
496            }
497
498            let cmd_pool = unsafe {
499                device
500                    .create_command_pool(&cmd_pool_info, None)
501                    .map_err(|err| {
502                        warn!("unable to create command pool: {err}");
503
504                        match err {
505                            vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
506                            | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
507                            _ => DriverError::Unsupported,
508                        }
509                    })?
510            };
511            let cmd_bufs = unsafe {
512                device
513                    .allocate_command_buffers(
514                        &vk::CommandBufferAllocateInfo::default()
515                            .command_buffer_count(queue_family.queue_count)
516                            .command_pool(cmd_pool)
517                            .level(vk::CommandBufferLevel::PRIMARY),
518                    )
519                    .map_err(|err| {
520                        warn!("unable to allocate command buffer: {err}");
521
522                        match err {
523                            vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
524                            | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
525                            _ => DriverError::Unsupported,
526                        }
527                    })?
528            };
529            let mut partial_family = PartialQueueFamily {
530                device,
531                cmd_pool,
532                queue_signals: Vec::with_capacity(queue_family.queue_count as _),
533            };
534
535            for cmd_buf in cmd_bufs {
536                Device::begin_command_buffer(
537                    device,
538                    cmd_buf,
539                    &vk::CommandBufferBeginInfo::default()
540                        .flags(vk::CommandBufferUsageFlags::SIMULTANEOUS_USE),
541                )?;
542                Device::end_command_buffer(device, cmd_buf)?;
543
544                let fence = Fence::create(device, false)?;
545                partial_family
546                    .queue_signals
547                    .push(QueueSignal { cmd_buf, fence });
548            }
549
550            partial.families.push(partial_family.finish());
551        }
552
553        let families = std::mem::take(&mut partial.families);
554        std::mem::forget(partial);
555
556        Ok(Self {
557            device: device.clone(),
558            families: families.into_boxed_slice(),
559        })
560    }
561
562    fn destroy(&mut self) {
563        // Intentionally idempotent: callers may destroy early, then `Drop` will call this again.
564        let families = std::mem::take(&mut self.families);
565
566        for queue_family in &families {
567            if queue_family.cmd_pool.is_null() {
568                continue;
569            }
570
571            thread_local! {
572                static CMD_BUFS: RefCell<Vec<vk::CommandBuffer>> = Default::default();
573            }
574
575            CMD_BUFS.with_borrow_mut(|tls| {
576                tls.clear();
577                tls.extend(queue_family.queue_signals.iter().map(|queue| queue.cmd_buf));
578                unsafe {
579                    self.device.free_command_buffers(queue_family.cmd_pool, tls);
580                }
581            });
582
583            unsafe {
584                self.device
585                    .destroy_command_pool(queue_family.cmd_pool, None);
586            }
587        }
588    }
589
590    fn reset_all(&mut self) -> Result<(), DriverError> {
591        for queue_family in &mut self.families {
592            for queue in &mut queue_family.queue_signals {
593                queue.fence.reset()?;
594            }
595        }
596
597        Ok(())
598    }
599
600    fn submit(
601        &mut self,
602        device: &Device,
603        queue: vk::Queue,
604        queue_family_index: u32,
605        queue_index: u32,
606    ) -> Result<(), SwapchainError> {
607        let queue_signal =
608            &mut self.families[queue_family_index as usize].queue_signals[queue_index as usize];
609
610        if queue_signal.fence.is_queued() {
611            queue_signal
612                .fence
613                .wait()
614                .map_err(|_| SwapchainError::DeviceLost)?
615                .reset()
616                .map_err(|_| SwapchainError::DeviceLost)?;
617        }
618
619        Device::queue_submit(
620            device,
621            queue,
622            slice::from_ref(
623                &vk::SubmitInfo::default().command_buffers(slice::from_ref(&queue_signal.cmd_buf)),
624            ),
625            queue_signal.fence.handle,
626        )
627        .map_err(|_| SwapchainError::DeviceLost)?;
628
629        queue_signal.fence.mark_queued();
630
631        Ok(())
632    }
633
634    fn wait_for_and_reset_all(&mut self, device: &Device) -> Result<(), DriverError> {
635        let mut fences = self.families.iter().flat_map(|queue_family| {
636            queue_family
637                .queue_signals
638                .iter()
639                .filter_map(|queue| queue.fence.is_queued().then_some(queue.fence.handle))
640        });
641
642        let Some(first) = fences.next() else {
643            return Ok(());
644        };
645
646        let Some(second) = fences.next() else {
647            Device::wait_for_fences(device, slice::from_ref(&first))?;
648            self.reset_all()?;
649
650            return Ok(());
651        };
652
653        thread_local! {
654            static FENCES: RefCell<Vec<vk::Fence>> = Default::default();
655        }
656
657        FENCES.with_borrow_mut(|tls| {
658            tls.clear();
659            tls.extend([first, second]);
660            tls.extend(fences);
661
662            Device::wait_for_fences(device, tls)
663        })?;
664
665        self.reset_all()?;
666
667        Ok(())
668    }
669}
670
671impl Drop for QueueSignals {
672    fn drop(&mut self) {
673        if panicking() {
674            return;
675        }
676
677        self.destroy();
678    }
679}
680
681#[derive(Debug)]
682struct QueueSignal {
683    cmd_buf: vk::CommandBuffer,
684    fence: Fence,
685}
686
687#[derive(Debug)]
688struct Retired;
689
690/// Provides the ability to present rendering results to a [`Surface`].
691///
692/// See [`VkSwapchainKHR`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkSwapchainKHR.html).
693#[derive(Debug)]
694#[read_only::cast]
695pub struct Swapchain {
696    /// The native Vulkan resource handle of this swapchain.
697    ///
698    /// _Note:_ This field is read-only.
699    pub handle: vk::SwapchainKHR,
700
701    /// Effective runtime information for the live swapchain.
702    ///
703    /// Calls to [`set_info`](Self::set_info) update the requested configuration. This field is
704    /// updated lazily after the next successful acquire recreates the swapchain. The timeout fields
705    /// are updated immediately.
706    ///
707    /// _Note:_ This field is read-only.
708    pub info: EffectiveSwapchainInfo,
709
710    live: SwapchainState<Live>,
711    queue_signals: QueueSignals,
712
713    /// Whether swapchain recreation has been requested but not completed yet.
714    pub recreate_pending: bool,
715
716    requested_info: SwapchainInfo,
717    retired: Vec<SwapchainState<Retired>>,
718
719    /// The surface which supports this swapchain.
720    ///
721    /// _Note:_ This field is read-only.
722    pub surface: Surface,
723
724    #[cfg(feature = "checked")]
725    swapchain_id: SwapchainId,
726}
727
728impl Swapchain {
729    /// Creates a [`vk::SwapchainKHR`] object.
730    #[profiling::function]
731    pub fn create(surface: Surface, info: impl Into<SwapchainInfo>) -> Result<Self, DriverError> {
732        let mut requested_info = info.into();
733        let queue_signals = QueueSignals::create(&surface)?;
734        let (live, info) = SwapchainState::create(&surface, &mut requested_info, None)?;
735        let handle = live.handle();
736
737        Ok(Self {
738            handle,
739            info,
740            queue_signals,
741            recreate_pending: false,
742            requested_info,
743            retired: Vec::new(),
744            surface,
745
746            #[cfg(feature = "checked")]
747            swapchain_id: live.swapchain_id,
748
749            live,
750        })
751    }
752
753    /// Acquires the next available swapchain image for rendering.
754    #[profiling::function]
755    pub fn acquire_next_image(
756        &mut self,
757        acquired: vk::Semaphore,
758    ) -> Result<SwapchainImage, SwapchainError> {
759        if self.recreate_pending {
760            self.recreate()?;
761        }
762
763        let image = match self.live.acquire_next_image(&self.surface.device, acquired) {
764            Ok(acquired) => acquired,
765            Err(AcquireImageError::OutOfDate) => {
766                self.recreate_pending = true;
767
768                return Err(SwapchainError::NotReady);
769            }
770            Err(AcquireImageError::Swapchain(
771                err @ SwapchainError::FullScreenExclusiveModeLost,
772            )) => {
773                self.recreate_pending = true;
774
775                return Err(err);
776            }
777            Err(AcquireImageError::Swapchain(err)) => return Err(err),
778        };
779
780        self.recreate_pending = image.suboptimal;
781
782        Ok(image)
783    }
784
785    fn clamp_min_image_count(min_image_count: u32, surface: vk::SurfaceCapabilitiesKHR) -> u32 {
786        let min_image_count = min_image_count.max(surface.min_image_count);
787
788        if surface.max_image_count == 0 {
789            return min_image_count;
790        }
791
792        min_image_count.min(surface.max_image_count)
793    }
794
795    fn destroy_retired(&mut self) -> Result<(), SwapchainError> {
796        let idx = 0;
797        let mut res = None;
798
799        while idx < self.retired.len() {
800            if let Err(err) = self
801                .retired
802                .swap_remove(idx)
803                .destroy_when_idle(&self.surface.device)
804                && !matches!(res, Some(SwapchainError::Driver(DriverError::Unsupported)))
805                && res
806                    .replace(err)
807                    .map(|old| old.different(err))
808                    .unwrap_or_default()
809            {
810                warn!("unsupported multi-error: {err:?}");
811
812                res = Some(SwapchainError::Driver(DriverError::Unsupported))
813            }
814        }
815
816        res.map(Result::Err).unwrap_or(Ok(()))
817    }
818
819    fn destroy_retired_on_drop(&mut self) {
820        for retired in &mut self.retired {
821            if let Err(err) =
822                retired.wait_until_retired(&self.surface.device, retired.present_timeout)
823            {
824                // Drop is best-effort shutdown. Preserve normal retryable semantics outside Drop,
825                // but do not silently lose retired Vulkan handles when the public owner is going away.
826                warn!("unable to wait for retired swapchain presents: {err:?}");
827            }
828
829            retired.handle.destroy();
830        }
831
832        self.retired.clear();
833    }
834
835    fn map_present_error(err: vk::Result) -> PresentError {
836        match err {
837            vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => {
838                debug!("unable to present: {}", err);
839
840                PresentError::FullScreenExclusiveModeLost
841            }
842            vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => {
843                debug!("unable to present: {}", err);
844
845                PresentError::OutOfDate
846            }
847            vk::Result::ERROR_DEVICE_LOST => {
848                info!("device lost");
849
850                PresentError::DeviceLost
851            }
852            vk::Result::ERROR_SURFACE_LOST_KHR => {
853                info!("surface lost");
854
855                PresentError::SurfaceLost
856            }
857            err => {
858                warn!("unable to present: {err}");
859
860                PresentError::DeviceLost
861            }
862        }
863    }
864
865    fn present_failure_from_present_error(err: PresentError) -> PresentFailure {
866        match err {
867            PresentError::DeviceLost => PresentFailure::DeviceLost,
868            PresentError::FullScreenExclusiveModeLost => {
869                PresentFailure::FullScreenExclusiveModeLost
870            }
871            PresentError::OutOfDate => PresentFailure::OutOfDate,
872            PresentError::SurfaceLost => PresentFailure::SurfaceLost,
873        }
874    }
875
876    fn present_failure_from_swapchain_error(err: SwapchainError) -> PresentFailure {
877        match err {
878            SwapchainError::Driver(err) => PresentFailure::Driver(err),
879            SwapchainError::DeviceLost => PresentFailure::DeviceLost,
880            SwapchainError::FullScreenExclusiveModeLost => {
881                PresentFailure::FullScreenExclusiveModeLost
882            }
883            SwapchainError::NotReady | SwapchainError::Timeout => {
884                // TODO: Better errors
885                PresentFailure::Driver(DriverError::Unsupported)
886            }
887            SwapchainError::SurfaceLost => PresentFailure::SurfaceLost,
888        }
889    }
890
891    fn present_failure_requires_recreate(err: PresentFailure) -> bool {
892        matches!(
893            err,
894            PresentFailure::FullScreenExclusiveModeLost | PresentFailure::OutOfDate,
895        )
896    }
897
898    fn present_result(
899        result: vk::Result,
900        aggregate_suboptimal: bool,
901        fallback_error: Option<PresentError>,
902    ) -> PresentResult {
903        match result {
904            vk::Result::SUCCESS if aggregate_suboptimal => PresentResult::QueuedSuboptimal,
905            vk::Result::SUCCESS => PresentResult::QueuedOptimal,
906            vk::Result::SUBOPTIMAL_KHR => PresentResult::QueuedSuboptimal,
907            vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => {
908                PresentResult::NotQueued(PresentError::FullScreenExclusiveModeLost)
909            }
910            vk::Result::ERROR_OUT_OF_DATE_KHR => PresentResult::NotQueued(PresentError::OutOfDate),
911            vk::Result::ERROR_SURFACE_LOST_KHR => {
912                PresentResult::NotQueued(PresentError::SurfaceLost)
913            }
914            vk::Result::ERROR_DEVICE_LOST => PresentResult::NotQueued(PresentError::DeviceLost),
915            _ => PresentResult::NotQueued(fallback_error.unwrap_or(PresentError::DeviceLost)),
916        }
917    }
918
919    fn present_result_requires_recreate(result: PresentResult) -> bool {
920        matches!(
921            result,
922            PresentResult::QueuedSuboptimal
923                | PresentResult::NotQueued(
924                    PresentError::FullScreenExclusiveModeLost | PresentError::OutOfDate,
925                )
926        )
927    }
928
929    /// Presents images previously acquired from their [`Swapchain`] instances.
930    ///
931    /// Panics with the same safety as [`Device::with_queue`].
932    #[profiling::function]
933    pub fn queue_present<'a>(
934        queue_family_index: u32,
935        queue_index: u32,
936        present_info: impl IntoIterator<Item = PresentInfo<'a>>,
937    ) -> Result<PresentBatch, PresentBatchError> {
938        let mut present_info = present_info.into_iter().collect::<Vec<_>>();
939        let Some(first) = present_info.first() else {
940            return Ok(PresentBatch::new(Box::new([])));
941        };
942        let present_count = present_info.len();
943        let device = first.swapchain.surface.device.clone();
944        let queue = QueueAddress {
945            family_index: queue_family_index,
946            queue_index,
947        };
948
949        for info in &mut present_info {
950            if !Device::is_same(&device, &info.swapchain.surface.device) {
951                warn!("unable to present swapchains from different devices in one batch");
952
953                return Err(PresentBatchError::new(
954                    PresentFailure::Driver(DriverError::InvalidData),
955                    present_count,
956                ));
957            }
958
959            info.swapchain
960                .validate_present_info(&info.image, queue_family_index, queue_index);
961            info.swapchain
962                .live
963                .wait_for_present_idx(
964                    &device,
965                    info.image.index,
966                    info.swapchain.info.present_timeout,
967                )
968                .map_err(|err| {
969                    PresentBatchError::new(
970                        Self::present_failure_from_swapchain_error(err),
971                        present_count,
972                    )
973                })?;
974        }
975
976        let mut image_indices = Vec::with_capacity(present_info.len());
977        let mut present_ids = Vec::with_capacity(present_info.len());
978        let mut results = vec![vk::Result::ERROR_UNKNOWN; present_info.len()];
979        let mut swapchains = Vec::with_capacity(present_info.len());
980        let mut wait_semaphores = Vec::new();
981
982        for info in &mut present_info {
983            let image_idx = info.image.index as usize;
984
985            image_indices.push(info.image.index);
986            swapchains.push(info.swapchain.live.handle());
987            wait_semaphores.extend_from_slice(info.wait_semaphores);
988            present_ids.push(
989                info.swapchain
990                    .live
991                    .next_present_id(image_idx)
992                    .unwrap_or_default(),
993            );
994        }
995
996        let mut vk_present_info = vk::PresentInfoKHR::default()
997            .wait_semaphores(&wait_semaphores)
998            .swapchains(&swapchains)
999            .image_indices(&image_indices)
1000            .results(&mut results);
1001
1002        let mut present_id_info;
1003        if present_ids.iter().any(|present_id| *present_id != 0) {
1004            present_id_info = vk::PresentIdKHR::default().present_ids(&present_ids);
1005            vk_present_info = vk_present_info.push_next(&mut present_id_info);
1006        }
1007
1008        let present =
1009            Device::with_queue(&device, queue_family_index, queue_index, |queue| unsafe {
1010                Device::expect_vk_khr_swapchain(&device)
1011                    .queue_present(queue, &vk_present_info)
1012                    .map_err(Self::map_present_error)
1013            });
1014        let present_error = present.err();
1015        let aggregate_suboptimal = matches!(present, Ok(true));
1016        let present_results = results
1017            .iter()
1018            .map(|result| Self::present_result(*result, aggregate_suboptimal, present_error))
1019            .collect::<Box<_>>();
1020
1021        let present_failure = present_error.map(Self::present_failure_from_present_error);
1022        let recreate_batch = present_failure.is_some_and(Self::present_failure_requires_recreate);
1023
1024        for (idx, info) in present_info.iter_mut().enumerate() {
1025            if recreate_batch || Self::present_result_requires_recreate(present_results[idx]) {
1026                info.swapchain.recreate_pending = true;
1027            }
1028
1029            if !present_results[idx].queued() {
1030                info.swapchain
1031                    .live
1032                    .clear_present_id(info.image.index as usize);
1033            }
1034        }
1035
1036        for (idx, info) in present_info.iter_mut().enumerate() {
1037            if !present_results[idx].queued() {
1038                continue;
1039            }
1040
1041            let image = unsafe { info.image.to_detached() };
1042            info.swapchain.live.mark_present_queued(image, queue);
1043        }
1044
1045        let signal = Device::with_queue(&device, queue_family_index, queue_index, |queue| {
1046            for (idx, info) in present_info.iter_mut().enumerate() {
1047                if !present_results[idx].queued() {
1048                    continue;
1049                }
1050
1051                info.swapchain.queue_signals.submit(
1052                    &device,
1053                    queue,
1054                    queue_family_index,
1055                    queue_index,
1056                )?;
1057            }
1058
1059            Ok::<_, SwapchainError>(())
1060        });
1061
1062        if let Err(err) = signal {
1063            return Err(PresentBatchError::with_results(
1064                Self::present_failure_from_swapchain_error(err),
1065                &present_results,
1066            ));
1067        }
1068
1069        if let Some(err) = present_error {
1070            return Err(PresentBatchError::with_results(
1071                Self::present_failure_from_present_error(err),
1072                &present_results,
1073            ));
1074        }
1075
1076        Ok(PresentBatch::new(present_results))
1077    }
1078
1079    /// Recreates the swapchain using the currently requested configuration.
1080    #[profiling::function]
1081    pub fn recreate(&mut self) -> Result<(), SwapchainError> {
1082        self.queue_signals
1083            .wait_for_and_reset_all(&self.surface.device)?;
1084        self.destroy_retired()?;
1085
1086        let (new_live, info) = SwapchainState::create(
1087            &self.surface,
1088            &mut self.requested_info,
1089            Some(self.live.handle()),
1090        )?;
1091        let old_live = replace(&mut self.live, new_live);
1092
1093        self.retired.push(old_live.into_retired());
1094        self.handle = self.live.handle();
1095        self.info = info;
1096        self.recreate_pending = false;
1097
1098        #[cfg(feature = "checked")]
1099        {
1100            self.swapchain_id = self.live.swapchain_id;
1101        }
1102
1103        Ok(())
1104    }
1105
1106    /// Updates the requested information which controls this swapchain.
1107    ///
1108    /// The live effective [`info`](Self::info) field updates [`acquire_timeout`](SwapchainInfo::acquire_timeout)
1109    /// immediately. Other fields are updated lazily after the next successful acquire recreates the
1110    /// swapchain and reflects the runtime values that were actually selected.
1111    pub fn set_info(&mut self, info: impl Into<SwapchainInfo>) {
1112        let info: SwapchainInfo = info.into();
1113
1114        let recreate = self.requested_info.height != info.height
1115            || self.requested_info.clipped != info.clipped
1116            || self.requested_info.image_array_layers != info.image_array_layers
1117            || self.requested_info.image_sharing_mode != info.image_sharing_mode
1118            || self.requested_info.min_image_count != info.min_image_count
1119            || self.requested_info.pre_transform != info.pre_transform
1120            || self.requested_info.present_mode != info.present_mode
1121            || self.requested_info.composite_alpha != info.composite_alpha
1122            || self.requested_info.surface != info.surface
1123            || self.requested_info.width != info.width;
1124
1125        self.requested_info = info;
1126        self.info.acquire_timeout = info.acquire_timeout;
1127        self.info.present_timeout = info.present_timeout;
1128        self.live.acquire_timeout = info.acquire_timeout;
1129        self.live.present_timeout = info.present_timeout;
1130
1131        trace!("requested info: {:?}", self.requested_info);
1132
1133        if recreate {
1134            self.recreate_pending = true;
1135        }
1136    }
1137
1138    fn supported_surface_usage(
1139        device: &Device,
1140        surface_format: vk::Format,
1141        surface_capabilities: vk::ImageUsageFlags,
1142    ) -> Result<vk::ImageUsageFlags, DriverError> {
1143        let image_usage = surface_capabilities & !vk::ImageUsageFlags::ATTACHMENT_FEEDBACK_LOOP_EXT;
1144
1145        if image_usage.is_empty() {
1146            warn!("surface reports no supported swapchain image usage");
1147
1148            return Err(DriverError::Unsupported);
1149        }
1150
1151        if device
1152            .physical
1153            .image_format_properties(
1154                surface_format,
1155                vk::ImageType::TYPE_2D,
1156                vk::ImageTiling::OPTIMAL,
1157                image_usage,
1158                vk::ImageCreateFlags::empty(),
1159            )
1160            .inspect_err(|err| {
1161                warn!(
1162                    "unable to get image format properties: {:?} {:?} {err}",
1163                    surface_format, image_usage
1164                )
1165            })?
1166            .is_none()
1167        {
1168            warn!(
1169                "unsupported swapchain image usage combination: {:?} {:?}",
1170                surface_format, image_usage
1171            );
1172
1173            return Err(DriverError::Unsupported);
1174        }
1175
1176        Ok(image_usage)
1177    }
1178
1179    fn validate_present_info(
1180        &self,
1181        image: &SwapchainImage,
1182        queue_family_index: u32,
1183        queue_index: u32,
1184    ) {
1185        #[cfg(feature = "checked")]
1186        {
1187            assert!(
1188                image.index < self.live.images.len() as u32,
1189                "swapchain image index out of bounds"
1190            );
1191            assert!(
1192                image.swapchain_id == self.swapchain_id,
1193                "swapchain image belongs to a different swapchain"
1194            );
1195        }
1196
1197        #[cfg(feature = "checked")]
1198        assert!(
1199            self.queue_signals
1200                .families
1201                .get(queue_family_index as usize)
1202                .and_then(|queue_family| queue_family.queue_signals.get(queue_index as usize))
1203                .is_some(),
1204            "unsupported queue index"
1205        );
1206    }
1207
1208    /// Waits for a previously queued present on `image_idx` from the live swapchain to complete.
1209    ///
1210    /// This does not search retired swapchain handles after recreation. Callers that may span
1211    /// recreation should retire their own pending image references before acquiring again.
1212    ///
1213    /// # Safety
1214    ///
1215    /// Callers must ensure that `image_idx` refers to an image acquired from this swapchain and
1216    /// that waiting for the associated present is valid for the current lifetime of the swapchain.
1217    pub unsafe fn wait_for_present_image(
1218        &mut self,
1219        image_idx: u32,
1220        timeout: u64,
1221    ) -> Result<(), SwapchainError> {
1222        self.live
1223            .wait_for_present_idx(&self.surface.device, image_idx, timeout)
1224    }
1225}
1226
1227impl Drop for Swapchain {
1228    #[profiling::function]
1229    fn drop(&mut self) {
1230        if panicking() {
1231            return;
1232        }
1233
1234        if let Err(err) = self
1235            .queue_signals
1236            .wait_for_and_reset_all(&self.surface.device)
1237        {
1238            warn!("unable to wait for swapchain signals: {err}");
1239        }
1240
1241        self.destroy_retired_on_drop();
1242
1243        if let Err(err) = self
1244            .live
1245            .wait_for_all_presents(&self.surface.device, self.info.present_timeout)
1246        {
1247            // Drop is best-effort shutdown. At this point there is no recoverable path for the
1248            // public owner, so log the failed wait and still release the Vulkan handle.
1249            warn!("unable to wait for live swapchain presents: {err:?}");
1250        }
1251
1252        // Destroy handles explicitly while queue signals are still alive; their field drop order is
1253        // otherwise independent of the present-retirement fallback queues recorded by the states.
1254        self.live.handle.destroy();
1255        self.handle = vk::SwapchainKHR::null();
1256    }
1257}
1258
1259impl Eq for Swapchain {}
1260
1261impl PartialEq for Swapchain {
1262    fn eq(&self, other: &Self) -> bool {
1263        self.handle == other.handle
1264    }
1265}
1266
1267/// Describes the condition of a swapchain.
1268#[derive(Clone, Copy, Debug)]
1269pub enum SwapchainError {
1270    /// This frame is lost but more may be acquired later.
1271    DeviceLost,
1272
1273    /// Recoverable driver error.
1274    Driver(DriverError),
1275
1276    /// Exclusive full-screen ownership was lost and the swapchain should be recreated.
1277    FullScreenExclusiveModeLost,
1278
1279    /// No image is currently available, but a future acquire may succeed.
1280    NotReady,
1281
1282    /// The surface was lost and must be recreated, which includes any operating system window.
1283    SurfaceLost,
1284
1285    /// No image was acquired before the configured acquire timeout elapsed.
1286    Timeout,
1287}
1288
1289impl SwapchainError {
1290    fn different(self, other: Self) -> bool {
1291        !self.matches(other)
1292    }
1293
1294    fn matches(self, other: Self) -> bool {
1295        use SwapchainError::*;
1296
1297        matches!(
1298            (self, other),
1299            (DeviceLost, DeviceLost)
1300                | (
1301                    Driver(DriverError::InvalidData),
1302                    Driver(DriverError::InvalidData)
1303                )
1304                | (
1305                    Driver(DriverError::OutOfMemory),
1306                    Driver(DriverError::OutOfMemory)
1307                )
1308                | (
1309                    Driver(DriverError::Unsupported),
1310                    Driver(DriverError::Unsupported)
1311                )
1312                | (FullScreenExclusiveModeLost, FullScreenExclusiveModeLost)
1313                | (NotReady, NotReady)
1314                | (SurfaceLost, SurfaceLost)
1315                | (Timeout, Timeout)
1316        )
1317    }
1318}
1319
1320impl From<DriverError> for SwapchainError {
1321    fn from(err: DriverError) -> Self {
1322        match err {
1323            DriverError::InvalidData => Self::DeviceLost,
1324            err => Self::Driver(err),
1325        }
1326    }
1327}
1328
1329#[derive(Debug)]
1330struct SwapchainHandle {
1331    device: Device,
1332    handle: vk::SwapchainKHR,
1333}
1334
1335impl SwapchainHandle {
1336    fn new(device: &Device, handle: vk::SwapchainKHR) -> Self {
1337        Self {
1338            device: device.clone(),
1339            handle,
1340        }
1341    }
1342
1343    fn destroy(&mut self) {
1344        Self::destroy_raw(&self.device, &mut self.handle);
1345    }
1346
1347    fn destroy_raw(device: &Device, handle: &mut vk::SwapchainKHR) {
1348        if handle.is_null() {
1349            return;
1350        }
1351
1352        unsafe {
1353            Device::expect_vk_khr_swapchain(device).destroy_swapchain(*handle, None);
1354        }
1355
1356        *handle = vk::SwapchainKHR::null();
1357    }
1358
1359    fn raw(&self) -> vk::SwapchainKHR {
1360        self.handle
1361    }
1362}
1363
1364impl Drop for SwapchainHandle {
1365    fn drop(&mut self) {
1366        if panicking() {
1367            return;
1368        }
1369
1370        self.destroy();
1371    }
1372}
1373
1374/// Opaque swapchain ownership identifier used by checked builds.
1375#[cfg(feature = "checked")]
1376#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1377struct SwapchainId(u64);
1378
1379#[cfg(feature = "checked")]
1380impl SwapchainId {
1381    fn next() -> Self {
1382        static NEXT_ID: AtomicU64 = AtomicU64::new(1);
1383
1384        Self(NEXT_ID.fetch_add(1, Ordering::Relaxed))
1385    }
1386}
1387
1388/// An opaque type representing a swapchain image.
1389///
1390/// In checked builds, using a swapchain image with a different swapchain than the one that acquired
1391/// it will panic.
1392#[derive(Debug)]
1393#[read_only::embed]
1394pub struct SwapchainImage {
1395    #[readonly]
1396    pub(self) image: Image,
1397
1398    /// The swapchain image index.
1399    ///
1400    /// _Note:_ This field is read-only.
1401    #[readonly]
1402    pub index: u32,
1403
1404    /// Whether the swapchain was suboptimal when this image was acquired.
1405    ///
1406    /// _Note:_ This field is read-only.
1407    #[readonly]
1408    pub suboptimal: bool,
1409
1410    #[cfg(feature = "checked")]
1411    #[readonly]
1412    pub(self) swapchain_id: SwapchainId,
1413}
1414
1415impl SwapchainImage {
1416    #[cfg(test)]
1417    pub(crate) fn from_raw(
1418        device: &Device,
1419        handle: vk::Image,
1420        info: impl Into<ImageInfo>,
1421        index: u32,
1422    ) -> Self {
1423        Self {
1424            read_only: ReadOnlySwapchainImage {
1425                image: unsafe { Image::from_raw(device, handle, info) },
1426                index,
1427                suboptimal: false,
1428
1429                #[cfg(feature = "checked")]
1430                swapchain_id: SwapchainId::next(),
1431            },
1432        }
1433    }
1434
1435    /// Creates an acquired wrapper for the same native swapchain image.
1436    ///
1437    /// # Safety
1438    ///
1439    /// The caller must ensure the Vulkan image handle remains valid for the lifetime of the
1440    /// returned image. This should only be called for images owned by a live swapchain, and each
1441    /// detached wrapper must be used according to the swapchain image acquire/present lifecycle.
1442    pub unsafe fn to_detached(&self) -> Self {
1443        Self {
1444            read_only: ReadOnlySwapchainImage {
1445                image: unsafe { self.image.to_detached() },
1446                index: self.index,
1447                suboptimal: false,
1448
1449                #[cfg(feature = "checked")]
1450                swapchain_id: self.swapchain_id,
1451            },
1452        }
1453    }
1454}
1455
1456impl Deref for ReadOnlySwapchainImage {
1457    type Target = Image;
1458
1459    fn deref(&self) -> &Self::Target {
1460        &self.image
1461    }
1462}
1463
1464/// Information used to create a [`Swapchain`] instance.
1465#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
1466#[builder(
1467    build_fn(private, name = "fallible_build"),
1468    derive(Clone, Copy, Debug),
1469    pattern = "owned"
1470)]
1471pub struct SwapchainInfo {
1472    /// Timeout in nanoseconds used when acquiring the next image.
1473    ///
1474    /// Do not use `u64::MAX` on surfaces where acquire forward progress cannot be guaranteed.
1475    #[builder(default = "u64::MAX")]
1476    pub acquire_timeout: u64,
1477
1478    /// Alpha compositing mode used by the presentation engine.
1479    #[builder(default = vk::CompositeAlphaFlagsKHR::OPAQUE)]
1480    pub composite_alpha: vk::CompositeAlphaFlagsKHR,
1481
1482    /// Whether to clip pixels obscured by other windows on the native surface.
1483    #[builder(default = "true")]
1484    pub clipped: bool,
1485
1486    /// The initial height of the surface.
1487    #[builder(default)]
1488    pub height: u32,
1489
1490    /// The requested number of array layers for each swapchain image.
1491    #[builder(default = "1")]
1492    pub image_array_layers: u32,
1493
1494    /// Sharing mode used for swapchain images.
1495    #[builder(default = vk::SharingMode::EXCLUSIVE)]
1496    pub image_sharing_mode: vk::SharingMode,
1497
1498    /// The minimum number of presentable images that the application needs.
1499    #[builder(default = "2")]
1500    pub min_image_count: u32,
1501
1502    /// Transform applied to presented images.
1503    #[builder(default = vk::SurfaceTransformFlagsKHR::IDENTITY)]
1504    pub pre_transform: vk::SurfaceTransformFlagsKHR,
1505
1506    /// `vk::PresentModeKHR` determines timing and queueing with which frames are displayed.
1507    #[builder(default = vk::PresentModeKHR::IMMEDIATE)]
1508    pub present_mode: vk::PresentModeKHR,
1509
1510    /// Timeout in nanoseconds used when presenting an image.
1511    #[builder(default = "u64::MAX")]
1512    pub present_timeout: u64,
1513
1514    /// The format and color space of the surface.
1515    #[builder(default)]
1516    pub surface: vk::SurfaceFormatKHR,
1517
1518    /// The initial width of the surface.
1519    #[builder(default)]
1520    pub width: u32,
1521}
1522
1523impl SwapchainInfo {
1524    /// Specifies a default swapchain with the given `width`, `height` and `format` values.
1525    #[inline(always)]
1526    pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo {
1527        Self {
1528            acquire_timeout: u64::MAX,
1529            clipped: true,
1530            composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
1531            height,
1532            image_array_layers: 1,
1533            image_sharing_mode: vk::SharingMode::EXCLUSIVE,
1534            min_image_count: 2,
1535            pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
1536            present_mode: vk::PresentModeKHR::IMMEDIATE,
1537            present_timeout: u64::MAX,
1538            surface,
1539            width,
1540        }
1541    }
1542
1543    /// Creates a default `SwapchainInfoBuilder`.
1544    pub fn builder() -> SwapchainInfoBuilder {
1545        Default::default()
1546    }
1547
1548    /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`.
1549    pub fn into_builder(self) -> SwapchainInfoBuilder {
1550        let Self {
1551            acquire_timeout,
1552            clipped,
1553            composite_alpha,
1554            height,
1555            image_array_layers,
1556            image_sharing_mode,
1557            min_image_count,
1558            pre_transform,
1559            present_mode,
1560            present_timeout,
1561            surface,
1562            width,
1563        } = self;
1564
1565        let acquire_timeout = Some(acquire_timeout);
1566        let clipped = Some(clipped);
1567        let composite_alpha = Some(composite_alpha);
1568        let height = Some(height);
1569        let image_array_layers = Some(image_array_layers);
1570        let image_sharing_mode = Some(image_sharing_mode);
1571        let min_image_count = Some(min_image_count);
1572        let pre_transform = Some(pre_transform);
1573        let present_mode = Some(present_mode);
1574        let present_timeout = Some(present_timeout);
1575        let surface = Some(surface);
1576        let width = Some(width);
1577
1578        SwapchainInfoBuilder {
1579            acquire_timeout,
1580            clipped,
1581            composite_alpha,
1582            height,
1583            image_array_layers,
1584            image_sharing_mode,
1585            min_image_count,
1586            pre_transform,
1587            present_mode,
1588            present_timeout,
1589            surface,
1590            width,
1591        }
1592    }
1593}
1594
1595impl From<SwapchainInfoBuilder> for SwapchainInfo {
1596    fn from(info: SwapchainInfoBuilder) -> Self {
1597        info.build()
1598    }
1599}
1600
1601impl From<EffectiveSwapchainInfo> for SwapchainInfo {
1602    fn from(info: EffectiveSwapchainInfo) -> Self {
1603        let EffectiveSwapchainInfo {
1604            acquire_timeout,
1605            clipped,
1606            composite_alpha,
1607            height,
1608            image_array_layers,
1609            image_count: _,
1610            image_sharing_mode,
1611            min_image_count,
1612            pre_transform,
1613            present_mode,
1614            present_timeout,
1615            surface,
1616            width,
1617        } = info;
1618
1619        Self {
1620            acquire_timeout,
1621            clipped,
1622            composite_alpha,
1623            height,
1624            image_array_layers,
1625            image_sharing_mode,
1626            min_image_count,
1627            pre_transform,
1628            present_mode,
1629            present_timeout,
1630            surface,
1631            width,
1632        }
1633    }
1634}
1635
1636impl SwapchainInfoBuilder {
1637    /// Builds a new `SwapchainInfo`.
1638    #[inline(always)]
1639    pub fn build(self) -> SwapchainInfo {
1640        self.fallible_build().expect("all fields have defaults")
1641    }
1642}
1643
1644#[derive(Debug)]
1645struct SwapchainState<S> {
1646    __: PhantomData<S>,
1647    acquire_timeout: u64,
1648    handle: SwapchainHandle,
1649    images: Box<[Option<Image>]>,
1650    present_timeout: u64,
1651    retirement: PresentRetirement,
1652
1653    #[cfg(feature = "checked")]
1654    swapchain_id: SwapchainId,
1655}
1656
1657impl<S> SwapchainState<S> {
1658    fn handle(&self) -> vk::SwapchainKHR {
1659        self.handle.raw()
1660    }
1661
1662    fn wait_for_all_presents(
1663        &mut self,
1664        device: &Device,
1665        timeout: u64,
1666    ) -> Result<(), SwapchainError> {
1667        if !self.retirement.present_pending {
1668            return Ok(());
1669        }
1670
1671        if !self.retirement.use_present_wait {
1672            let queue = self.retirement.queue.expect("missing present queue");
1673            let queue_wait_idle =
1674                Device::with_queue(device, queue.family_index, queue.queue_index, |queue| {
1675                    Device::queue_wait_idle(device, queue)
1676                });
1677
1678            self.retirement.present_pending = false;
1679            queue_wait_idle?;
1680
1681            return Ok(());
1682        }
1683
1684        let swapchain = self.handle();
1685        let mut res = None;
1686
1687        for present_id in self
1688            .retirement
1689            .image_present_ids
1690            .iter_mut()
1691            .filter_map(|present_id| present_id.map(|_| present_id))
1692        {
1693            let Some(id) = *present_id else {
1694                unreachable!();
1695            };
1696
1697            let wait_for_present =
1698                unsafe { Self::wait_for_present(device, swapchain, id, timeout) };
1699
1700            *present_id = None;
1701
1702            if let Err(err) = wait_for_present
1703                && !matches!(res, Some(SwapchainError::Driver(DriverError::Unsupported)))
1704                && res
1705                    .replace(err)
1706                    .map(|old| old.different(err))
1707                    .unwrap_or_default()
1708            {
1709                warn!("unsupported multi-error: {err:?}");
1710
1711                res = Some(SwapchainError::Driver(DriverError::Unsupported))
1712            }
1713        }
1714
1715        self.retirement.present_pending = false;
1716
1717        res.map(Result::Err).unwrap_or(Ok(()))
1718    }
1719
1720    unsafe fn wait_for_present(
1721        device: &Device,
1722        swapchain: vk::SwapchainKHR,
1723        present_id: u64,
1724        timeout: u64,
1725    ) -> Result<(), SwapchainError> {
1726        unsafe {
1727            Device::expect_vk_khr_present_wait(device)
1728                .wait_for_present(swapchain, present_id, timeout)
1729                .map_err(|err| {
1730                    warn!("unable to wait for present: {err}");
1731
1732                    match err {
1733                        vk::Result::ERROR_DEVICE_LOST => SwapchainError::DeviceLost,
1734                        vk::Result::ERROR_SURFACE_LOST_KHR => SwapchainError::SurfaceLost,
1735                        vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => {
1736                            SwapchainError::FullScreenExclusiveModeLost
1737                        }
1738                        vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => {
1739                            SwapchainError::NotReady
1740                        }
1741                        _ => SwapchainError::SurfaceLost,
1742                    }
1743                })
1744        }
1745    }
1746}
1747
1748impl SwapchainState<Live> {
1749    fn create(
1750        surface: &Surface,
1751        requested_info: &mut SwapchainInfo,
1752        old_swapchain: Option<vk::SwapchainKHR>,
1753    ) -> Result<(Self, EffectiveSwapchainInfo), DriverError> {
1754        let device = &surface.device;
1755        let surface_caps = Surface::capabilities(surface)?;
1756
1757        {
1758            let composite_alpha = Surface::composite_alpha_or_default(
1759                surface_caps.supported_composite_alpha,
1760                requested_info.composite_alpha,
1761            );
1762            if requested_info.composite_alpha != composite_alpha {
1763                warn!(
1764                    "requested composite alpha unsupported: {:?}; falling back to {:?}",
1765                    requested_info.composite_alpha, composite_alpha
1766                );
1767
1768                requested_info.composite_alpha = composite_alpha;
1769            }
1770        }
1771
1772        {
1773            let image_array_layers = requested_info
1774                .image_array_layers
1775                .clamp(1, surface_caps.max_image_array_layers);
1776            if requested_info.image_array_layers != image_array_layers {
1777                warn!(
1778                    "requested image array layers unsupported: {}; falling back to {}",
1779                    requested_info.image_array_layers, image_array_layers
1780                );
1781
1782                requested_info.image_array_layers = image_array_layers;
1783            }
1784        }
1785
1786        {
1787            if requested_info.image_sharing_mode == vk::SharingMode::CONCURRENT
1788                && device.physical.queue_families.len() < 2
1789            {
1790                warn!(
1791                    "requested concurrent image sharing with one queue family; falling back to exclusive"
1792                );
1793
1794                requested_info.image_sharing_mode = vk::SharingMode::EXCLUSIVE;
1795            }
1796        }
1797
1798        {
1799            requested_info.min_image_count =
1800                Swapchain::clamp_min_image_count(requested_info.min_image_count, surface_caps);
1801        }
1802
1803        {
1804            if !surface_caps
1805                .supported_transforms
1806                .contains(requested_info.pre_transform)
1807            {
1808                warn!(
1809                    "requested pre-transform unsupported: {:?}; falling back to {:?}",
1810                    requested_info.pre_transform, surface_caps.current_transform
1811                );
1812
1813                requested_info.pre_transform = surface_caps.current_transform;
1814            }
1815        }
1816
1817        {
1818            let present_modes = Surface::present_modes(surface)?;
1819            if !present_modes.contains(&requested_info.present_mode) {
1820                warn!(
1821                    "requested present mode unsupported: {:?}; falling back to FIFO",
1822                    requested_info.present_mode
1823                );
1824
1825                requested_info.present_mode = vk::PresentModeKHR::FIFO;
1826            }
1827        }
1828
1829        {
1830            // TODO: There is special handling for the MAX case we're not doing (read specs)
1831            if surface_caps.current_extent.width == u32::MAX
1832                && surface_caps.current_extent.height == u32::MAX
1833            {
1834                requested_info.width = requested_info.width.clamp(
1835                    surface_caps.min_image_extent.width,
1836                    surface_caps.max_image_extent.width,
1837                );
1838                requested_info.height = requested_info.height.clamp(
1839                    surface_caps.min_image_extent.height,
1840                    surface_caps.max_image_extent.height,
1841                );
1842            } else {
1843                requested_info.width = surface_caps.current_extent.width;
1844                requested_info.height = surface_caps.current_extent.height;
1845            }
1846
1847            if requested_info.width == 0 || requested_info.height == 0 {
1848                warn!(
1849                    "invalid surface extent: computed {}x{}",
1850                    requested_info.width, requested_info.height
1851                );
1852
1853                return Err(DriverError::Unsupported);
1854            }
1855        }
1856
1857        let image_extent = vk::Extent2D {
1858            width: requested_info.width,
1859            height: requested_info.height,
1860        };
1861        let image_usage = Swapchain::supported_surface_usage(
1862            device,
1863            requested_info.surface.format,
1864            surface_caps.supported_usage_flags,
1865        )?;
1866
1867        let mut swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
1868            .clipped(requested_info.clipped)
1869            .composite_alpha(requested_info.composite_alpha)
1870            .image_color_space(requested_info.surface.color_space)
1871            .image_extent(image_extent)
1872            .image_format(requested_info.surface.format)
1873            .image_sharing_mode(requested_info.image_sharing_mode)
1874            .image_usage(image_usage)
1875            .min_image_count(requested_info.min_image_count)
1876            .old_swapchain(old_swapchain.unwrap_or_default())
1877            .pre_transform(requested_info.pre_transform)
1878            .present_mode(requested_info.present_mode)
1879            .image_array_layers(requested_info.image_array_layers)
1880            .surface(surface.handle);
1881
1882        let queue_family_indices = matches!(
1883            requested_info.image_sharing_mode,
1884            vk::SharingMode::CONCURRENT
1885        )
1886        .then(|| (0..device.physical.queue_families.len() as u32).collect::<Box<_>>());
1887
1888        if let Some(queue_family_indices) = &queue_family_indices {
1889            swapchain_create_info =
1890                swapchain_create_info.queue_family_indices(queue_family_indices);
1891        }
1892
1893        let vk_khr_swapchain = Device::expect_vk_khr_swapchain(device);
1894        let mut handle = unsafe {
1895            vk_khr_swapchain
1896                .create_swapchain(&swapchain_create_info, None)
1897                .map_err(|err| {
1898                    warn!("unable to create swapchain: {err}");
1899
1900                    // TODO: Improve error handling
1901
1902                    DriverError::Unsupported
1903                })?
1904        };
1905        let images = unsafe {
1906            vk_khr_swapchain
1907                .get_swapchain_images(handle)
1908                .map_err(|err| {
1909                    SwapchainHandle::destroy_raw(device, &mut handle);
1910
1911                    match err {
1912                        vk::Result::INCOMPLETE => {
1913                            warn!("invalid swapchain image enumeration: incomplete");
1914
1915                            DriverError::InvalidData
1916                        }
1917                        vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
1918                        | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
1919                            warn!("unable to get swapchain images: {err}");
1920
1921                            DriverError::OutOfMemory
1922                        }
1923                        _ => {
1924                            error!("unable to get swapchain images: {err}");
1925
1926                            DriverError::Unsupported
1927                        }
1928                    }
1929                })?
1930        };
1931
1932        #[cfg(feature = "checked")]
1933        let swapchain_id = SwapchainId::next();
1934
1935        let vk::Extent2D { height, width } = image_extent;
1936        let vk::SwapchainCreateInfoKHR {
1937            image_format,
1938            image_array_layers,
1939            ..
1940        } = swapchain_create_info;
1941
1942        let images = {
1943            let image_info = ImageInfo::image_2d_array(
1944                width,
1945                height,
1946                image_array_layers,
1947                image_format,
1948                image_usage,
1949            );
1950
1951            images
1952                .into_iter()
1953                .enumerate()
1954                .map(|(idx, image)| {
1955                    let index = idx as u32;
1956                    let image = unsafe { Image::from_raw(device, image, image_info) };
1957
1958                    image.set_debug_name(format!("swapchain{index}"));
1959
1960                    Some(image)
1961                })
1962                .collect::<Box<_>>()
1963        };
1964
1965        let image_count = images.len() as _;
1966
1967        let info = {
1968            let SwapchainInfo {
1969                acquire_timeout,
1970                clipped,
1971                image_array_layers,
1972                present_timeout,
1973                surface,
1974                ..
1975            } = *requested_info;
1976            let vk::SwapchainCreateInfoKHR {
1977                composite_alpha,
1978                image_sharing_mode,
1979                min_image_count,
1980                pre_transform,
1981                present_mode,
1982                ..
1983            } = swapchain_create_info;
1984
1985            EffectiveSwapchainInfo {
1986                acquire_timeout,
1987                clipped,
1988                composite_alpha,
1989                height,
1990                image_array_layers,
1991                image_count,
1992                image_sharing_mode,
1993                min_image_count,
1994                pre_transform,
1995                present_mode,
1996                present_timeout,
1997                surface,
1998                width,
1999            }
2000        };
2001
2002        info!(
2003            "swapchain {}x{} {:?}x{image_count} {:?} {image_usage:#?}",
2004            info.width, info.height, info.present_mode, info.surface.format,
2005        );
2006
2007        let use_present_wait = {
2008            surface.device.physical.vk_khr_present_id.is_some()
2009                && surface.device.physical.vk_khr_present_wait.is_some()
2010        };
2011
2012        Ok((
2013            Self {
2014                acquire_timeout: info.acquire_timeout,
2015                handle: SwapchainHandle::new(device, handle),
2016                images,
2017                present_timeout: info.present_timeout,
2018                retirement: PresentRetirement::new(image_count as _, use_present_wait),
2019
2020                #[cfg(feature = "checked")]
2021                swapchain_id,
2022
2023                __: PhantomData,
2024            },
2025            info,
2026        ))
2027    }
2028
2029    fn acquire_next_image(
2030        &mut self,
2031        device: &Device,
2032        acquired: vk::Semaphore,
2033    ) -> Result<SwapchainImage, AcquireImageError> {
2034        let res = unsafe {
2035            Device::expect_vk_khr_swapchain(device)
2036                .acquire_next_image(
2037                    self.handle(),
2038                    self.acquire_timeout,
2039                    acquired,
2040                    vk::Fence::null(),
2041                )
2042                .map_err(|err| match err {
2043                    vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => {
2044                        SwapchainError::FullScreenExclusiveModeLost.into()
2045                    }
2046                    vk::Result::ERROR_OUT_OF_DATE_KHR => AcquireImageError::OutOfDate,
2047                    vk::Result::NOT_READY => SwapchainError::NotReady.into(),
2048                    vk::Result::TIMEOUT => SwapchainError::Timeout.into(),
2049                    vk::Result::ERROR_DEVICE_LOST => SwapchainError::DeviceLost.into(),
2050                    vk::Result::ERROR_SURFACE_LOST_KHR => SwapchainError::SurfaceLost.into(),
2051                    _ => AcquireImageError::Swapchain(DriverError::Unsupported.into()),
2052                })
2053        };
2054
2055        if let Err(err) = res {
2056            use {AcquireImageError::*, Level::*, SwapchainError::*};
2057
2058            let level = match err {
2059                OutOfDate | Swapchain(NotReady | Timeout) => Debug,
2060                Swapchain(DeviceLost | SurfaceLost) => Warn,
2061                _ => Error,
2062            };
2063
2064            log!(level, "unable to acquire image: {err:?}");
2065        }
2066
2067        let (index, suboptimal) = res?;
2068
2069        if suboptimal {
2070            debug!("acquired image is suboptimal");
2071        }
2072
2073        let image = self
2074            .images
2075            .get_mut(index as usize)
2076            .map(|image| image.take().expect("expected available image"))
2077            .expect("expected image");
2078
2079        let swapchain_image = SwapchainImage {
2080            read_only: ReadOnlySwapchainImage {
2081                image,
2082                index,
2083                suboptimal,
2084                swapchain_id: self.swapchain_id,
2085            },
2086        };
2087
2088        Ok(swapchain_image)
2089    }
2090
2091    fn clear_present_id(&mut self, image_idx: usize) {
2092        let present_id = &mut self.retirement.image_present_ids[image_idx];
2093
2094        debug_assert!(present_id.is_some());
2095
2096        *present_id = None;
2097    }
2098
2099    fn into_retired(self) -> SwapchainState<Retired> {
2100        SwapchainState {
2101            acquire_timeout: self.acquire_timeout,
2102            handle: self.handle,
2103            images: self.images,
2104            present_timeout: self.present_timeout,
2105            retirement: self.retirement,
2106
2107            #[cfg(feature = "checked")]
2108            swapchain_id: self.swapchain_id,
2109
2110            __: PhantomData,
2111        }
2112    }
2113
2114    fn mark_present_queued(&mut self, image: SwapchainImage, queue: QueueAddress) {
2115        let ReadOnlySwapchainImage { image, index, .. } = image.read_only;
2116
2117        self.images[index as usize] = Some(image);
2118        self.retirement.present_pending = true;
2119        self.retirement.queue = Some(queue);
2120    }
2121
2122    fn next_present_id(&mut self, image_idx: usize) -> Option<u64> {
2123        if !self.retirement.use_present_wait {
2124            return None;
2125        }
2126
2127        let next_present_id = self.retirement.next_present_id;
2128        self.retirement.next_present_id += 1;
2129
2130        let present_id = &mut self.retirement.image_present_ids[image_idx];
2131
2132        debug_assert!(present_id.is_none());
2133
2134        *present_id = Some(next_present_id);
2135
2136        Some(next_present_id)
2137    }
2138
2139    fn wait_for_present_idx(
2140        &mut self,
2141        device: &Device,
2142        image_idx: u32,
2143        timeout: u64,
2144    ) -> Result<(), SwapchainError> {
2145        let swapchain = self.handle();
2146        let Some(image_present_id) = self
2147            .retirement
2148            .image_present_ids
2149            .get_mut(image_idx as usize)
2150        else {
2151            return Ok(());
2152        };
2153        let Some(present_id) = *image_present_id else {
2154            return Ok(());
2155        };
2156
2157        unsafe {
2158            Self::wait_for_present(device, swapchain, present_id, timeout)?;
2159        }
2160
2161        *image_present_id = None;
2162
2163        if self
2164            .retirement
2165            .image_present_ids
2166            .iter()
2167            .all(Option::is_none)
2168        {
2169            self.retirement.present_pending = false;
2170        }
2171
2172        Ok(())
2173    }
2174}
2175
2176impl SwapchainState<Retired> {
2177    fn destroy_when_idle(mut self, device: &Device) -> Result<(), SwapchainError> {
2178        self.wait_until_retired(device, self.present_timeout)?;
2179        self.handle.destroy();
2180
2181        Ok(())
2182    }
2183
2184    fn wait_until_retired(&mut self, device: &Device, timeout: u64) -> Result<(), SwapchainError> {
2185        self.wait_for_all_presents(device, timeout)
2186    }
2187}
2188
2189#[cfg(test)]
2190mod test {
2191    use super::*;
2192
2193    type Info = SwapchainInfo;
2194    type Builder = SwapchainInfoBuilder;
2195    type EffectiveInfo = EffectiveSwapchainInfo;
2196
2197    #[test]
2198    pub fn swapchain_info() {
2199        let info = Info::new(20, 24, vk::SurfaceFormatKHR::default());
2200        let builder = info.into_builder().build();
2201
2202        assert_eq!(info, builder);
2203    }
2204
2205    #[test]
2206    pub fn swapchain_info_builder() {
2207        let info = Info::new(23, 64, vk::SurfaceFormatKHR::default());
2208        let builder = Builder::default()
2209            .width(23)
2210            .height(64)
2211            .surface(vk::SurfaceFormatKHR::default())
2212            .build();
2213
2214        assert_eq!(info, builder);
2215    }
2216
2217    #[test]
2218    pub fn swapchain_info_builder_defaults() {
2219        assert_eq!(
2220            Builder::default().build(),
2221            Info::new(0, 0, vk::SurfaceFormatKHR::default())
2222        );
2223    }
2224
2225    #[test]
2226    pub fn effective_swapchain_info_round_trips_to_requested_info() {
2227        let info = EffectiveInfo {
2228            acquire_timeout: 42,
2229            clipped: false,
2230            composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
2231            height: 123,
2232            image_array_layers: 2,
2233            image_count: 7,
2234            image_sharing_mode: vk::SharingMode::CONCURRENT,
2235            min_image_count: 3,
2236            pre_transform: vk::SurfaceTransformFlagsKHR::ROTATE_90,
2237            present_mode: vk::PresentModeKHR::FIFO,
2238            present_timeout: 999,
2239            surface: vk::SurfaceFormatKHR::default()
2240                .format(vk::Format::B8G8R8A8_UNORM)
2241                .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
2242            width: 456,
2243        };
2244
2245        assert_eq!(
2246            info.into_requested_info(),
2247            Info {
2248                acquire_timeout: 42,
2249                clipped: false,
2250                composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
2251                height: 123,
2252                image_array_layers: 2,
2253                image_sharing_mode: vk::SharingMode::CONCURRENT,
2254                min_image_count: 3,
2255                pre_transform: vk::SurfaceTransformFlagsKHR::ROTATE_90,
2256                present_mode: vk::PresentModeKHR::FIFO,
2257                present_timeout: 999,
2258                surface: vk::SurfaceFormatKHR::default()
2259                    .format(vk::Format::B8G8R8A8_UNORM)
2260                    .color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR),
2261                width: 456,
2262            }
2263        );
2264    }
2265}