From 76450da41fcf9d9f018d1b638d8739e5c0a9d91e Mon Sep 17 00:00:00 2001 From: Serkyo Date: Thu, 30 Apr 2026 15:15:07 +0200 Subject: [PATCH] feat(renderer): finalize swapchain initialization Negotiated swapchain settings (format, present mode, extent), created the swapchain and its images, and ensured correct destruction order. Added comprehensive documentation for all new fields and methods. --- crates/renderer/src/lib.rs | 93 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index 743b512..95981b7 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -1,7 +1,7 @@ pub mod error; use std::ffi::{CStr, c_char}; -use ash::{Device, Entry, Instance, ext, khr, vk}; +use ash::{Device, Entry, Instance, ext, khr, vk::{self, SurfaceKHR}}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use tracing::{debug, error, info, warn}; @@ -25,8 +25,20 @@ pub struct Renderer { graphics_queue: vk::Queue, /// The index of the graphics queue family. graphics_queue_index: u32, + /// The surface loader instance. surface_loader: khr::surface::Instance, + /// The window surface handle. surface: vk::SurfaceKHR, + /// The swapchain loader instance. + swapchain_loader: khr::swapchain::Device, + /// The swapchain handle. + swapchain: vk::SwapchainKHR, + /// The images retrieved from the swapchain. + swapchain_images: Vec, + /// The pixel format of the swapchain images. + swapchain_format: vk::Format, + /// The resolution of the swapchain images. + swapchain_extent: vk::Extent2D, } impl Renderer { @@ -105,6 +117,8 @@ impl Renderer { // Create the logical device let (device, graphics_queue) = Self::create_logical_device(&instance, physical_device, graphics_queue_index)?; + let (swapchain_loader, swapchain, swapchain_images, swapchain_format, swapchain_extent) = Self::create_swapchain(&instance, physical_device, &device, &surface_loader, surface)?; + Ok(Self { _entry: entry, instance, @@ -116,6 +130,11 @@ impl Renderer { graphics_queue_index, surface_loader, surface, + swapchain_loader, + swapchain, + swapchain_images, + swapchain_format, + swapchain_extent, }) } @@ -212,24 +231,88 @@ impl Renderer { Ok((device, graphics_queue)) } + + /// Creates a swapchain and retrieves its images. + /// + /// This function negotiates with the surface to find a suitable color format, + /// presentation mode, and image resolution. + fn create_swapchain( + instance: &Instance, + physical_device: vk::PhysicalDevice, + device: &Device, + surface_loader: &khr::surface::Instance, + surface: vk::SurfaceKHR, + ) -> Result< + (khr::swapchain::Device, vk::SwapchainKHR, Vec, vk::Format, vk::Extent2D), + RendererError, + > { + let surface_capabilities = unsafe { surface_loader.get_physical_device_surface_capabilities(physical_device, surface)? }; + + let surface_formats = unsafe { surface_loader.get_physical_device_surface_formats(physical_device, surface)? }; + + let surface_present_modes = unsafe { surface_loader.get_physical_device_surface_present_modes(physical_device, surface)?}; + + // 1. Pick the best color format (Prefer SRGB for accurate colors) + let format = surface_formats.iter() + .find(|f| f.format == vk::Format::B8G8R8A8_SRGB && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR) + .unwrap_or(&surface_formats[0]); + + // 2. Pick the best presentation mode (Mailbox = Triple Buffering, FIFO = VSync) + let present_mode = surface_present_modes.into_iter() + .find(|&m| m == vk::PresentModeKHR::MAILBOX) + .unwrap_or(vk::PresentModeKHR::FIFO); + + let extent = surface_capabilities.current_extent; + + let mut image_count = surface_capabilities.min_image_count + 1; + + if surface_capabilities.max_image_count > 0 && image_count > surface_capabilities.max_image_count { + image_count = surface_capabilities.max_image_count; + } + + let swapchain_loader = khr::swapchain::Device::new(instance, device); + + let create_info = vk::SwapchainCreateInfoKHR::default() + .surface(surface) + .min_image_count(image_count) + .image_format(format.format) + .image_color_space(format.color_space) + .image_extent(extent) + .image_array_layers(1) + .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .pre_transform(surface_capabilities.current_transform) + .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) + .present_mode(present_mode) + .clipped(true); + + let swapchain = unsafe { swapchain_loader.create_swapchain(&create_info, None)? }; + + let images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? }; + + Ok((swapchain_loader, swapchain, images, format.format, extent )) + } } /// Ensures all Vulkan resources are destroyed in the correct order. impl Drop for Renderer { fn drop(&mut self) { unsafe { - // 1. Destroy the logical device first + // 1. Destroy the swapchain BEFORE the device it belongs to + self.swapchain_loader.destroy_swapchain(self.swapchain, None); + + // 2. Destroy the logical device self.device.destroy_device(None); - // 2. Destroy the surface + // 3. Destroy the surface self.surface_loader.destroy_surface(self.surface, None); - // 3. Destroy the debug messenger if we created one + // 4. Destroy the debug messenger if we created one if let Some(utils) = &self.debug_utils { utils.destroy_debug_utils_messenger(self.debug_messenger, None); } - // 4. Finally, destroy the instance + // 5. Finally, destroy the instance self.instance.destroy_instance(None); } }