use crate::context::SharedVulkanContext;
use crate::traits::*;
use crate::ImageUsage;
use crate::{Error, Result};
use ash::extensions::khr::Surface;
pub use ash::extensions::khr::Swapchain as SwapchainLoader;
use ash::vk::Extent2D;
use ash::vk::{self, Image, SurfaceKHR};
use ash::Device;
use ash::Instance;
use ivy_base::Extent;
use std::cmp;
pub const MAX_FRAMES: usize = 5;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SwapchainInfo {
pub present_mode: vk::PresentModeKHR,
pub format: vk::SurfaceFormatKHR,
pub image_count: u32,
pub usage: ImageUsage,
}
impl Default for SwapchainInfo {
fn default() -> Self {
Self {
present_mode: vk::PresentModeKHR::MAILBOX,
format: vk::SurfaceFormatKHR {
format: vk::Format::B8G8R8A8_SRGB,
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
},
image_count: 2,
usage: ImageUsage::TRANSFER_DST | ImageUsage::COLOR_ATTACHMENT | ImageUsage::SAMPLED,
}
}
}
#[derive(Debug)]
pub(crate) struct SwapchainSupport {
pub capabilities: vk::SurfaceCapabilitiesKHR,
pub formats: Vec<vk::SurfaceFormatKHR>,
pub present_modes: Vec<vk::PresentModeKHR>,
}
pub(crate) fn query_support(
surface_loader: &Surface,
surface: SurfaceKHR,
physical_device: vk::PhysicalDevice,
) -> Result<SwapchainSupport> {
let capabilities = unsafe {
surface_loader.get_physical_device_surface_capabilities(physical_device, surface)?
};
let formats =
unsafe { surface_loader.get_physical_device_surface_formats(physical_device, surface)? };
let present_modes = unsafe {
surface_loader.get_physical_device_surface_present_modes(physical_device, surface)?
};
Ok(SwapchainSupport {
capabilities,
formats,
present_modes,
})
}
fn pick_format(
formats: &[vk::SurfaceFormatKHR],
preferred_format: vk::SurfaceFormatKHR,
) -> vk::SurfaceFormatKHR {
for surface_format in formats {
if *surface_format == preferred_format {
return *surface_format;
}
}
formats[0]
}
fn pick_present_mode(
modes: &[vk::PresentModeKHR],
preferred: vk::PresentModeKHR,
) -> vk::PresentModeKHR {
for mode in modes {
if *mode == preferred {
return *mode;
}
}
vk::PresentModeKHR::FIFO
}
fn pick_extent(desired_extent: Extent, capabilities: &vk::SurfaceCapabilitiesKHR) -> Extent {
if capabilities.current_extent.width != std::u32::MAX {
return capabilities.current_extent.into_extent();
}
let width = cmp::max(
capabilities.min_image_extent.width,
cmp::min(
capabilities.max_image_extent.width,
desired_extent.width as u32,
),
);
let height = cmp::max(
capabilities.min_image_extent.height,
cmp::min(
capabilities.max_image_extent.height,
desired_extent.height as u32,
),
);
(width, height).into()
}
pub struct Swapchain {
support: SwapchainSupport,
context: SharedVulkanContext,
swapchain: vk::SwapchainKHR,
images: Vec<Image>,
extent: Extent,
image_index: Option<u32>,
surface_format: vk::SurfaceFormatKHR,
info: SwapchainInfo,
}
impl Swapchain {
pub fn new<T: Backend>(
context: SharedVulkanContext,
window: &T,
info: SwapchainInfo,
) -> Result<Self> {
let support = query_support(
context.surface_loader(),
context.surface().unwrap(),
context.physical_device(),
)?;
let mut image_count = info.image_count.max(support.capabilities.min_image_count);
if support.capabilities.max_image_count != 0 {
image_count = cmp::min(image_count, support.capabilities.max_image_count);
}
let queue_family_indices = [
context.queue_families().graphics().unwrap(),
context.queue_families().present().unwrap(),
];
let (sharing_mode, queue_family_indices): (vk::SharingMode, &[u32]) =
if context.queue_families().graphics() == context.queue_families().present() {
(vk::SharingMode::EXCLUSIVE, &[])
} else {
(vk::SharingMode::CONCURRENT, &queue_family_indices)
};
let surface_format = pick_format(&support.formats, info.format);
let present_mode = pick_present_mode(&support.present_modes, info.present_mode);
let extent = window.framebuffer_size();
let extent = pick_extent(extent, &support.capabilities);
let create_info = vk::SwapchainCreateInfoKHR::builder()
.surface(context.surface().unwrap())
.min_image_count(image_count)
.image_format(surface_format.format)
.image_color_space(surface_format.color_space)
.image_extent(Extent2D::from_extent(extent))
.image_array_layers(1)
.image_usage(info.usage)
.image_sharing_mode(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());
let swapchain_loader = context.swapchain_loader();
let swapchain = unsafe { swapchain_loader.create_swapchain(&create_info, None)? };
let images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? };
Ok(Swapchain {
support,
context,
swapchain,
images,
extent,
surface_format,
image_index: None,
info,
})
}
pub fn recreate(&mut self, extent: Extent) -> Result<()> {
let context = &self.context;
let support = &self.support;
let info = self.info;
let image_count = self.image_count();
let queue_family_indices = [
context.queue_families().graphics().unwrap(),
context.queue_families().present().unwrap(),
];
let (sharing_mode, queue_family_indices): (vk::SharingMode, &[u32]) =
if context.queue_families().graphics() == context.queue_families().present() {
(vk::SharingMode::EXCLUSIVE, &[])
} else {
(vk::SharingMode::CONCURRENT, &queue_family_indices)
};
let surface_format = pick_format(&support.formats, info.format);
let present_mode = pick_present_mode(&support.present_modes, info.present_mode);
let extent = pick_extent(extent, &support.capabilities);
let create_info = vk::SwapchainCreateInfoKHR::builder()
.surface(context.surface().unwrap())
.min_image_count(image_count as u32)
.image_format(surface_format.format)
.image_color_space(surface_format.color_space)
.image_extent(Extent2D::from_extent(extent))
.image_array_layers(1)
.image_usage(info.usage)
.image_sharing_mode(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(self.swapchain);
let swapchain_loader = context.swapchain_loader();
let swapchain = unsafe { swapchain_loader.create_swapchain(&create_info, None)? };
let images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? };
self.swapchain = swapchain;
self.images = images;
self.image_index = None;
self.extent = extent;
Ok(())
}
pub fn acquire_next_image(&mut self, semaphore: vk::Semaphore) -> Result<u32> {
self.image_index = None;
let (image_index, _) = unsafe {
self.context.swapchain_loader().acquire_next_image(
self.swapchain,
std::u64::MAX,
semaphore,
vk::Fence::null(),
)?
};
self.image_index = Some(image_index);
Ok(image_index)
}
pub fn present(&self, queue: vk::Queue, wait_semaphores: &[vk::Semaphore]) -> Result<bool> {
let present_info = vk::PresentInfoKHR {
s_type: vk::StructureType::PRESENT_INFO_KHR,
p_next: std::ptr::null(),
wait_semaphore_count: wait_semaphores.len() as _,
p_wait_semaphores: wait_semaphores.as_ptr(),
swapchain_count: 1,
p_swapchains: &self.swapchain,
p_image_indices: &self.image_index()?,
p_results: std::ptr::null_mut(),
};
let suboptimal = unsafe {
self.context
.swapchain_loader()
.queue_present(queue, &present_info)?
};
Ok(suboptimal)
}
pub fn image_index(&self) -> Result<u32> {
self.image_index.ok_or(Error::NoCurrentSwapchainImage)
}
pub fn image_count(&self) -> usize {
self.images.len()
}
pub fn image_format(&self) -> vk::Format {
self.surface_format.format
}
pub fn surface_format(&self) -> vk::SurfaceFormatKHR {
self.surface_format
}
pub fn extent(&self) -> Extent {
self.extent
}
pub fn image(&self, index: usize) -> Image {
self.images[index]
}
pub fn images(&self) -> &Vec<Image> {
&self.images
}
pub fn create_loader(instance: &Instance, device: &Device) -> SwapchainLoader {
SwapchainLoader::new(instance, device)
}
pub fn context(&self) -> &SharedVulkanContext {
&self.context
}
}
impl Drop for Swapchain {
fn drop(&mut self) {
unsafe {
self.context
.swapchain_loader()
.destroy_swapchain(self.swapchain, None);
};
}
}