1pub use self::{acquire_present::*, surface::*};
323
324mod acquire_present;
325mod surface;
326
327use crate::{
328 device::{Device, DeviceOwned},
329 format::Format,
330 image::{Image, ImageCreateFlags, ImageFormatInfo, ImageTiling, ImageType, ImageUsage},
331 instance::InstanceOwnedDebugWrapper,
332 macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum},
333 sync::Sharing,
334 Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
335 VulkanObject,
336};
337use parking_lot::Mutex;
338use smallvec::SmallVec;
339use std::{
340 fmt::Debug,
341 mem::MaybeUninit,
342 num::NonZeroU64,
343 ptr,
344 sync::{
345 atomic::{AtomicBool, AtomicU64, Ordering},
346 Arc,
347 },
348 time::Duration,
349};
350
351#[derive(Debug)]
353pub struct Swapchain {
354 handle: ash::vk::SwapchainKHR,
355 device: InstanceOwnedDebugWrapper<Arc<Device>>,
356 surface: InstanceOwnedDebugWrapper<Arc<Surface>>,
357 id: NonZeroU64,
358
359 flags: SwapchainCreateFlags,
360 min_image_count: u32,
361 image_format: Format,
362 image_view_formats: Vec<Format>,
363 image_color_space: ColorSpace,
364 image_extent: [u32; 2],
365 image_array_layers: u32,
366 image_usage: ImageUsage,
367 image_sharing: Sharing<SmallVec<[u32; 4]>>,
368 pre_transform: SurfaceTransform,
369 composite_alpha: CompositeAlpha,
370 present_mode: PresentMode,
371 present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>,
372 clipped: bool,
373 scaling_behavior: Option<PresentScaling>,
374 present_gravity: Option<[PresentGravity; 2]>,
375 full_screen_exclusive: FullScreenExclusive,
376 win32_monitor: Option<Win32Monitor>,
377
378 prev_present_id: AtomicU64,
379
380 full_screen_exclusive_held: AtomicBool,
382
383 images: Vec<ImageEntry>,
385
386 is_retired: Mutex<bool>,
393}
394
395#[derive(Debug)]
396struct ImageEntry {
397 handle: ash::vk::Image,
398 layout_initialized: AtomicBool,
399}
400
401impl Swapchain {
402 #[inline]
413 pub fn new(
414 device: Arc<Device>,
415 surface: Arc<Surface>,
416 create_info: SwapchainCreateInfo,
417 ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Validated<VulkanError>> {
418 Self::validate_new_inner(&device, &surface, &create_info)?;
419
420 Ok(unsafe { Self::new_unchecked(device, surface, create_info) }?)
421 }
422
423 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
424 #[inline]
425 pub unsafe fn new_unchecked(
426 device: Arc<Device>,
427 surface: Arc<Surface>,
428 create_info: SwapchainCreateInfo,
429 ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
430 let (handle, image_handles) =
431 unsafe { Self::new_inner_unchecked(&device, &surface, &create_info, None) }?;
432
433 unsafe { Self::from_handle(device, handle, image_handles, surface, create_info) }
434 }
435
436 pub fn recreate(
444 self: &Arc<Self>,
445 create_info: SwapchainCreateInfo,
446 ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Validated<VulkanError>> {
447 Self::validate_new_inner(&self.device, &self.surface, &create_info)?;
448
449 {
450 let mut is_retired = self.is_retired.lock();
451
452 if *is_retired {
454 return Err(Box::new(ValidationError {
455 context: "self".into(),
456 problem: "has already been used to recreate a swapchain".into(),
457 vuids: &["VUID-VkSwapchainCreateInfoKHR-oldSwapchain-01933"],
458 ..Default::default()
459 })
460 .into());
461 } else {
462 *is_retired = true;
470 }
471 }
472
473 Ok(unsafe { self.recreate_unchecked(create_info) }?)
474 }
475
476 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
477 pub unsafe fn recreate_unchecked(
478 self: &Arc<Self>,
479 create_info: SwapchainCreateInfo,
480 ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
481 *self.is_retired.lock() = true;
490
491 let (handle, image_handles) = unsafe {
492 Self::new_inner_unchecked(&self.device, &self.surface, &create_info, Some(self))
493 }?;
494
495 let (swapchain, swapchain_images) = unsafe {
496 Self::from_handle(
497 self.device.clone(),
498 handle,
499 image_handles,
500 self.surface.clone(),
501 create_info,
502 )
503 }?;
504
505 if self.full_screen_exclusive == FullScreenExclusive::ApplicationControlled {
506 swapchain.full_screen_exclusive_held.store(
507 self.full_screen_exclusive_held.load(Ordering::Relaxed),
508 Ordering::Relaxed,
509 );
510 };
511
512 Ok((swapchain, swapchain_images))
513 }
514
515 fn validate_new_inner(
516 device: &Device,
517 surface: &Surface,
518 create_info: &SwapchainCreateInfo,
519 ) -> Result<(), Box<ValidationError>> {
520 if !device.enabled_extensions().khr_swapchain {
521 return Err(Box::new(ValidationError {
522 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
523 "khr_swapchain",
524 )])]),
525 ..Default::default()
526 }));
527 }
528
529 create_info
530 .validate(device)
531 .map_err(|err| err.add_context("create_info"))?;
532
533 let &SwapchainCreateInfo {
534 flags: _,
535 min_image_count,
536 image_format,
537 image_view_formats: _,
538 image_color_space,
539 image_extent,
540 image_array_layers,
541 image_usage,
542 image_sharing: _,
543 pre_transform,
544 composite_alpha,
545 present_mode,
546 ref present_modes,
547 clipped: _,
548 scaling_behavior,
549 present_gravity,
550 full_screen_exclusive,
551 win32_monitor,
552 _ne: _,
553 } = create_info;
554
555 assert_eq!(device.instance(), surface.instance());
556
557 if !device.active_queue_family_indices().iter().any(|&index| {
559 unsafe {
560 device
561 .physical_device()
562 .surface_support_unchecked(index, surface)
563 }
564 .unwrap_or_default()
565 }) {
566 return Err(Box::new(ValidationError {
567 context: "surface".into(),
568 problem: "is not supported by the physical device".into(),
569 vuids: &["VUID-VkSwapchainCreateInfoKHR-surface-01270"],
570 ..Default::default()
571 }));
572 }
573
574 let surface_capabilities = unsafe {
575 device.physical_device().surface_capabilities_unchecked(
576 surface,
577 SurfaceInfo {
578 present_mode: device
579 .enabled_extensions()
580 .ext_swapchain_maintenance1
581 .then_some(present_mode),
582 full_screen_exclusive,
583 win32_monitor: win32_monitor
584 .filter(|_| full_screen_exclusive != FullScreenExclusive::Default),
585 ..Default::default()
586 },
587 )
588 }
589 .map_err(|_err| {
590 Box::new(ValidationError {
591 problem: "`PhysicalDevice::surface_capabilities` \
592 returned an error"
593 .into(),
594 ..Default::default()
595 })
596 })?;
597 let surface_formats = unsafe {
598 device.physical_device().surface_formats_unchecked(
599 surface,
600 SurfaceInfo {
601 present_mode: device
602 .enabled_extensions()
603 .ext_swapchain_maintenance1
604 .then_some(present_mode),
605 full_screen_exclusive,
606 win32_monitor: win32_monitor.filter(|_| {
607 surface.api() == SurfaceApi::Win32
608 && full_screen_exclusive == FullScreenExclusive::ApplicationControlled
609 }),
610 ..Default::default()
611 },
612 )
613 }
614 .map_err(|_err| {
615 Box::new(ValidationError {
616 problem: "`PhysicalDevice::surface_formats` \
617 returned an error"
618 .into(),
619 ..Default::default()
620 })
621 })?;
622 let surface_present_modes = unsafe {
623 device.physical_device().surface_present_modes_unchecked(
624 surface,
625 SurfaceInfo {
626 full_screen_exclusive,
627 win32_monitor: win32_monitor.filter(|_| {
628 surface.api() == SurfaceApi::Win32
629 && full_screen_exclusive == FullScreenExclusive::ApplicationControlled
630 }),
631 ..Default::default()
632 },
633 )
634 }
635 .map_err(|_err| {
636 Box::new(ValidationError {
637 problem: "`PhysicalDevice::surface_present_modes` \
638 returned an error"
639 .into(),
640 ..Default::default()
641 })
642 })?;
643
644 if surface_capabilities
645 .max_image_count
646 .is_some_and(|c| min_image_count > c)
647 {
648 return Err(Box::new(ValidationError {
649 problem: "`create_info.min_image_count` is greater than the `max_image_count` \
650 value of the capabilities of `surface`"
651 .into(),
652 vuids: &["VUID-VkSwapchainCreateInfoKHR-minImageCount-01272"],
653 ..Default::default()
654 }));
655 }
656
657 if min_image_count < surface_capabilities.min_image_count {
658 return Err(Box::new(ValidationError {
659 problem: "`create_info.min_image_count` is less than the `min_image_count` \
660 value of the capabilities of `surface`"
661 .into(),
662 vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-02839"],
663 ..Default::default()
664 }));
665 }
666
667 if !surface_formats
668 .iter()
669 .any(|&fc| fc == (image_format, image_color_space))
670 {
671 return Err(Box::new(ValidationError {
672 problem: "the combination of `create_info.image_format` and \
673 `create_info.image_color_space` is not supported for `surface`"
674 .into(),
675 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01273"],
676 ..Default::default()
677 }));
678 }
679
680 if image_array_layers > surface_capabilities.max_image_array_layers {
681 return Err(Box::new(ValidationError {
682 problem: "`create_info.image_array_layers` is greater than the \
683 `max_image_array_layers` value of the capabilities of `surface`"
684 .into(),
685 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"],
686 ..Default::default()
687 }));
688 }
689
690 if matches!(
691 present_mode,
692 PresentMode::Immediate
693 | PresentMode::Mailbox
694 | PresentMode::Fifo
695 | PresentMode::FifoRelaxed
696 ) && !surface_capabilities
697 .supported_usage_flags
698 .contains(image_usage)
699 {
700 return Err(Box::new(ValidationError {
701 problem: "`create_info.present_mode` is `PresentMode::Immediate`, \
702 `PresentMode::Mailbox`, `PresentMode::Fifo` or `PresentMode::FifoRelaxed`, \
703 and `create_info.image_usage` contains flags that are not set in \
704 the `supported_usage_flags` value of the capabilities of `surface`"
705 .into(),
706 vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01427"],
707 ..Default::default()
708 }));
709 }
710
711 if !surface_capabilities
712 .supported_transforms
713 .contains_enum(pre_transform)
714 {
715 return Err(Box::new(ValidationError {
716 problem: "`create_info.pre_transform` is not present in the \
717 `supported_transforms` value of the capabilities of `surface`"
718 .into(),
719 vuids: &["VUID-VkSwapchainCreateInfoKHR-preTransform-01279"],
720 ..Default::default()
721 }));
722 }
723
724 if !surface_capabilities
725 .supported_composite_alpha
726 .contains_enum(composite_alpha)
727 {
728 return Err(Box::new(ValidationError {
729 problem: "`create_info.composite_alpha` is not present in the \
730 `supported_composite_alpha` value of the capabilities of `surface`"
731 .into(),
732 vuids: &["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280"],
733 ..Default::default()
734 }));
735 }
736
737 if !surface_present_modes.contains(&present_mode) {
738 return Err(Box::new(ValidationError {
739 problem: "`create_info.present_mode` is not supported for `surface`".into(),
740 vuids: &["VUID-VkSwapchainCreateInfoKHR-presentMode-01281"],
741 ..Default::default()
742 }));
743 }
744
745 if present_modes.is_empty() {
746 if let Some(scaling_behavior) = scaling_behavior {
747 if !surface_capabilities
748 .supported_present_scaling
749 .contains_enum(scaling_behavior)
750 {
751 return Err(Box::new(ValidationError {
752 problem: "`create_info.scaling_behavior` is not present in the \
753 `supported_present_scaling` value of the \
754 capabilities of `surface`"
755 .into(),
756 vuids: &[
757 "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07770",
758 ],
759 ..Default::default()
760 }));
761 }
762 }
763
764 if let Some(present_gravity) = present_gravity {
765 for (axis_index, (present_gravity, supported_present_gravity)) in present_gravity
766 .into_iter()
767 .zip(surface_capabilities.supported_present_gravity)
768 .enumerate()
769 {
770 if !supported_present_gravity.contains_enum(present_gravity) {
771 return Err(Box::new(ValidationError {
772 problem: format!(
773 "`create_info.present_gravity[{0}]` is not present in the \
774 `supported_present_gravity[{0}]` value of the \
775 capabilities of `surface`",
776 axis_index,
777 )
778 .into(),
779 vuids: &[
780 "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07772",
781 "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07774",
782 ],
783 ..Default::default()
784 }));
785 }
786 }
787 }
788 } else {
789 for (index, &present_mode) in present_modes.iter().enumerate() {
790 if !surface_present_modes.contains(&present_mode) {
791 return Err(Box::new(ValidationError {
792 problem: format!(
793 "`create_info.present_modes[{}]` is not supported for `surface`",
794 index,
795 )
796 .into(),
797 vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-None-07762"],
798 ..Default::default()
799 }));
800 }
801
802 if !surface_capabilities
803 .compatible_present_modes
804 .contains(&present_mode)
805 {
806 return Err(Box::new(ValidationError {
807 problem: format!(
808 "`create_info.present_modes[{}]` is not present in the \
809 `compatible_present_modes` value of the \
810 capabilities of `surface`",
811 index,
812 )
813 .into(),
814 vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-07763"],
815 ..Default::default()
816 }));
817 }
818
819 if scaling_behavior.is_some() || present_gravity.is_some() {
820 let surface_capabilities = unsafe {
821 device.physical_device().surface_capabilities_unchecked(
822 surface,
823 SurfaceInfo {
824 present_mode: Some(present_mode),
825 full_screen_exclusive,
826 win32_monitor,
827 ..Default::default()
828 },
829 )
830 }
831 .map_err(|_err| {
832 Box::new(ValidationError {
833 problem: "`PhysicalDevice::surface_capabilities` \
834 returned an error"
835 .into(),
836 ..Default::default()
837 })
838 })?;
839
840 if let Some(scaling_behavior) = scaling_behavior {
841 if !surface_capabilities
842 .supported_present_scaling
843 .contains_enum(scaling_behavior)
844 {
845 return Err(Box::new(ValidationError {
846 problem: format!(
847 "`create_info.scaling_behavior` is not present in the \
848 `supported_present_scaling` value of the \
849 capabilities of `surface` for \
850 `create_info.present_modes[{}]`",
851 index,
852 )
853 .into(),
854 vuids: &[
855 "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-07771",
856 ],
857 ..Default::default()
858 }));
859 }
860 }
861
862 if let Some(present_gravity) = present_gravity {
863 for (axis_index, (present_gravity, supported_present_gravity)) in
864 present_gravity
865 .into_iter()
866 .zip(surface_capabilities.supported_present_gravity)
867 .enumerate()
868 {
869 if !supported_present_gravity.contains_enum(present_gravity) {
870 return Err(Box::new(ValidationError {
871 problem: format!(
872 "`create_info.present_gravity[{0}]` is not present in the \
873 `supported_present_gravity[{0}]` value of the \
874 capabilities of `surface` for \
875 `create_info.present_modes[{1}]`",
876 axis_index, index,
877 )
878 .into(),
879 vuids: &[
880 "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-07773",
881 "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-07775",
882 ],
883 ..Default::default()
884 }));
885 }
886 }
887 }
888 }
889 }
890 }
891
892 if scaling_behavior.is_some() {
893 if let Some(min_scaled_image_extent) = surface_capabilities.min_scaled_image_extent {
894 if image_extent[0] < min_scaled_image_extent[0]
895 || image_extent[1] < min_scaled_image_extent[1]
896 {
897 return Err(Box::new(ValidationError {
898 problem: "`scaling_behavior` is `Some`, and an element of \
899 `create_info.image_extent` is less than the corresponding element \
900 of the `min_scaled_image_extent` value of the \
901 capabilities of `surface`"
902 .into(),
903 vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"],
904 ..Default::default()
905 }));
906 }
907 }
908
909 if let Some(max_scaled_image_extent) = surface_capabilities.max_scaled_image_extent {
910 if image_extent[0] > max_scaled_image_extent[0]
911 || image_extent[1] > max_scaled_image_extent[1]
912 {
913 return Err(Box::new(ValidationError {
914 problem: "`scaling_behavior` is `Some`, and an element of \
915 `create_info.image_extent` is greater than the corresponding element \
916 of the `max_scaled_image_extent` value of the \
917 capabilities of `surface`"
918 .into(),
919 vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-07782"],
920 ..Default::default()
921 }));
922 }
923 }
924 } else {
925 }
962
963 Ok(())
964 }
965
966 unsafe fn new_inner_unchecked(
967 device: &Device,
968 surface: &Surface,
969 create_info: &SwapchainCreateInfo,
970 old_swapchain: Option<&Swapchain>,
971 ) -> Result<(ash::vk::SwapchainKHR, Vec<ash::vk::Image>), VulkanError> {
972 let create_info_fields1_vk = create_info.to_vk_fields1();
973 let mut create_info_extensions_vk = create_info.to_vk_extensions(&create_info_fields1_vk);
974 let create_info_vk = create_info.to_vk(
975 surface.handle(),
976 old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle),
977 &mut create_info_extensions_vk,
978 );
979
980 let fns = device.fns();
981
982 let handle = {
983 let mut output = MaybeUninit::uninit();
984 unsafe {
985 (fns.khr_swapchain.create_swapchain_khr)(
986 device.handle(),
987 &create_info_vk,
988 ptr::null(),
989 output.as_mut_ptr(),
990 )
991 }
992 .result()
993 .map_err(VulkanError::from)?;
994 unsafe { output.assume_init() }
995 };
996
997 let image_handles = loop {
998 let mut count = 0;
999 unsafe {
1000 (fns.khr_swapchain.get_swapchain_images_khr)(
1001 device.handle(),
1002 handle,
1003 &mut count,
1004 ptr::null_mut(),
1005 )
1006 }
1007 .result()
1008 .map_err(VulkanError::from)?;
1009
1010 let mut images = Vec::with_capacity(count as usize);
1011 let result = unsafe {
1012 (fns.khr_swapchain.get_swapchain_images_khr)(
1013 device.handle(),
1014 handle,
1015 &mut count,
1016 images.as_mut_ptr(),
1017 )
1018 };
1019
1020 match result {
1021 ash::vk::Result::SUCCESS => {
1022 unsafe { images.set_len(count as usize) };
1023 break images;
1024 }
1025 ash::vk::Result::INCOMPLETE => (),
1026 err => return Err(VulkanError::from(err)),
1027 }
1028 };
1029
1030 Ok((handle, image_handles))
1031 }
1032
1033 pub unsafe fn from_handle(
1043 device: Arc<Device>,
1044 handle: ash::vk::SwapchainKHR,
1045 image_handles: impl IntoIterator<Item = ash::vk::Image>,
1046 surface: Arc<Surface>,
1047 create_info: SwapchainCreateInfo,
1048 ) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), VulkanError> {
1049 let SwapchainCreateInfo {
1050 flags,
1051 min_image_count,
1052 image_format,
1053 image_view_formats,
1054 image_color_space,
1055 image_extent,
1056 image_array_layers,
1057 image_usage,
1058 image_sharing,
1059 pre_transform,
1060 composite_alpha,
1061 present_mode,
1062 present_modes,
1063 clipped,
1064 scaling_behavior,
1065 present_gravity,
1066 full_screen_exclusive,
1067 win32_monitor,
1068 _ne: _,
1069 } = create_info;
1070
1071 let swapchain = Arc::new(Swapchain {
1072 handle,
1073 device: InstanceOwnedDebugWrapper(device),
1074 surface: InstanceOwnedDebugWrapper(surface),
1075 id: Self::next_id(),
1076
1077 flags,
1078 min_image_count,
1079 image_format,
1080 image_view_formats,
1081 image_color_space,
1082 image_extent,
1083 image_array_layers,
1084 image_usage,
1085 image_sharing,
1086 pre_transform,
1087 composite_alpha,
1088 present_mode,
1089 present_modes,
1090 clipped,
1091 scaling_behavior,
1092 present_gravity,
1093 full_screen_exclusive,
1094 win32_monitor,
1095
1096 prev_present_id: Default::default(),
1097 full_screen_exclusive_held: AtomicBool::new(false),
1098 images: image_handles
1099 .into_iter()
1100 .map(|handle| ImageEntry {
1101 handle,
1102 layout_initialized: AtomicBool::new(false),
1103 })
1104 .collect(),
1105 is_retired: Mutex::new(false),
1106 });
1107
1108 let swapchain_images = swapchain
1109 .images
1110 .iter()
1111 .enumerate()
1112 .map(|(image_index, entry)| {
1113 Ok(Arc::new(unsafe {
1114 Image::from_swapchain(entry.handle, swapchain.clone(), image_index as u32)
1115 }?))
1116 })
1117 .collect::<Result<_, VulkanError>>()?;
1118
1119 Ok((swapchain, swapchain_images))
1120 }
1121
1122 #[inline]
1124 pub fn create_info(&self) -> SwapchainCreateInfo {
1125 SwapchainCreateInfo {
1126 flags: self.flags,
1127 min_image_count: self.min_image_count,
1128 image_format: self.image_format,
1129 image_view_formats: self.image_view_formats.clone(),
1130 image_color_space: self.image_color_space,
1131 image_extent: self.image_extent,
1132 image_array_layers: self.image_array_layers,
1133 image_usage: self.image_usage,
1134 image_sharing: self.image_sharing.clone(),
1135 pre_transform: self.pre_transform,
1136 composite_alpha: self.composite_alpha,
1137 present_mode: self.present_mode,
1138 present_modes: self.present_modes.clone(),
1139 clipped: self.clipped,
1140 scaling_behavior: self.scaling_behavior,
1141 present_gravity: self.present_gravity,
1142 full_screen_exclusive: self.full_screen_exclusive,
1143 win32_monitor: self.win32_monitor,
1144 _ne: crate::NonExhaustive(()),
1145 }
1146 }
1147
1148 #[inline]
1150 pub fn surface(&self) -> &Arc<Surface> {
1151 &self.surface
1152 }
1153
1154 #[inline]
1156 pub fn flags(&self) -> SwapchainCreateFlags {
1157 self.flags
1158 }
1159
1160 #[inline]
1162 pub fn index_of_image(&self, image: &Image) -> Option<u32> {
1163 self.images
1164 .iter()
1165 .position(|entry| entry.handle == image.handle())
1166 .map(|i| i as u32)
1167 }
1168
1169 #[inline]
1171 pub fn image_count(&self) -> u32 {
1172 self.images.len() as u32
1173 }
1174
1175 #[inline]
1177 pub fn image_format(&self) -> Format {
1178 self.image_format
1179 }
1180
1181 #[inline]
1183 pub fn image_view_formats(&self) -> &[Format] {
1184 &self.image_view_formats
1185 }
1186
1187 #[inline]
1189 pub fn image_color_space(&self) -> ColorSpace {
1190 self.image_color_space
1191 }
1192
1193 #[inline]
1195 pub fn image_extent(&self) -> [u32; 2] {
1196 self.image_extent
1197 }
1198
1199 #[inline]
1201 pub fn image_array_layers(&self) -> u32 {
1202 self.image_array_layers
1203 }
1204
1205 #[inline]
1207 pub fn image_usage(&self) -> ImageUsage {
1208 self.image_usage
1209 }
1210
1211 #[inline]
1213 pub fn image_sharing(&self) -> &Sharing<SmallVec<[u32; 4]>> {
1214 &self.image_sharing
1215 }
1216
1217 #[inline]
1219 pub fn pre_transform(&self) -> SurfaceTransform {
1220 self.pre_transform
1221 }
1222
1223 #[inline]
1225 pub fn composite_alpha(&self) -> CompositeAlpha {
1226 self.composite_alpha
1227 }
1228
1229 #[inline]
1231 pub fn present_mode(&self) -> PresentMode {
1232 self.present_mode
1233 }
1234
1235 #[inline]
1237 pub fn present_modes(&self) -> &[PresentMode] {
1238 &self.present_modes
1239 }
1240
1241 #[inline]
1243 pub fn clipped(&self) -> bool {
1244 self.clipped
1245 }
1246
1247 #[inline]
1249 pub fn scaling_behavior(&self) -> Option<PresentScaling> {
1250 self.scaling_behavior
1251 }
1252
1253 #[inline]
1255 pub fn present_gravity(&self) -> Option<[PresentGravity; 2]> {
1256 self.present_gravity
1257 }
1258
1259 #[inline]
1261 pub fn full_screen_exclusive(&self) -> FullScreenExclusive {
1262 self.full_screen_exclusive
1263 }
1264
1265 #[inline]
1293 pub unsafe fn acquire_next_image(
1294 &self,
1295 acquire_info: &AcquireNextImageInfo,
1296 ) -> Result<AcquiredImage, Validated<VulkanError>> {
1297 let is_retired_lock = self.is_retired.lock();
1298 self.validate_acquire_next_image(acquire_info, *is_retired_lock)?;
1299
1300 Ok(unsafe { self.acquire_next_image_unchecked(acquire_info) }?)
1301 }
1302
1303 fn validate_acquire_next_image(
1304 &self,
1305 acquire_info: &AcquireNextImageInfo,
1306 is_retired: bool,
1307 ) -> Result<(), Box<ValidationError>> {
1308 acquire_info
1309 .validate(&self.device)
1310 .map_err(|err| err.add_context("acquire_info"))?;
1311
1312 if is_retired {
1313 return Err(Box::new(ValidationError {
1314 problem: "this swapchain is retired".into(),
1315 vuids: &["VUID-VkAcquireNextImageInfoKHR-swapchain-01675"],
1316 ..Default::default()
1317 }));
1318 }
1319
1320 Ok(())
1327 }
1328
1329 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1330 pub unsafe fn acquire_next_image_unchecked(
1331 &self,
1332 acquire_info: &AcquireNextImageInfo,
1333 ) -> Result<AcquiredImage, VulkanError> {
1334 let acquire_info_vk = acquire_info.to_vk(self.handle(), self.device.device_mask());
1335
1336 let (image_index, is_suboptimal) = {
1337 let fns = self.device.fns();
1338 let mut output = MaybeUninit::uninit();
1339
1340 let result = if self.device.api_version() >= Version::V1_1
1341 || self.device.enabled_extensions().khr_device_group
1342 {
1343 unsafe {
1344 (fns.khr_swapchain.acquire_next_image2_khr)(
1345 self.device.handle(),
1346 &acquire_info_vk,
1347 output.as_mut_ptr(),
1348 )
1349 }
1350 } else {
1351 debug_assert!(acquire_info_vk.p_next.is_null());
1352 unsafe {
1353 (fns.khr_swapchain.acquire_next_image_khr)(
1354 self.device.handle(),
1355 acquire_info_vk.swapchain,
1356 acquire_info_vk.timeout,
1357 acquire_info_vk.semaphore,
1358 acquire_info_vk.fence,
1359 output.as_mut_ptr(),
1360 )
1361 }
1362 };
1363
1364 match result {
1365 ash::vk::Result::SUCCESS => (unsafe { output.assume_init() }, false),
1366 ash::vk::Result::SUBOPTIMAL_KHR => (unsafe { output.assume_init() }, true),
1367 ash::vk::Result::NOT_READY => return Err(VulkanError::NotReady),
1368 ash::vk::Result::TIMEOUT => return Err(VulkanError::Timeout),
1369 err => {
1370 let err = VulkanError::from(err);
1371
1372 if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
1373 self.full_screen_exclusive_held
1374 .store(false, Ordering::SeqCst);
1375 }
1376
1377 return Err(err);
1378 }
1379 }
1380 };
1381
1382 Ok(AcquiredImage {
1383 image_index,
1384 is_suboptimal,
1385 })
1386 }
1387
1388 #[inline]
1397 pub fn wait_for_present(
1398 &self,
1399 present_id: NonZeroU64,
1400 timeout: Option<Duration>,
1401 ) -> Result<bool, Validated<VulkanError>> {
1402 let is_retired_lock = self.is_retired.lock();
1403 self.validate_wait_for_present(present_id, timeout, *is_retired_lock)?;
1404
1405 Ok(unsafe { self.wait_for_present_unchecked(present_id, timeout) }?)
1406 }
1407
1408 fn validate_wait_for_present(
1409 &self,
1410 _present_id: NonZeroU64,
1411 timeout: Option<Duration>,
1412 is_retired: bool,
1413 ) -> Result<(), Box<ValidationError>> {
1414 if !self.device.enabled_features().present_wait {
1415 return Err(Box::new(ValidationError {
1416 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
1417 "present_wait",
1418 )])]),
1419 vuids: &["VUID-vkWaitForPresentKHR-presentWait-06234"],
1420 ..Default::default()
1421 }));
1422 }
1423
1424 if is_retired {
1425 return Err(Box::new(ValidationError {
1426 problem: "this swapchain is retired".into(),
1427 vuids: &["VUID-vkWaitForPresentKHR-swapchain-04997"],
1428 ..Default::default()
1429 }));
1430 }
1431
1432 if let Some(timeout) = timeout {
1433 if timeout.as_nanos() >= u64::MAX as u128 {
1434 return Err(Box::new(ValidationError {
1435 context: "timeout".into(),
1436 problem: "is not less than `u64::MAX` nanoseconds".into(),
1437 ..Default::default()
1438 }));
1439 }
1440 }
1441
1442 Ok(())
1443 }
1444
1445 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1446 pub unsafe fn wait_for_present_unchecked(
1447 &self,
1448 present_id: NonZeroU64,
1449 timeout: Option<Duration>,
1450 ) -> Result<bool, VulkanError> {
1451 let result = {
1452 let fns = self.device.fns();
1453 unsafe {
1454 (fns.khr_present_wait.wait_for_present_khr)(
1455 self.device.handle(),
1456 self.handle,
1457 present_id.get(),
1458 timeout.map_or(u64::MAX, |duration| {
1459 u64::try_from(duration.as_nanos()).unwrap()
1460 }),
1461 )
1462 }
1463 };
1464
1465 match result {
1466 ash::vk::Result::SUCCESS => Ok(false),
1467 ash::vk::Result::SUBOPTIMAL_KHR => Ok(true),
1468 ash::vk::Result::TIMEOUT => Err(VulkanError::Timeout),
1469 err => {
1470 let err = VulkanError::from(err);
1471
1472 if matches!(err, VulkanError::FullScreenExclusiveModeLost) {
1473 self.full_screen_exclusive_held
1474 .store(false, Ordering::SeqCst);
1475 }
1476
1477 Err(err)
1478 }
1479 }
1480 }
1481
1482 #[inline]
1489 pub fn acquire_full_screen_exclusive_mode(&self) -> Result<(), Validated<VulkanError>> {
1490 self.validate_acquire_full_screen_exclusive_mode()?;
1491
1492 Ok(unsafe { self.acquire_full_screen_exclusive_mode_unchecked() }?)
1493 }
1494
1495 fn validate_acquire_full_screen_exclusive_mode(&self) -> Result<(), Box<ValidationError>> {
1496 if *self.is_retired.lock() {
1497 return Err(Box::new(ValidationError {
1498 problem: "this swapchain is retired".into(),
1499 vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02674"],
1500 ..Default::default()
1501 }));
1502 }
1503
1504 if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
1505 return Err(Box::new(ValidationError {
1506 context: "self.full_screen_exclusive()".into(),
1507 problem: "is not `FullScreenExclusive::ApplicationControlled`".into(),
1508 vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02675"],
1509 ..Default::default()
1510 }));
1511 }
1512
1513 if self
1515 .full_screen_exclusive_held
1516 .swap(true, Ordering::Acquire)
1517 {
1518 return Err(Box::new(ValidationError {
1519 problem: "this swapchain already holds full-screen exclusive access".into(),
1520 vuids: &["VUID-vkAcquireFullScreenExclusiveModeEXT-swapchain-02676"],
1521 ..Default::default()
1522 }));
1523 }
1524
1525 Ok(())
1526 }
1527
1528 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1529 pub unsafe fn acquire_full_screen_exclusive_mode_unchecked(&self) -> Result<(), VulkanError> {
1530 self.full_screen_exclusive_held
1531 .store(true, Ordering::Relaxed);
1532
1533 let fns = self.device.fns();
1534 unsafe {
1535 (fns.ext_full_screen_exclusive
1536 .acquire_full_screen_exclusive_mode_ext)(
1537 self.device.handle(), self.handle
1538 )
1539 }
1540 .result()
1541 .map_err(VulkanError::from)?;
1542
1543 Ok(())
1544 }
1545
1546 #[inline]
1551 pub fn release_full_screen_exclusive_mode(&self) -> Result<(), Validated<VulkanError>> {
1552 self.validate_release_full_screen_exclusive_mode()?;
1553
1554 Ok(unsafe { self.release_full_screen_exclusive_mode_unchecked() }?)
1555 }
1556
1557 fn validate_release_full_screen_exclusive_mode(&self) -> Result<(), Box<ValidationError>> {
1558 if *self.is_retired.lock() {
1559 return Err(Box::new(ValidationError {
1560 problem: "this swapchain is retired".into(),
1561 vuids: &["VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02677"],
1562 ..Default::default()
1563 }));
1564 }
1565
1566 if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
1567 return Err(Box::new(ValidationError {
1568 context: "self.full_screen_exclusive()".into(),
1569 problem: "is not `FullScreenExclusive::ApplicationControlled`".into(),
1570 vuids: &["VUID-vkReleaseFullScreenExclusiveModeEXT-swapchain-02678"],
1571 ..Default::default()
1572 }));
1573 }
1574
1575 Ok(())
1576 }
1577
1578 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1579 pub unsafe fn release_full_screen_exclusive_mode_unchecked(&self) -> Result<(), VulkanError> {
1580 self.full_screen_exclusive_held
1581 .store(false, Ordering::Release);
1582
1583 let fns = self.device.fns();
1584 unsafe {
1585 (fns.ext_full_screen_exclusive
1586 .release_full_screen_exclusive_mode_ext)(
1587 self.device.handle(), self.handle
1588 )
1589 }
1590 .result()
1591 .map_err(VulkanError::from)?;
1592
1593 Ok(())
1594 }
1595
1596 #[inline]
1601 pub fn is_full_screen_exclusive(&self) -> bool {
1602 if self.full_screen_exclusive != FullScreenExclusive::ApplicationControlled {
1603 false
1604 } else {
1605 self.full_screen_exclusive_held.load(Ordering::SeqCst)
1606 }
1607 }
1608
1609 pub(crate) fn image_layout_initialized(&self, image_index: u32) {
1614 let image_entry = self.images.get(image_index as usize);
1615 if let Some(image_entry) = image_entry {
1616 image_entry
1617 .layout_initialized
1618 .store(true, Ordering::Relaxed);
1619 }
1620 }
1621
1622 pub(crate) fn is_image_layout_initialized(&self, image_index: u32) -> bool {
1623 let image_entry = self.images.get(image_index as usize);
1624 if let Some(image_entry) = image_entry {
1625 image_entry.layout_initialized.load(Ordering::Relaxed)
1626 } else {
1627 false
1628 }
1629 }
1630
1631 #[inline]
1632 pub(crate) unsafe fn full_screen_exclusive_held(&self) -> &AtomicBool {
1633 &self.full_screen_exclusive_held
1634 }
1635
1636 #[inline]
1637 pub(crate) unsafe fn try_claim_present_id(&self, present_id: NonZeroU64) -> bool {
1638 let present_id = u64::from(present_id);
1639 self.prev_present_id.fetch_max(present_id, Ordering::SeqCst) < present_id
1640 }
1641}
1642
1643impl Drop for Swapchain {
1644 #[inline]
1645 fn drop(&mut self) {
1646 let fns = self.device.fns();
1647 unsafe {
1648 (fns.khr_swapchain.destroy_swapchain_khr)(
1649 self.device.handle(),
1650 self.handle,
1651 ptr::null(),
1652 )
1653 };
1654 }
1655}
1656
1657unsafe impl VulkanObject for Swapchain {
1658 type Handle = ash::vk::SwapchainKHR;
1659
1660 #[inline]
1661 fn handle(&self) -> Self::Handle {
1662 self.handle
1663 }
1664}
1665
1666unsafe impl DeviceOwned for Swapchain {
1667 #[inline]
1668 fn device(&self) -> &Arc<Device> {
1669 &self.device
1670 }
1671}
1672
1673impl_id_counter!(Swapchain);
1674
1675#[derive(Clone, Debug)]
1681pub struct SwapchainCreateInfo {
1682 pub flags: SwapchainCreateFlags,
1686
1687 pub min_image_count: u32,
1693
1694 pub image_format: Format,
1698
1699 pub image_view_formats: Vec<Format>,
1717
1718 pub image_color_space: ColorSpace,
1722
1723 pub image_extent: [u32; 2],
1731
1732 pub image_array_layers: u32,
1736
1737 pub image_usage: ImageUsage,
1741
1742 pub image_sharing: Sharing<SmallVec<[u32; 4]>>,
1747
1748 pub pre_transform: SurfaceTransform,
1752
1753 pub composite_alpha: CompositeAlpha,
1757
1758 pub present_mode: PresentMode,
1763
1764 pub present_modes: SmallVec<[PresentMode; PresentMode::COUNT]>,
1775
1776 pub clipped: bool,
1783
1784 pub scaling_behavior: Option<PresentScaling>,
1794
1795 pub present_gravity: Option<[PresentGravity; 2]>,
1806
1807 pub full_screen_exclusive: FullScreenExclusive,
1815
1816 pub win32_monitor: Option<Win32Monitor>,
1823
1824 pub _ne: crate::NonExhaustive,
1825}
1826
1827impl Default for SwapchainCreateInfo {
1828 #[inline]
1829 fn default() -> Self {
1830 Self {
1831 flags: SwapchainCreateFlags::empty(),
1832 min_image_count: 2,
1833 image_format: Format::UNDEFINED,
1834 image_view_formats: Vec::new(),
1835 image_color_space: ColorSpace::SrgbNonLinear,
1836 image_extent: [0, 0],
1837 image_array_layers: 1,
1838 image_usage: ImageUsage::empty(),
1839 image_sharing: Sharing::Exclusive,
1840 pre_transform: SurfaceTransform::Identity,
1841 composite_alpha: CompositeAlpha::Opaque,
1842 present_mode: PresentMode::Fifo,
1843 present_modes: SmallVec::new(),
1844 clipped: true,
1845 scaling_behavior: None,
1846 present_gravity: None,
1847 full_screen_exclusive: FullScreenExclusive::Default,
1848 win32_monitor: None,
1849 _ne: crate::NonExhaustive(()),
1850 }
1851 }
1852}
1853
1854impl SwapchainCreateInfo {
1855 pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
1856 let &Self {
1857 flags,
1858 min_image_count: _,
1859 image_format,
1860 ref image_view_formats,
1861 image_color_space,
1862 image_extent,
1863 image_array_layers,
1864 image_usage,
1865 ref image_sharing,
1866 pre_transform,
1867 composite_alpha,
1868 present_mode,
1869 ref present_modes,
1870 clipped: _,
1871 scaling_behavior,
1872 present_gravity,
1873 full_screen_exclusive,
1874 win32_monitor,
1875 _ne: _,
1876 } = self;
1877
1878 flags.validate_device(device).map_err(|err| {
1879 err.add_context("flags")
1880 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-flags-parameter"])
1881 })?;
1882
1883 image_format.validate_device(device).map_err(|err| {
1884 err.add_context("image_format")
1885 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageFormat-parameter"])
1886 })?;
1887
1888 image_color_space.validate_device(device).map_err(|err| {
1889 err.add_context("image_color_space")
1890 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageColorSpace-parameter"])
1891 })?;
1892
1893 image_usage.validate_device(device).map_err(|err| {
1894 err.add_context("image_usage")
1895 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-imageUsage-parameter"])
1896 })?;
1897
1898 if image_usage.is_empty() {
1899 return Err(Box::new(ValidationError {
1900 context: "image_usage".into(),
1901 problem: "is empty".into(),
1902 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageUsage-requiredbitmask"],
1903 ..Default::default()
1904 }));
1905 }
1906
1907 pre_transform.validate_device(device).map_err(|err| {
1908 err.add_context("pre_transform")
1909 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-preTransform-parameter"])
1910 })?;
1911
1912 composite_alpha.validate_device(device).map_err(|err| {
1913 err.add_context("composite_alpha")
1914 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-compositeAlpha-parameter"])
1915 })?;
1916
1917 present_mode.validate_device(device).map_err(|err| {
1918 err.add_context("present_mode")
1919 .set_vuids(&["VUID-VkSwapchainCreateInfoKHR-presentMode-parameter"])
1920 })?;
1921
1922 if image_extent.contains(&0) {
1923 return Err(Box::new(ValidationError {
1924 context: "image_extent".into(),
1925 problem: "one or more elements are zero".into(),
1926 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageExtent-01689"],
1927 ..Default::default()
1928 }));
1929 }
1930
1931 if image_array_layers == 0 {
1932 return Err(Box::new(ValidationError {
1933 context: "image_array_layers".into(),
1934 problem: "is zero".into(),
1935 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageArrayLayers-01275"],
1936 ..Default::default()
1937 }));
1938 }
1939
1940 match image_sharing {
1941 Sharing::Exclusive => (),
1942 Sharing::Concurrent(queue_family_indices) => {
1943 if queue_family_indices.len() < 2 {
1944 return Err(Box::new(ValidationError {
1945 context: "image_sharing".into(),
1946 problem: "is `Sharing::Concurrent`, and contains less than 2 \
1947 queue family indices"
1948 .into(),
1949 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01278"],
1950 ..Default::default()
1951 }));
1952 }
1953
1954 let queue_family_count =
1955 device.physical_device().queue_family_properties().len() as u32;
1956
1957 for (index, &queue_family_index) in queue_family_indices.iter().enumerate() {
1958 if queue_family_indices[..index].contains(&queue_family_index) {
1959 return Err(Box::new(ValidationError {
1960 context: "queue_family_indices".into(),
1961 problem: format!(
1962 "the queue family index in the list at index {} is contained in \
1963 the list more than once",
1964 index,
1965 )
1966 .into(),
1967 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"],
1968 ..Default::default()
1969 }));
1970 }
1971
1972 if queue_family_index >= queue_family_count {
1973 return Err(Box::new(ValidationError {
1974 context: format!("queue_family_indices[{}]", index).into(),
1975 problem: "is not less than the number of queue families in the \
1976 physical device"
1977 .into(),
1978 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageSharingMode-01428"],
1979 ..Default::default()
1980 }));
1981 }
1982 }
1983 }
1984 };
1985
1986 let image_format_properties = unsafe {
1987 device
1988 .physical_device()
1989 .image_format_properties_unchecked(ImageFormatInfo {
1990 format: image_format,
1991 image_type: ImageType::Dim2d,
1992 tiling: ImageTiling::Optimal,
1993 usage: image_usage,
1994 ..Default::default()
1995 })
1996 }
1997 .map_err(|_err| {
1998 Box::new(ValidationError {
1999 problem: "`PhysicalDevice::image_format_properties` \
2000 returned an error"
2001 .into(),
2002 ..Default::default()
2003 })
2004 })?;
2005
2006 if image_format_properties.is_none() {
2007 return Err(Box::new(ValidationError {
2008 problem: "the combination of `image_format` and `image_usage` is not supported \
2009 for images by the physical device"
2010 .into(),
2011 vuids: &["VUID-VkSwapchainCreateInfoKHR-imageFormat-01778"],
2012 ..Default::default()
2013 }));
2014 }
2015
2016 if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT)
2017 && !image_view_formats.contains(&image_format)
2018 {
2019 return Err(Box::new(ValidationError {
2020 problem: "`flags` contains `SwapchainCreateFlags::MUTABLE_FORMAT`, but \
2021 `image_view_formats` does not contain `image_format`"
2022 .into(),
2023 vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-03168"],
2024 ..Default::default()
2025 }));
2026 }
2027
2028 if !image_view_formats.is_empty() {
2029 if !(device.api_version() >= Version::V1_2
2030 || device.enabled_extensions().khr_image_format_list)
2031 {
2032 return Err(Box::new(ValidationError {
2033 context: "image_view_formats".into(),
2034 problem: "is not empty".into(),
2035 requires_one_of: RequiresOneOf(&[
2036 RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
2037 RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]),
2038 ]),
2039 ..Default::default()
2040 }));
2041 }
2042
2043 if !flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT)
2044 && image_view_formats.len() != 1
2045 {
2046 return Err(Box::new(ValidationError {
2047 problem: "`flags` does not contain `SwapchainCreateFlags::MUTABLE_FORMAT`, \
2048 but `image_view_formats` contains more than one element"
2049 .into(),
2050 vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-04100"],
2051 ..Default::default()
2052 }));
2053 }
2054
2055 for (index, &image_view_format) in image_view_formats.iter().enumerate() {
2056 image_view_format.validate_device(device).map_err(|err| {
2057 err.add_context(format!("image_view_formats[{}]", index))
2058 .set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"])
2059 })?;
2060
2061 if image_view_format.compatibility() != image_format.compatibility() {
2062 return Err(Box::new(ValidationError {
2063 problem: format!(
2064 "`image_view_formats[{}]` is not compatible with `image_format`",
2065 index
2066 )
2067 .into(),
2068 vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-04099"],
2069 ..Default::default()
2070 }));
2071 }
2072 }
2073 }
2074
2075 if !present_modes.is_empty() {
2076 if !device.enabled_extensions().ext_swapchain_maintenance1 {
2077 return Err(Box::new(ValidationError {
2078 context: "present_modes".into(),
2079 problem: "is not empty".into(),
2080 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2081 "ext_swapchain_maintenance1",
2082 )])]),
2083 ..Default::default()
2084 }));
2085 }
2086
2087 for (index, &present_mode) in present_modes.iter().enumerate() {
2088 present_mode.validate_device(device).map_err(|err| {
2089 err.add_context(format!("present_modes[{}]", index))
2090 .set_vuids(&[
2091 "VUID-VkSwapchainPresentModesCreateInfoEXT-pPresentModes-parameter",
2092 ])
2093 })?;
2094 }
2095
2096 if !present_modes.contains(&present_mode) {
2097 return Err(Box::new(ValidationError {
2098 problem: "`present_modes` is not empty, but does not contain `present_mode`"
2099 .into(),
2100 vuids: &["VUID-VkSwapchainPresentModesCreateInfoEXT-presentMode-07764"],
2101 ..Default::default()
2102 }));
2103 }
2104 }
2105
2106 if let Some(scaling_behavior) = scaling_behavior {
2107 if !device.enabled_extensions().ext_swapchain_maintenance1 {
2108 return Err(Box::new(ValidationError {
2109 context: "scaling_behavior".into(),
2110 problem: "is `Some`".into(),
2111 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2112 "ext_swapchain_maintenance1",
2113 )])]),
2114 ..Default::default()
2115 }));
2116 }
2117
2118 scaling_behavior.validate_device(device).map_err(|err| {
2119 err.add_context("scaling_behavior").set_vuids(&[
2120 "VUID-VkSwapchainPresentScalingCreateInfoEXT-scalingBehavior-parameter",
2121 ])
2122 })?;
2123
2124 }
2127
2128 if let Some(present_gravity) = present_gravity {
2129 if !device.enabled_extensions().ext_swapchain_maintenance1 {
2130 return Err(Box::new(ValidationError {
2131 context: "present_gravity".into(),
2132 problem: "is `Some`".into(),
2133 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2134 "ext_swapchain_maintenance1",
2135 )])]),
2136 ..Default::default()
2137 }));
2138 }
2139
2140 for (axis_index, present_gravity) in present_gravity.into_iter().enumerate() {
2141 present_gravity.validate_device(device).map_err(|err| {
2142 err.add_context(format!("present_gravity[{}]", axis_index))
2143 .set_vuids(&[
2144 "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityX-parameter",
2145 "VUID-VkSwapchainPresentScalingCreateInfoEXT-presentGravityY-parameter",
2146 ])
2147 })?;
2148 }
2149
2150 }
2158
2159 if full_screen_exclusive != FullScreenExclusive::Default {
2160 if !device.enabled_extensions().ext_full_screen_exclusive {
2161 return Err(Box::new(ValidationError {
2162 context: "full_screen_exclusive".into(),
2163 problem: "is not `FullScreenExclusive::Default`".into(),
2164 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
2165 "ext_full_screen_exclusive",
2166 )])]),
2167 ..Default::default()
2168 }));
2169 }
2170
2171 full_screen_exclusive
2172 .validate_device(device)
2173 .map_err(|err| {
2174 err.add_context("full_screen_exclusive").set_vuids(&[
2175 "VUID-VkSurfaceFullScreenExclusiveInfoEXT-fullScreenExclusive-parameter",
2176 ])
2177 })?;
2178
2179 if win32_monitor.is_none() {
2180 return Err(Box::new(ValidationError {
2181 problem: "`full_screen_exclusive` is not `FullScreenExclusive::Default`, but \
2182 `win32_monitor` is `None`"
2183 .into(),
2184 vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-02679"],
2185 ..Default::default()
2186 }));
2187 }
2188 } else if win32_monitor.is_some() {
2189 return Err(Box::new(ValidationError {
2190 problem: "`full_screen_exclusive` is `FullScreenExclusive::Default`, but \
2191 `win32_monitor` is `Some`"
2192 .into(),
2193 ..Default::default()
2194 }));
2195 }
2196
2197 Ok(())
2198 }
2199
2200 pub(crate) fn to_vk<'a>(
2201 &'a self,
2202 surface_vk: ash::vk::SurfaceKHR,
2203 old_swapchain_vk: ash::vk::SwapchainKHR,
2204 extensions_vk: &'a mut SwapchainCreateInfoExtensionsVk<'_>,
2205 ) -> ash::vk::SwapchainCreateInfoKHR<'a> {
2206 let &Self {
2207 flags,
2208 min_image_count,
2209 image_format,
2210 image_view_formats: _,
2211 image_color_space,
2212 image_extent,
2213 image_array_layers,
2214 image_usage,
2215 ref image_sharing,
2216 pre_transform,
2217 composite_alpha,
2218 present_mode,
2219 present_modes: _,
2220 clipped,
2221 scaling_behavior: _,
2222 present_gravity: _,
2223 full_screen_exclusive: _,
2224 win32_monitor: _,
2225 _ne: _,
2226 } = self;
2227
2228 let (image_sharing_mode_vk, queue_family_indices_vk) = match image_sharing {
2229 Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, [].as_slice()),
2230 Sharing::Concurrent(ids) => (ash::vk::SharingMode::CONCURRENT, ids.as_slice()),
2231 };
2232
2233 let mut val_vk = ash::vk::SwapchainCreateInfoKHR::default()
2234 .flags(flags.into())
2235 .surface(surface_vk)
2236 .min_image_count(min_image_count)
2237 .image_format(image_format.into())
2238 .image_color_space(image_color_space.into())
2239 .image_extent(ash::vk::Extent2D {
2240 width: image_extent[0],
2241 height: image_extent[1],
2242 })
2243 .image_array_layers(image_array_layers)
2244 .image_usage(image_usage.into())
2245 .image_sharing_mode(image_sharing_mode_vk)
2246 .queue_family_indices(queue_family_indices_vk)
2247 .pre_transform(pre_transform.into())
2248 .composite_alpha(composite_alpha.into())
2249 .present_mode(present_mode.into())
2250 .clipped(clipped)
2251 .old_swapchain(old_swapchain_vk);
2252
2253 let SwapchainCreateInfoExtensionsVk {
2254 full_screen_exclusive_vk,
2255 full_screen_exclusive_win32_vk,
2256 image_format_list_vk,
2257 present_modes_vk,
2258 present_scaling_vk,
2259 } = extensions_vk;
2260
2261 if let Some(next) = full_screen_exclusive_vk {
2262 val_vk = val_vk.push_next(next);
2263 }
2264
2265 if let Some(next) = full_screen_exclusive_win32_vk {
2266 val_vk = val_vk.push_next(next);
2267 }
2268
2269 if let Some(next) = image_format_list_vk {
2270 val_vk = val_vk.push_next(next);
2271 }
2272
2273 if let Some(next) = present_modes_vk {
2274 val_vk = val_vk.push_next(next);
2275 }
2276
2277 if let Some(next) = present_scaling_vk {
2278 val_vk = val_vk.push_next(next);
2279 }
2280
2281 val_vk
2282 }
2283
2284 pub(crate) fn to_vk_extensions<'a>(
2285 &self,
2286 fields1_vk: &'a SwapchainCreateInfoFields1Vk,
2287 ) -> SwapchainCreateInfoExtensionsVk<'a> {
2288 let &Self {
2289 flags: _,
2290 min_image_count: _,
2291 image_format: _,
2292 image_view_formats: _,
2293 image_color_space: _,
2294 image_extent: _,
2295 image_array_layers: _,
2296 image_usage: _,
2297 image_sharing: _,
2298 pre_transform: _,
2299 composite_alpha: _,
2300 present_mode: _,
2301 present_modes: _,
2302 clipped: _,
2303 scaling_behavior,
2304 present_gravity,
2305 full_screen_exclusive,
2306 win32_monitor,
2307 _ne: _,
2308 } = self;
2309 let SwapchainCreateInfoFields1Vk {
2310 present_modes_vk,
2311 view_formats_vk,
2312 } = fields1_vk;
2313
2314 let full_screen_exclusive_vk = (full_screen_exclusive != FullScreenExclusive::Default)
2315 .then(|| {
2316 ash::vk::SurfaceFullScreenExclusiveInfoEXT::default()
2317 .full_screen_exclusive(full_screen_exclusive.into())
2318 });
2319
2320 let full_screen_exclusive_win32_vk = win32_monitor.map(|win32_monitor| {
2321 ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT::default().hmonitor(win32_monitor.0)
2322 });
2323
2324 let image_format_list_vk = (!view_formats_vk.is_empty())
2325 .then(|| ash::vk::ImageFormatListCreateInfo::default().view_formats(view_formats_vk));
2326
2327 let present_modes_vk = (!present_modes_vk.is_empty()).then(|| {
2328 ash::vk::SwapchainPresentModesCreateInfoEXT::default().present_modes(present_modes_vk)
2329 });
2330
2331 let present_scaling_vk =
2332 (scaling_behavior.is_some() || present_gravity.is_some()).then(|| {
2333 let [present_gravity_x, present_gravity_y] =
2334 present_gravity.map_or_else(Default::default, |pg| pg.map(Into::into));
2335 ash::vk::SwapchainPresentScalingCreateInfoEXT::default()
2336 .scaling_behavior(scaling_behavior.map_or_else(Default::default, Into::into))
2337 .present_gravity_x(present_gravity_x)
2338 .present_gravity_y(present_gravity_y)
2339 });
2340
2341 SwapchainCreateInfoExtensionsVk {
2342 full_screen_exclusive_vk,
2343 full_screen_exclusive_win32_vk,
2344 image_format_list_vk,
2345 present_modes_vk,
2346 present_scaling_vk,
2347 }
2348 }
2349
2350 pub(crate) fn to_vk_fields1(&self) -> SwapchainCreateInfoFields1Vk {
2351 let present_modes_vk = self.present_modes.iter().copied().map(Into::into).collect();
2352 let view_formats_vk = self
2353 .image_view_formats
2354 .iter()
2355 .copied()
2356 .map(ash::vk::Format::from)
2357 .collect();
2358
2359 SwapchainCreateInfoFields1Vk {
2360 present_modes_vk,
2361 view_formats_vk,
2362 }
2363 }
2364}
2365
2366pub(crate) struct SwapchainCreateInfoExtensionsVk<'a> {
2367 pub(crate) full_screen_exclusive_vk:
2368 Option<ash::vk::SurfaceFullScreenExclusiveInfoEXT<'static>>,
2369 pub(crate) full_screen_exclusive_win32_vk:
2370 Option<ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT<'static>>,
2371 pub(crate) image_format_list_vk: Option<ash::vk::ImageFormatListCreateInfo<'a>>,
2372 pub(crate) present_modes_vk: Option<ash::vk::SwapchainPresentModesCreateInfoEXT<'a>>,
2373 pub(crate) present_scaling_vk: Option<ash::vk::SwapchainPresentScalingCreateInfoEXT<'static>>,
2374}
2375
2376pub(crate) struct SwapchainCreateInfoFields1Vk {
2377 pub(crate) present_modes_vk: SmallVec<[ash::vk::PresentModeKHR; PresentMode::COUNT]>,
2378 pub(crate) view_formats_vk: Vec<ash::vk::Format>,
2379}
2380
2381vulkan_bitflags! {
2382 #[non_exhaustive]
2383
2384 SwapchainCreateFlags = SwapchainCreateFlagsKHR(u32);
2386
2387 MUTABLE_FORMAT = MUTABLE_FORMAT
2405 RequiresOneOf([
2406 RequiresAllOf([DeviceExtension(khr_swapchain_mutable_format)]),
2407 ]),
2408
2409 }
2415
2416impl From<SwapchainCreateFlags> for ImageCreateFlags {
2417 #[inline]
2418 fn from(flags: SwapchainCreateFlags) -> Self {
2419 let mut result = ImageCreateFlags::empty();
2421
2422 if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT) {
2433 result |= ImageCreateFlags::MUTABLE_FORMAT | ImageCreateFlags::EXTENDED_USAGE;
2434 }
2435
2436 result
2437 }
2438}
2439
2440vulkan_bitflags_enum! {
2441 #[non_exhaustive]
2442
2443 PresentScalingFlags,
2445
2446 PresentScaling,
2448
2449 = PresentScalingFlagsEXT(u32);
2450
2451 ONE_TO_ONE, OneToOne = ONE_TO_ONE,
2453
2454 ASPECT_RATIO_STRETCH, AspectRatioStretch = ASPECT_RATIO_STRETCH,
2457
2458 STRETCH, Stretch = STRETCH,
2461}
2462
2463vulkan_bitflags_enum! {
2464 #[non_exhaustive]
2465
2466 PresentGravityFlags,
2468
2469 PresentGravity,
2471
2472 = PresentGravityFlagsEXT(u32);
2473
2474 MIN, Min = MIN,
2476
2477 MAX, Max = MAX,
2479
2480 CENTERED, Centered = CENTERED,
2482}
2483
2484vulkan_enum! {
2485 #[non_exhaustive]
2486
2487 FullScreenExclusive = FullScreenExclusiveEXT(i32);
2489
2490 Default = DEFAULT,
2493
2494 Allowed = ALLOWED,
2499
2500 Disallowed = DISALLOWED,
2503
2504 ApplicationControlled = APPLICATION_CONTROLLED,
2508}
2509
2510#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2512pub struct Win32Monitor(pub(crate) ash::vk::HMONITOR);
2513
2514impl Win32Monitor {
2515 pub unsafe fn new(hmonitor: ash::vk::HMONITOR) -> Self {
2521 Self(hmonitor)
2522 }
2523}
2524
2525unsafe impl Send for Win32Monitor {}
2527unsafe impl Sync for Win32Monitor {}