Skip to main content

ursus_core/vulkan/core/
swapchain.rs

1use 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}