use anyhow::Result; use winit::window::Window; use vulkanalia::prelude::v1_0::*; use vulkanalia::vk::{KhrSurfaceExtension, KhrSwapchainExtension}; use crate::app::AppData; use crate::physical_device::QueueFamilyIndices; #[derive(Clone, Debug)] pub(crate) struct SwapchainSupport { capabilities: vk::SurfaceCapabilitiesKHR, pub(crate) formats: Vec, pub(crate) present_modes: Vec, } impl SwapchainSupport { pub(crate) unsafe fn get( instance: &Instance, data: &AppData, physical_device: vk::PhysicalDevice, ) -> Result { Ok(Self { capabilities: instance .get_physical_device_surface_capabilities_khr(physical_device, data.surface)?, formats: instance .get_physical_device_surface_formats_khr(physical_device, data.surface)?, present_modes: instance .get_physical_device_surface_present_modes_khr(physical_device, data.surface)?, }) } } fn get_swapchain_surface_format(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR { formats .iter() .cloned() .find(|f| { f.format == vk::Format::B8G8R8A8_SRGB && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR }) .unwrap_or_else(|| formats[0]) } fn get_swapchain_present_mode(present_modes: &[vk::PresentModeKHR]) -> vk::PresentModeKHR { present_modes .iter() .cloned() .find(|m| *m == vk::PresentModeKHR::MAILBOX) .unwrap_or(vk::PresentModeKHR::FIFO) } fn get_swapchain_extent(window: &Window, capabilities: vk::SurfaceCapabilitiesKHR) -> vk::Extent2D { if capabilities.current_extent.width != u32::max_value() { capabilities.current_extent } else { let size = window.inner_size(); let clamp = |min: u32, max: u32, v: u32| min.max(max.min(v)); vk::Extent2D::builder() .width(clamp( capabilities.min_image_extent.width, capabilities.max_image_extent.width, size.width, )) .height(clamp( capabilities.min_image_extent.height, capabilities.max_image_extent.height, size.height, )) .build() } } pub(crate) unsafe fn create_swapchain( window: &Window, instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<()> { let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; let support = SwapchainSupport::get(instance, data, data.physical_device)?; let surface_format = get_swapchain_surface_format(&support.formats); let present_mode = get_swapchain_present_mode(&support.present_modes); let extent = get_swapchain_extent(window, support.capabilities); let mut image_count = support.capabilities.min_image_count + 1; if support.capabilities.max_image_count != 0 && image_count > support.capabilities.max_image_count { image_count = support.capabilities.max_image_count; } let mut queue_family_indices = vec![]; let image_sharing_mode = if indices.graphics != indices.present { queue_family_indices.push(indices.graphics); queue_family_indices.push(indices.present); vk::SharingMode::CONCURRENT } else { vk::SharingMode::EXCLUSIVE }; let info = vk::SwapchainCreateInfoKHR::builder() .surface(data.surface) .min_image_count(image_count) .image_format(surface_format.format) .image_color_space(surface_format.color_space) .image_extent(extent) .image_array_layers(1) .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) .image_sharing_mode(image_sharing_mode) .queue_family_indices(&queue_family_indices) .pre_transform(support.capabilities.current_transform) .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) .present_mode(present_mode) .clipped(true) .old_swapchain(vk::SwapchainKHR::null()); data.swapchain_format = surface_format.format; data.swapchain_extent = extent; data.swapchain = device.create_swapchain_khr(&info, None)?; data.swapchain_images = device.get_swapchain_images_khr(data.swapchain)?; Ok(()) }