ursus_core/vulkan/core/
swapchain.rs1use crate::render::gfx::types::Format;
2use crate::vulkan::{Device, Instance};
3use ash::vk;
4
5pub struct Swapchain {
6 pub handle: vk::SwapchainKHR,
7 pub images: Vec<vk::Image>,
8 pub image_views: Vec<vk::ImageView>,
9 pub format: Format,
10 pub extent: vk::Extent2D,
11 loader: ash::khr::swapchain::Device,
12 device: ash::Device,
13}
14
15impl Swapchain {
16 pub fn new(
17 instance: &Instance,
18 device: &Device,
19 surface: vk::SurfaceKHR,
20 width: u32,
21 height: u32,
22 vsync: bool,
23 ) -> anyhow::Result<Self> {
24 let surface_loader = ash::khr::surface::Instance::new(&instance.entry, &instance.handle);
25 let loader = ash::khr::swapchain::Device::new(&instance.handle, &device.handle);
26
27 let formats = unsafe { surface_loader.get_physical_device_surface_formats(device.physical, surface)? };
28 let format = formats
29 .iter()
30 .find(|f| f.format == vk::Format::B8G8R8A8_SRGB && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR)
31 .copied()
32 .unwrap_or(formats[0]);
33
34 let present_modes =
35 unsafe { surface_loader.get_physical_device_surface_present_modes(device.physical, surface)? };
36
37 let present_mode = if vsync {
38 vk::PresentModeKHR::FIFO
39 } else {
40 [
41 vk::PresentModeKHR::MAILBOX,
42 vk::PresentModeKHR::IMMEDIATE,
43 vk::PresentModeKHR::FIFO,
44 ]
45 .iter()
46 .find(|&&mode| present_modes.contains(&mode))
47 .copied()
48 .unwrap_or(vk::PresentModeKHR::FIFO)
49 };
50
51 let capabilities =
52 unsafe { surface_loader.get_physical_device_surface_capabilities(device.physical, surface)? };
53 let extent = if capabilities.current_extent.width != u32::MAX {
54 capabilities.current_extent
55 } else {
56 vk::Extent2D {
57 width: width.clamp(capabilities.min_image_extent.width, capabilities.max_image_extent.width),
58 height: height.clamp(capabilities.min_image_extent.height, capabilities.max_image_extent.height),
59 }
60 };
61
62 let image_count = (capabilities.min_image_count + 1).min(if capabilities.max_image_count == 0 {
63 u32::MAX
64 } else {
65 capabilities.max_image_count
66 });
67
68 let (sharing_mode, queue_families): (vk::SharingMode, Vec<u32>) =
69 if device.graphics_family != device.present_family {
70 (vk::SharingMode::CONCURRENT, vec![device.graphics_family, device.present_family])
71 } else {
72 (vk::SharingMode::EXCLUSIVE, vec![])
73 };
74
75 let create_info = vk::SwapchainCreateInfoKHR::default()
76 .surface(surface)
77 .min_image_count(image_count)
78 .image_format(format.format)
79 .image_color_space(format.color_space)
80 .image_extent(extent)
81 .image_array_layers(1)
82 .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST)
83 .image_sharing_mode(sharing_mode)
84 .queue_family_indices(&queue_families)
85 .pre_transform(capabilities.current_transform)
86 .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
87 .present_mode(present_mode)
88 .clipped(true);
89
90 let handle = unsafe { loader.create_swapchain(&create_info, None)? };
91 let images = unsafe { loader.get_swapchain_images(handle)? };
92
93 let image_views: anyhow::Result<Vec<_>> = images
94 .iter()
95 .map(|&image| {
96 let view_info = vk::ImageViewCreateInfo::default()
97 .image(image)
98 .view_type(vk::ImageViewType::TYPE_2D)
99 .format(format.format)
100 .subresource_range(vk::ImageSubresourceRange {
101 aspect_mask: vk::ImageAspectFlags::COLOR,
102 base_mip_level: 0,
103 level_count: 1,
104 base_array_layer: 0,
105 layer_count: 1,
106 });
107 Ok(unsafe { device.handle.create_image_view(&view_info, None)? })
108 })
109 .collect();
110 let image_views = image_views?;
111
112 log::info!(
113 "Swapchain: {}x{} {:?} ({} images, {:?})",
114 extent.width,
115 extent.height,
116 format.format,
117 image_views.len(),
118 present_mode
119 );
120
121 Ok(Self {
122 handle,
123 images,
124 image_views,
125 format: Format::from_vk(format.format),
126 extent,
127 loader,
128 device: device.handle.clone(),
129 })
130 }
131}
132
133impl Drop for Swapchain {
134 fn drop(&mut self) {
135 unsafe {
136 for &view in &self.image_views {
137 self.device.destroy_image_view(view, None);
138 }
139 self.loader.destroy_swapchain(self.handle, None);
140 }
141 log::debug!("Swapchain destroyed");
142 }
143}