use crate::prelude::*; use anyhow::Result; use std::cmp; use std::sync::Mutex; pub enum NextImageSynchronization<'a> { Semaphore(&'a Semaphore<'a>), Fence(&'a Fence<'a>), } impl<'a> From<&'a Semaphore<'a>> for NextImageSynchronization<'a> { fn from(value: &'a Semaphore<'a>) -> Self { Self::Semaphore(value) } } impl<'a> From<&'a Fence<'a>> for NextImageSynchronization<'a> { fn from(value: &'a Fence<'a>) -> Self { Self::Fence(value) } } #[derive(Debug)] pub struct Swapchain<'a> { width: u32, height: u32, index: u32, device: &'a Device, surface: Surface<'a>, create_info: VkSwapchainCreateInfoKHR, swapchain: VkSwapchainKHR, usage: VkImageUsageFlagBits, raw: bool, } impl<'a> Swapchain<'a> { pub fn new( device: &'a Device, surface: Surface<'a>, vsync: bool, image_count: u32, image_usage: impl Into, prefered_format: VkFormat, array_layers: u32, ) -> Result> { let surface_caps = surface.capabilities(&device)?; let extent = if surface_caps.currentExtent.width == u32::max_value() { return Err(anyhow::Error::msg("Surface has no extent")); } else { VkExtent2D { width: surface_caps.currentExtent.width, height: surface_caps.currentExtent.height, } }; let mut present_mode = VK_PRESENT_MODE_FIFO_KHR; if !vsync { for present_mode_iter in surface.present_modes(&device)? { if present_mode_iter == VK_PRESENT_MODE_MAILBOX_KHR { present_mode = VK_PRESENT_MODE_MAILBOX_KHR; break; } else if present_mode_iter == VK_PRESENT_MODE_IMMEDIATE_KHR { present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } } let swapchain_image_count = if surface_caps.maxImageCount < surface_caps.minImageCount { cmp::max(image_count, surface_caps.minImageCount) } else { cmp::max( cmp::min(image_count, surface_caps.maxImageCount), surface_caps.minImageCount, ) }; let pretransform = if (surface_caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 { VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR.into() } else { surface_caps.currentTransform }; let (format, colorspace) = surface.format_colorspace(&device, prefered_format)?; let swapchain_ci = VkSwapchainCreateInfoKHR::new( 0, surface.vk_handle(), swapchain_image_count, format, colorspace, extent, array_layers, image_usage, VK_SHARING_MODE_EXCLUSIVE, &[], pretransform, VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, present_mode, device.physical_device().features().shaderClipDistance, ); let swapchain = device.create_swapchain(&swapchain_ci)?; Ok(Swapchain { width: extent.width, height: extent.height, usage: swapchain_ci.imageUsage, index: 0, device, surface, create_info: swapchain_ci, swapchain, raw: false, }) } pub fn from_ci(device: &'a Device, swapchain_ci: &VkSwapchainCreateInfoKHR) -> Result { Ok(Swapchain { width: swapchain_ci.imageExtent.width, height: swapchain_ci.imageExtent.height, usage: swapchain_ci.imageUsage, index: 0, surface: Surface::from_vk_surface( swapchain_ci.surface, device.physical_device().instance(), ), create_info: swapchain_ci.clone(), swapchain: device.create_swapchain(swapchain_ci)?, device, raw: false, }) } pub fn from_raw( device: &'a Device, swapchain_ci: &VkSwapchainCreateInfoKHR, swapchain: VkSwapchainKHR, ) -> Self { Swapchain { width: swapchain_ci.imageExtent.width, height: swapchain_ci.imageExtent.height, usage: swapchain_ci.imageUsage, index: 0, surface: Surface::from_vk_surface( swapchain_ci.surface, device.physical_device().instance(), ), device, create_info: swapchain_ci.clone(), swapchain, raw: true, } } pub fn recreate(&mut self) -> Result<()> { // wait for the device to get idle self.device.wait_idle()?; let surface_caps = self.surface.capabilities(self.device)?; let extent = if surface_caps.currentExtent.width == u32::max_value() || surface_caps.currentExtent.height == u32::max_value() { return Err(anyhow::Error::msg("Surface has no extent")); } else if surface_caps.currentExtent.width == 0 || surface_caps.currentExtent.height == 0 { // don't recreate swapchain return Ok(()); } else { VkExtent2D { width: surface_caps.currentExtent.width, height: surface_caps.currentExtent.height, } }; let mut swapchain_ci = self.create_info.clone(); swapchain_ci.imageExtent = extent; swapchain_ci.set_old_swapchain(self.swapchain); let swapchain = self.device.create_swapchain(&swapchain_ci)?; // destroy the old swapchain self.destroy(); // replace swapchain self.swapchain = swapchain; // set new surface size self.width = extent.width; self.height = extent.height; Ok(()) } pub fn acquire_next_image<'b>( &mut self, time_out: u64, synchro: impl Into>, ) -> Result> { let synchro = synchro.into(); let res = self.device.acquire_next_image( self.swapchain, time_out, match synchro { NextImageSynchronization::Semaphore(semaphore) => Some(semaphore.vk_handle()), NextImageSynchronization::Fence(_) => None, }, match synchro { NextImageSynchronization::Semaphore(_) => None, NextImageSynchronization::Fence(fence) => Some(fence.vk_handle()), }, ); if let Ok(r) = &res { if let OutOfDate::Ok(i) = r { self.index = *i; } } res } /// set current /// only use when externally acquired next index !!! pub unsafe fn set_image_index(&mut self, index: u32) { self.index = index; } pub fn current_index(&self) -> u32 { self.index } pub fn vk_images(&self) -> Result> { self.device.swapchain_images(self.swapchain) } pub fn wrap_images( &self, images: &[VkImage], queue: &'a Mutex>, assume_layout: bool, ) -> Result>> { let format = self.format(); let tiling = VK_IMAGE_TILING_OPTIMAL; if !Image::check_configuration(&self.device, tiling, format, self.usage) { return Err(anyhow::Error::msg(format!( "Image configuration not allowed (tiling: {:?}, format: {:?}, usage: {:?})", tiling, format, self.usage, ))); } let mut swapchain_images = Vec::new(); for image in images { swapchain_images.push( Image::from_preinitialized( *image, format, self.width(), self.height(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, self.usage, assume_layout, ) .attach_sampler(Sampler::nearest_sampler().build(self.device)?) .build(&self.device, queue)?, ); } Ok(swapchain_images) } pub fn width(&self) -> u32 { self.width } pub fn height(&self) -> u32 { self.height } pub fn format(&self) -> VkFormat { self.create_info.imageFormat } #[inline] fn destroy(&self) { self.device.destroy_swapchain(self.swapchain) } } impl<'a> VulkanDevice for Swapchain<'a> { fn device(&self) -> &Device { &self.device } } impl<'a> VkHandle for Swapchain<'a> { fn vk_handle(&self) -> VkSwapchainKHR { self.swapchain } } impl<'a> VkHandle for &'a Swapchain<'a> { fn vk_handle(&self) -> VkSwapchainKHR { self.swapchain } } impl<'a> Drop for Swapchain<'a> { fn drop(&mut self) { if !self.raw { self.destroy(); } } }