1use {
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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
43pub struct EffectiveSwapchainInfo {
44 pub acquire_timeout: u64,
46
47 pub composite_alpha: vk::CompositeAlphaFlagsKHR,
49
50 pub clipped: bool,
52
53 pub height: u32,
55
56 pub image_array_layers: u32,
58
59 pub image_count: u32,
61
62 pub image_sharing_mode: vk::SharingMode,
64
65 min_image_count: u32,
66
67 pub pre_transform: vk::SurfaceTransformFlagsKHR,
69
70 pub present_mode: vk::PresentModeKHR,
72
73 pub present_timeout: u64,
75
76 pub surface: vk::SurfaceFormatKHR,
78
79 pub width: u32,
81}
82
83impl EffectiveSwapchainInfo {
84 pub fn builder() -> SwapchainInfoBuilder {
86 Default::default()
87 }
88
89 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 pub fn into_requested_info(self) -> SwapchainInfo {
126 SwapchainInfo::from(self)
127 }
128}
129
130#[derive(Debug)]
131struct Live;
132
133#[derive(Debug)]
135pub struct PresentInfo<'a> {
136 pub image: SwapchainImage,
138
139 pub swapchain: &'a mut Swapchain,
141
142 pub wait_semaphores: &'a [vk::Semaphore],
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
150pub enum PresentResult {
151 QueuedOptimal,
153
154 QueuedSuboptimal,
156
157 NotQueued(PresentError),
159}
160
161impl PresentResult {
162 pub fn queued(self) -> bool {
164 matches!(self, Self::QueuedOptimal | Self::QueuedSuboptimal)
165 }
166
167 pub fn suboptimal(self) -> bool {
169 matches!(self, Self::QueuedSuboptimal)
170 }
171}
172
173#[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 pub fn as_slice(&self) -> &[PresentResult] {
186 &self.results
187 }
188
189 pub fn is_empty(&self) -> bool {
191 self.results.is_empty()
192 }
193
194 pub fn iter(&self) -> slice::Iter<'_, PresentResult> {
196 self.results.iter()
197 }
198
199 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#[derive(Clone, Debug)]
249pub struct PresentBatchError {
250 pub results: Box<[Option<PresentResult>]>,
255
256 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#[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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
328pub enum PresentError {
329 DeviceLost,
331
332 FullScreenExclusiveModeLost,
334
335 OutOfDate,
337
338 SurfaceLost,
340}
341
342#[derive(Clone, Copy, Debug)]
344pub enum PresentFailure {
345 DeviceLost,
347
348 Driver(DriverError),
350
351 FullScreenExclusiveModeLost,
353
354 OutOfDate,
356
357 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 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 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#[derive(Debug)]
694#[read_only::cast]
695pub struct Swapchain {
696 pub handle: vk::SwapchainKHR,
700
701 pub info: EffectiveSwapchainInfo,
709
710 live: SwapchainState<Live>,
711 queue_signals: QueueSignals,
712
713 pub recreate_pending: bool,
715
716 requested_info: SwapchainInfo,
717 retired: Vec<SwapchainState<Retired>>,
718
719 pub surface: Surface,
723
724 #[cfg(feature = "checked")]
725 swapchain_id: SwapchainId,
726}
727
728impl Swapchain {
729 #[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 #[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 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 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 #[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 #[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 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 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 warn!("unable to wait for live swapchain presents: {err:?}");
1250 }
1251
1252 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#[derive(Clone, Copy, Debug)]
1269pub enum SwapchainError {
1270 DeviceLost,
1272
1273 Driver(DriverError),
1275
1276 FullScreenExclusiveModeLost,
1278
1279 NotReady,
1281
1282 SurfaceLost,
1284
1285 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#[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#[derive(Debug)]
1393#[read_only::embed]
1394pub struct SwapchainImage {
1395 #[readonly]
1396 pub(self) image: Image,
1397
1398 #[readonly]
1402 pub index: u32,
1403
1404 #[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 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#[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 #[builder(default = "u64::MAX")]
1476 pub acquire_timeout: u64,
1477
1478 #[builder(default = vk::CompositeAlphaFlagsKHR::OPAQUE)]
1480 pub composite_alpha: vk::CompositeAlphaFlagsKHR,
1481
1482 #[builder(default = "true")]
1484 pub clipped: bool,
1485
1486 #[builder(default)]
1488 pub height: u32,
1489
1490 #[builder(default = "1")]
1492 pub image_array_layers: u32,
1493
1494 #[builder(default = vk::SharingMode::EXCLUSIVE)]
1496 pub image_sharing_mode: vk::SharingMode,
1497
1498 #[builder(default = "2")]
1500 pub min_image_count: u32,
1501
1502 #[builder(default = vk::SurfaceTransformFlagsKHR::IDENTITY)]
1504 pub pre_transform: vk::SurfaceTransformFlagsKHR,
1505
1506 #[builder(default = vk::PresentModeKHR::IMMEDIATE)]
1508 pub present_mode: vk::PresentModeKHR,
1509
1510 #[builder(default = "u64::MAX")]
1512 pub present_timeout: u64,
1513
1514 #[builder(default)]
1516 pub surface: vk::SurfaceFormatKHR,
1517
1518 #[builder(default)]
1520 pub width: u32,
1521}
1522
1523impl SwapchainInfo {
1524 #[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 pub fn builder() -> SwapchainInfoBuilder {
1545 Default::default()
1546 }
1547
1548 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 #[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 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 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}