use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::Hasher;
use std::sync::Arc;
use erupt::vk;
#[cfg(feature = "smallvec")]
use smallvec::SmallVec;
#[cfg(feature = "tracing")]
use tracing::{error, info};
use crate::context::Context;
use crate::{
AscheError, BinarySemaphore, GraphicsQueue, ImageView, RenderPass,
RenderPassColorAttachmentDescriptor, RenderPassDepthAttachmentDescriptor, Result,
};
#[derive(Debug)]
pub struct SwapchainFrame {
pub index: u32,
pub view: vk::ImageView,
}
#[derive(Debug)]
pub struct Swapchain {
framebuffers: HashMap<u64, vk::Framebuffer>,
graphic_queue_family_index: u32,
presentation_mode: vk::PresentModeKHR,
size: Option<u32>,
swapchain: Option<SwapchainInner>,
format: vk::Format,
color_space: vk::ColorSpaceKHR,
context: Arc<Context>,
}
impl Swapchain {
pub(crate) fn new(
context: Arc<Context>,
graphic_queue_family_index: u32,
presentation_mode: vk::PresentModeKHR,
format: vk::Format,
color_space: vk::ColorSpaceKHR,
) -> Result<Self> {
let mut swapchain = Self {
framebuffers: HashMap::with_capacity(3),
graphic_queue_family_index,
presentation_mode,
size: None,
swapchain: None,
format,
color_space,
context,
};
swapchain.recreate(None)?;
Ok(swapchain)
}
pub fn recreate(&mut self, window_extend: Option<vk::Extent2D>) -> Result<()> {
self.destroy_framebuffer();
#[cfg(feature = "tracing")]
info!(
"Creating swapchain with format {:?} and color space {:?}",
self.format, self.color_space
);
let formats = self.query_formats()?;
let capabilities = self.query_surface_capabilities()?;
let presentation_mode = self.query_presentation_mode()?;
let mut image_count = capabilities.min_image_count + 1;
if capabilities.max_image_count > 0 && image_count > capabilities.max_image_count {
image_count = capabilities.max_image_count;
}
let extent = match capabilities.current_extent.width {
u32::MAX => window_extend.unwrap_or_default(),
_ => capabilities.current_extent,
};
let pre_transform = if capabilities
.supported_transforms
.contains(vk::SurfaceTransformFlagsKHR::IDENTITY_KHR)
{
vk::SurfaceTransformFlagBitsKHR::IDENTITY_KHR
} else {
capabilities.current_transform
};
let format = formats
.iter()
.find(|f| f.format == self.format && f.color_space == self.color_space)
.ok_or(AscheError::SwapchainFormatIncompatible)?;
let presentation_mode = *presentation_mode
.iter()
.find(|m| **m == self.presentation_mode)
.ok_or(AscheError::PresentationModeUnsupported)?;
let old_swapchain = self.swapchain.take();
let swapchain = SwapchainInner::new(
self.context.clone(),
SwapchainDescriptor {
graphic_queue_family_index: self.graphic_queue_family_index,
extent,
pre_transform,
format: format.format,
color_space: format.color_space,
presentation_mode,
image_count,
},
old_swapchain,
)?;
#[cfg(feature = "tracing")]
info!("Swapchain has {} image(s)", image_count);
self.swapchain.replace(swapchain);
Ok(())
}
fn query_formats(&self) -> Result<Vec<vk::SurfaceFormatKHR>> {
unsafe {
self.context
.instance
.raw
.get_physical_device_surface_formats_khr(
self.context.physical_device,
self.context.instance.surface,
None,
)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to get the physical device surface formats: {}", err);
AscheError::VkResult(err)
})
}
fn query_surface_capabilities(&self) -> Result<vk::SurfaceCapabilitiesKHR> {
unsafe {
self.context
.instance
.raw
.get_physical_device_surface_capabilities_khr(
self.context.physical_device,
self.context.instance.surface,
None,
)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!(
"Unable to get the physical device surface capabilities: {}",
err
);
AscheError::VkResult(err)
})
}
fn query_presentation_mode(&self) -> Result<Vec<vk::PresentModeKHR>> {
unsafe {
self.context
.instance
.raw
.get_physical_device_surface_present_modes_khr(
self.context.physical_device,
self.context.instance.surface,
None,
)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to get the physical device surface modes: {}", err);
AscheError::VkResult(err)
})
}
pub fn frame_count(&self) -> Result<u32> {
let capabilities = self.query_surface_capabilities()?;
let mut image_count = capabilities.min_image_count + 1;
if capabilities.max_image_count > 0 && image_count > capabilities.max_image_count {
image_count = capabilities.max_image_count;
}
Ok(image_count)
}
pub fn next_frame(&self, signal_semaphore: &BinarySemaphore) -> Result<SwapchainFrame> {
self.swapchain
.as_ref()
.ok_or(AscheError::SwapchainNotInitialized)?
.get_next_frame(signal_semaphore)
}
pub fn queue_frame(
&self,
graphics_queue: &GraphicsQueue,
frame: SwapchainFrame,
wait_semaphores: &[&BinarySemaphore],
) -> Result<()> {
self.swapchain
.as_ref()
.ok_or(AscheError::SwapchainNotInitialized)?
.queue_frame(frame, graphics_queue.raw, wait_semaphores)
}
pub(crate) fn next_framebuffer(
&mut self,
render_pass: &RenderPass,
color_attachments: &[RenderPassColorAttachmentDescriptor],
depth_attachment: &Option<RenderPassDepthAttachmentDescriptor>,
extent: vk::Extent2D,
) -> Result<vk::Framebuffer> {
let mut hasher = DefaultHasher::new();
hasher.write_u64(render_pass.raw.0);
for color_attachment in color_attachments {
hasher.write_u64(color_attachment.attachment.0);
}
if let Some(depth_attachment) = depth_attachment {
hasher.write_u64(depth_attachment.attachment.0);
}
let hash = hasher.finish();
let mut created = false;
let framebuffer = if let Some(framebuffer) = self.framebuffers.get(&hash) {
*framebuffer
} else {
created = true;
self.create_framebuffer(render_pass, color_attachments, depth_attachment, extent)?
};
if created {
self.framebuffers.insert(hash, framebuffer);
}
Ok(framebuffer)
}
fn create_framebuffer(
&self,
render_pass: &RenderPass,
color_attachments: &[RenderPassColorAttachmentDescriptor],
depth_attachment: &Option<RenderPassDepthAttachmentDescriptor>,
extent: vk::Extent2D,
) -> Result<vk::Framebuffer> {
let attachments = color_attachments
.iter()
.map(|x| x.attachment)
.chain(depth_attachment.iter().map(|x| x.attachment));
#[cfg(feature = "smallvec")]
let attachments = attachments.collect::<SmallVec<[vk::ImageView; 4]>>();
#[cfg(not(feature = "smallvec"))]
let attachments = attachments.collect::<Vec<vk::ImageView>>();
let framebuffer_info = vk::FramebufferCreateInfoBuilder::new()
.render_pass(render_pass.raw)
.attachments(&attachments)
.width(extent.width)
.height(extent.height)
.layers(1);
let framebuffer = unsafe {
self.context
.device
.create_framebuffer(&framebuffer_info, None, None)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to create a frame buffer: {}", err);
AscheError::VkResult(err)
})?;
Ok(framebuffer)
}
fn destroy_framebuffer(&mut self) {
for (_, framebuffer) in self.framebuffers.drain() {
unsafe {
self.context
.device
.destroy_framebuffer(Some(framebuffer), None);
}
}
}
}
impl Drop for Swapchain {
fn drop(&mut self) {
self.destroy_framebuffer();
}
}
#[derive(Debug)]
pub struct SwapchainInner {
present_complete_semaphore: vk::Semaphore,
image_views: Vec<ImageView>,
raw: vk::SwapchainKHR,
context: Arc<Context>,
}
#[derive(Clone, Debug)]
struct SwapchainDescriptor {
graphic_queue_family_index: u32,
extent: vk::Extent2D,
pre_transform: vk::SurfaceTransformFlagBitsKHR,
format: vk::Format,
color_space: vk::ColorSpaceKHR,
presentation_mode: vk::PresentModeKHR,
image_count: u32,
}
impl SwapchainInner {
fn new(
context: Arc<Context>,
descriptor: SwapchainDescriptor,
old_swapchain: Option<SwapchainInner>,
) -> Result<Self> {
let old_swapchain = match old_swapchain {
Some(mut osc) => {
let swapchain = osc.raw;
Self::destroy_resources(&context.device, &mut osc.present_complete_semaphore);
osc.raw = vk::SwapchainKHR::null();
swapchain
}
None => vk::SwapchainKHR::null(),
};
let graphic_family_index = &[descriptor.graphic_queue_family_index];
let swapchain_create_info = vk::SwapchainCreateInfoKHRBuilder::new()
.surface(context.instance.surface)
.min_image_count(descriptor.image_count)
.image_format(descriptor.format)
.image_color_space(descriptor.color_space)
.image_extent(descriptor.extent)
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.queue_family_indices(graphic_family_index)
.pre_transform(descriptor.pre_transform)
.composite_alpha(vk::CompositeAlphaFlagBitsKHR::OPAQUE_KHR)
.present_mode(descriptor.presentation_mode)
.old_swapchain(old_swapchain)
.clipped(true);
let swapchain = unsafe {
context
.device
.create_swapchain_khr(&swapchain_create_info, None, None)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to create a swapchain: {}", err);
AscheError::VkResult(err)
})?;
let images =
unsafe { context.device.get_swapchain_images_khr(swapchain, None) }.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to get the swapchain images: {}", err);
AscheError::VkResult(err)
})?;
let image_views =
SwapchainInner::create_image_views(&context, &images, descriptor.format, images.len())?;
let semaphore_create_info = vk::SemaphoreCreateInfo::default();
let present_complete_semaphore = unsafe {
context
.device
.create_semaphore(&semaphore_create_info, None, None)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to create the presentation semaphore: {}", err);
AscheError::VkResult(err)
})?;
Ok(Self {
context,
raw: swapchain,
image_views,
present_complete_semaphore,
})
}
fn get_next_frame(&self, signal_semaphore: &BinarySemaphore) -> Result<SwapchainFrame> {
let info = vk::AcquireNextImageInfoKHRBuilder::new()
.semaphore(signal_semaphore.raw)
.device_mask(1)
.swapchain(self.raw)
.timeout(u64::MAX);
let index =
unsafe { self.context.device.acquire_next_image2_khr(&info, None) }.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to acquire the next frame image: {}", err);
AscheError::VkResult(err)
})?;
let view = self.image_views[index as usize].raw;
Ok(SwapchainFrame { index, view })
}
fn queue_frame(
&self,
frame: SwapchainFrame,
graphic_queue: vk::Queue,
wait_semaphores: &[&BinarySemaphore],
) -> Result<()> {
let wait_semaphores = wait_semaphores.iter().map(|s| s.raw);
#[cfg(feature = "smallvec")]
let wait_semaphores = wait_semaphores.collect::<SmallVec<[vk::Semaphore; 4]>>();
#[cfg(not(feature = "smallvec"))]
let wait_semaphores = wait_semaphores.collect::<Vec<vk::Semaphore>>();
let swapchains = [self.raw];
let image_indices = [frame.index];
let present_info = vk::PresentInfoKHRBuilder::new()
.wait_semaphores(&wait_semaphores)
.swapchains(&swapchains)
.image_indices(&image_indices);
unsafe {
self.context
.device
.queue_present_khr(graphic_queue, &present_info)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to queue the next frame: {}", err);
AscheError::VkResult(err)
})?;
Ok(())
}
fn create_image_views(
context: &Arc<Context>,
images: &[vk::Image],
format: vk::Format,
size: usize,
) -> Result<Vec<ImageView>> {
let mut image_views = Vec::with_capacity(size);
for image in images.iter() {
let imageview_create_info = vk::ImageViewCreateInfoBuilder::new()
.view_type(vk::ImageViewType::_2D)
.format(format)
.components(vk::ComponentMapping {
r: vk::ComponentSwizzle::R,
g: vk::ComponentSwizzle::G,
b: vk::ComponentSwizzle::B,
a: vk::ComponentSwizzle::A,
})
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
})
.image(*image);
let raw = unsafe {
context
.device
.create_image_view(&imageview_create_info, None, None)
}
.map_err(|err| {
#[cfg(feature = "tracing")]
error!("Unable to create a swapchain image view: {}", err);
AscheError::VkResult(err)
})?;
image_views.push(ImageView::new(raw, context.clone()));
}
Ok(image_views)
}
fn destroy_resources(
device: &erupt::DeviceLoader,
present_complete_semaphore: &mut vk::Semaphore,
) {
unsafe {
device.destroy_semaphore(Some(*present_complete_semaphore), None);
*present_complete_semaphore = vk::Semaphore::null();
};
}
}
impl Drop for SwapchainInner {
fn drop(&mut self) {
Self::destroy_resources(&self.context.device, &mut self.present_complete_semaphore);
unsafe {
self.context
.device
.destroy_swapchain_khr(Some(self.raw), None)
};
}
}