use std::{
error::Error,
sync::{Arc, Mutex},
};
use ash::vk;
use crate::{
device::{Device, queue::Queue},
image::Image,
};
pub(crate) struct SwapchainInfo {
queue_family_indices: [u32; 2],
extent: vk::Extent2D,
present_mode: vk::PresentModeKHR,
vsync: bool,
pub image_count: u32,
pub surface_format: vk::SurfaceFormatKHR,
}
unsafe impl Send for SwapchainInfo {}
impl SwapchainInfo {
fn new(
device: Arc<Device>,
extent: [u32; 2],
vsync: bool,
) -> Result<Arc<Self>, Box<dyn Error>> {
let queue_family_indices = [
match device
.physical_device
.info
.queue_families_info
.iter()
.find(|info| info.flags.intersects(vk::QueueFlags::GRAPHICS))
{
Some(info) => info.index,
None => {
return Err("device does not support graphics queue".into());
}
},
match device
.physical_device
.info
.queue_families_info
.iter()
.find(|info| info.present_support)
{
Some(info) => info.index,
None => {
return Err("device does not support presentation".into());
}
},
];
let swap_chain_support_details =
match device.physical_device.swap_chain_support_details.clone() {
Some(s) => s,
None => return Err("device has been created without surface support".into()),
};
let swap_surface_format = match swap_chain_support_details.formats.iter().find(|format| {
format.format == vk::Format::B8G8R8A8_UNORM
&& format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR
}) {
Some(&format) => format,
None => {
return Err("not found required swap surface format".into());
}
};
let swap_extent = vk::Extent2D {
width: extent[0],
height: extent[1],
};
let image_count = {
let max_image_count = swap_chain_support_details.capabilities.max_image_count;
let min_image_count = swap_chain_support_details.capabilities.min_image_count;
if max_image_count > 0 {
max_image_count
} else {
min_image_count
}
};
let swap_present_mode = match swap_chain_support_details
.present_modes
.iter()
.find(|&&mode| mode == vk::PresentModeKHR::MAILBOX)
{
Some(&mode) => mode,
None => {
if vsync {
vk::PresentModeKHR::FIFO
} else {
vk::PresentModeKHR::IMMEDIATE
}
}
};
Ok(Arc::new(Self {
queue_family_indices,
vsync,
surface_format: swap_surface_format,
extent: swap_extent,
present_mode: swap_present_mode,
image_count,
}))
}
}
pub struct Swapchain {
pub(crate) swapchain_info: Arc<SwapchainInfo>,
pub(crate) swapchain: ash::khr::swapchain::Device,
pub(crate) swapchain_khr: vk::SwapchainKHR,
pub image_sequence: Vec<Arc<Image>>,
pub present_queue: Arc<Mutex<Queue>>,
}
unsafe impl Send for Swapchain {}
unsafe impl Sync for Swapchain {}
impl Drop for Swapchain {
fn drop(&mut self) {
unsafe {
let mut lock = self.present_queue.lock().unwrap();
lock.wait_idle().unwrap();
self.swapchain.destroy_swapchain(self.swapchain_khr, None);
}
}
}
impl Swapchain {
pub fn from_old(old: Arc<Self>, extent: [u32; 2]) -> Result<Arc<Self>, Box<dyn Error>> {
let device = old.present_queue.lock().unwrap().device.clone();
let swapchain_info = SwapchainInfo::new(device.clone(), extent, old.swapchain_info.vsync)?;
let self_in = Self::new_in(old.present_queue.clone(), swapchain_info, old.swapchain_khr)?;
device
.surface
.as_ref()
.unwrap()
.swapchain
.set(Some(self_in.clone()));
Ok(self_in)
}
pub fn new(
present_queue: Arc<Mutex<Queue>>,
extent: [u32; 2],
vsync: bool,
) -> Result<Arc<Self>, Box<dyn Error>> {
let device = present_queue.lock().unwrap().device.clone();
let swapchain_info = SwapchainInfo::new(device.clone(), extent, vsync)?;
device.surface.as_ref().unwrap().swapchain.set(None);
let self_in = Self::new_in(present_queue, swapchain_info, vk::SwapchainKHR::null())?;
device
.surface
.as_ref()
.unwrap()
.swapchain
.set(Some(self_in.clone()));
Ok(self_in)
}
fn new_in(
present_queue: Arc<Mutex<Queue>>,
swapchain_info: Arc<SwapchainInfo>,
old_swapchain: vk::SwapchainKHR,
) -> Result<Arc<Self>, Box<dyn Error>> {
let device = present_queue.lock().unwrap().device.clone();
let swapchain = ash::khr::swapchain::Device::new(&device.instance.handle, &device.handle);
let mut compression_control = vk::ImageCompressionControlEXT::default()
.flags(vk::ImageCompressionFlagsEXT::FIXED_RATE_DEFAULT);
let support_details = device
.physical_device
.swap_chain_support_details
.clone()
.unwrap();
let mut swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
.surface(device.surface.as_ref().unwrap().clone().surface_khr)
.min_image_count(swapchain_info.image_count)
.image_format(swapchain_info.surface_format.format)
.image_color_space(swapchain_info.surface_format.color_space)
.image_extent(swapchain_info.extent)
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST)
.pre_transform(support_details.capabilities.current_transform)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.present_mode(swapchain_info.present_mode)
.clipped(true)
.old_swapchain(old_swapchain);
if swapchain_info.queue_family_indices[0] != swapchain_info.queue_family_indices[1] {
swapchain_create_info = swapchain_create_info
.image_sharing_mode(vk::SharingMode::CONCURRENT)
.queue_family_indices(&swapchain_info.queue_family_indices)
}
if device.extensions.iter().any(|ext| {
ext.as_str()
== vk::EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_NAME
.to_str()
.unwrap()
}) {
swapchain_create_info = swapchain_create_info.push_next(&mut compression_control);
}
let swapchain_khr = unsafe { swapchain.create_swapchain(&swapchain_create_info, None) }?;
let swapchain_images = unsafe { swapchain.get_swapchain_images(swapchain_khr) }?;
let mut images = vec![];
for &swapchain_image in &swapchain_images {
let create_info = vk::ImageViewCreateInfo::default()
.image(swapchain_image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(swapchain_info.surface_format.format)
.components(vk::ComponentMapping {
r: vk::ComponentSwizzle::IDENTITY,
g: vk::ComponentSwizzle::IDENTITY,
b: vk::ComponentSwizzle::IDENTITY,
a: vk::ComponentSwizzle::IDENTITY,
})
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1),
);
let image_view = unsafe { device.handle.create_image_view(&create_info, None) }?;
images.push(Image::new_swapchain(
device.clone(),
image_view,
[swapchain_info.extent.width, swapchain_info.extent.height],
swapchain_info.surface_format.format,
));
}
Ok(Arc::new(Swapchain {
swapchain_info,
swapchain,
swapchain_khr,
image_sequence: images,
present_queue,
}))
}
}