use std::ops::Deref;
use anyhow::Result;
use ash::vk;
use crate::{AppSettings, Device, Error, Instance, Surface, WindowInterface};
use crate::image::*;
#[derive(Debug)]
pub(crate) struct SwapchainImage {
#[allow(dead_code)]
pub image: Image,
pub view: ImageView,
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Swapchain {
pub(super) handle: vk::SwapchainKHR,
pub(super) images: Vec<SwapchainImage>,
pub(super) format: vk::SurfaceFormatKHR,
pub(super) present_mode: vk::PresentModeKHR,
pub(super) extent: vk::Extent2D,
#[derivative(Debug = "ignore")]
pub(super) functions: ash::extensions::khr::Swapchain,
}
impl Swapchain {
pub fn new<Window: WindowInterface>(
instance: &Instance,
device: Device,
settings: &AppSettings<Window>,
surface: &Surface,
) -> Result<Self> {
let format = choose_surface_format(settings, surface)?;
let present_mode = choose_present_mode(settings, surface);
let extent = choose_swapchain_extent(settings, surface);
let image_count = {
let mut count = surface.capabilities().min_image_count + 1;
if surface.capabilities().max_image_count != 0 {
count = count.clamp(0, surface.capabilities().max_image_count);
}
count
};
let info = vk::SwapchainCreateInfoKHR::builder()
.surface(unsafe { surface.handle() })
.image_format(format.format)
.image_color_space(format.color_space)
.image_extent(extent)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.present_mode(present_mode)
.min_image_count(image_count)
.clipped(true)
.pre_transform(surface.capabilities().current_transform)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.build();
let functions = ash::extensions::khr::Swapchain::new(instance, unsafe { &device.handle() });
let swapchain = unsafe { functions.create_swapchain(&info, None)? };
#[cfg(feature = "log-objects")]
trace!("Created new VkSwapchainKHR {swapchain:p}");
let images: Vec<SwapchainImage> = unsafe { functions.get_swapchain_images(swapchain)? }
.iter()
.map(move |image| -> Result<SwapchainImage> {
let image = Image::new_managed(
device.clone(),
*image,
format.format,
vk::Extent3D {
width: extent.width,
height: extent.height,
depth: 1,
},
1,
1,
vk::SampleCountFlags::TYPE_1,
);
let view = image.view(vk::ImageAspectFlags::COLOR)?;
Ok(SwapchainImage {
image,
view,
})
})
.collect::<Result<Vec<SwapchainImage>>>()?;
Ok(Swapchain {
handle: swapchain,
format,
present_mode,
extent,
images,
functions,
})
}
pub unsafe fn handle(&self) -> vk::SwapchainKHR {
self.handle
}
pub(crate) fn images(&self) -> &[SwapchainImage] {
self.images.as_slice()
}
pub fn present_mode(&self) -> vk::PresentModeKHR {
self.present_mode
}
pub fn extent(&self) -> &vk::Extent2D {
&self.extent
}
pub fn format(&self) -> vk::SurfaceFormatKHR {
self.format
}
}
impl Deref for Swapchain {
type Target = ash::extensions::khr::Swapchain;
fn deref(&self) -> &Self::Target {
&self.functions
}
}
impl Drop for Swapchain {
fn drop(&mut self) {
#[cfg(feature = "log-objects")]
trace!("Destroying VkSwapchainKHR {:p}", self.handle);
self.images.clear();
unsafe {
self.functions.destroy_swapchain(self.handle, None);
}
}
}
fn choose_surface_format<Window: WindowInterface>(
settings: &AppSettings<Window>,
surface: &Surface,
) -> Result<vk::SurfaceFormatKHR> {
const FALLBACK_FORMAT: vk::SurfaceFormatKHR = vk::SurfaceFormatKHR {
format: vk::Format::B8G8R8A8_SRGB,
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
};
if let Some(preferred_format) = settings.surface_format {
if surface.formats().contains(&preferred_format) {
return Ok(preferred_format);
}
}
if surface.formats().contains(&FALLBACK_FORMAT) {
return Ok(FALLBACK_FORMAT);
}
surface
.formats()
.first()
.copied()
.ok_or_else(|| anyhow::Error::from(Error::NoSurfaceFormat))
}
fn choose_present_mode<Window: WindowInterface>(
settings: &AppSettings<Window>,
surface: &Surface,
) -> vk::PresentModeKHR {
if let Some(mode) = settings.present_mode {
if surface.present_modes().contains(&mode) {
return mode;
}
}
vk::PresentModeKHR::FIFO
}
fn choose_swapchain_extent<Window: WindowInterface>(
settings: &AppSettings<Window>,
surface: &Surface,
) -> vk::Extent2D {
if surface.capabilities().current_extent.width != u32::MAX {
return surface.capabilities().current_extent;
}
vk::Extent2D {
width: settings.window.unwrap().width().clamp(
surface.capabilities().min_image_extent.width,
surface.capabilities().max_image_extent.width,
),
height: settings.window.unwrap().height().clamp(
surface.capabilities().min_image_extent.height,
surface.capabilities().max_image_extent.height,
),
}
}