miel 0.1.0

A simple rendering framework leveraging the Vulkan API
Documentation
use ash::{khr, vk};
use thiserror::Error;

use crate::utils::ThreadSafeRef;

use super::{
    allocator::Allocator,
    device::Device,
    image::{Image, ImageBuildError, ImageCreateInfo},
    instance::Instance,
    surface::Surface,
};

#[allow(dead_code)]
pub(crate) struct SwapchainImage {
    pub handle: vk::Image,
    pub view: vk::ImageView,
}

#[allow(dead_code)]
pub(crate) struct Swapchain {
    pub handle: vk::SwapchainKHR,
    pub loader: khr::swapchain::Device,

    pub extent: vk::Extent2D,
    pub images: Vec<SwapchainImage>,
    pub depth_image: Image,

    // bookkeeping
    device_ref: ThreadSafeRef<Device>,
}

#[derive(Debug, Error)]
pub enum SwapchainCreateError {
    #[error("vulkan call to create the swapchain failed")]
    VulkanCreation(vk::Result),

    #[error("vulkan call to fetch swapchain images failed")]
    ImageFetching(vk::Result),

    #[error("vulkan call to create swapchain image views failed")]
    ImageViewCreation(vk::Result),

    #[error("depth image building failed")]
    DepthImageBuilding(ImageBuildError),
}

impl Swapchain {
    pub fn create(
        instance: &Instance,
        device_ref: ThreadSafeRef<Device>,
        surface: &Surface,
        suggested_size: vk::Extent2D,
        allocator_ref: ThreadSafeRef<Allocator>,
    ) -> Result<Self, SwapchainCreateError> {
        let device = device_ref.lock();
        let loader = khr::swapchain::Device::new(instance, &device);

        let mut min_image_count = surface.capabilities.min_image_count + 1;
        if surface.capabilities.max_image_count > 0
            && min_image_count > surface.capabilities.max_image_count
        {
            min_image_count = surface.capabilities.max_image_count;
        }

        let extent = match surface.capabilities.current_extent {
            vk::Extent2D {
                width: u32::MAX,
                height: u32::MAX,
            } => suggested_size,
            _ => surface.capabilities.current_extent,
        };

        let create_info = vk::SwapchainCreateInfoKHR::default()
            .surface(surface.handle)
            .min_image_count(min_image_count)
            .image_format(surface.format.format)
            .image_color_space(surface.format.color_space)
            .image_extent(extent)
            .image_array_layers(1)
            .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
            .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
            .pre_transform(surface.capabilities.current_transform)
            .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
            .present_mode(surface.present_mode)
            .clipped(true);

        let handle = unsafe { loader.create_swapchain(&create_info, None) }
            .map_err(SwapchainCreateError::VulkanCreation)?;

        let images_handles = unsafe { loader.get_swapchain_images(handle) }
            .map_err(SwapchainCreateError::ImageFetching)?;
        let image_view_create_info = vk::ImageViewCreateInfo::default()
            .view_type(vk::ImageViewType::TYPE_2D)
            .format(surface.format.format)
            .components(
                vk::ComponentMapping::default()
                    .r(vk::ComponentSwizzle::R)
                    .g(vk::ComponentSwizzle::G)
                    .b(vk::ComponentSwizzle::B)
                    .a(vk::ComponentSwizzle::A),
            )
            .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 images = images_handles
            .into_iter()
            .map(|handle| {
                let image_view_create_info = image_view_create_info.image(handle);
                let view = unsafe { device.create_image_view(&image_view_create_info, None) }
                    .map_err(SwapchainCreateError::ImageViewCreation)?;

                Ok(SwapchainImage { handle, view })
            })
            .collect::<Result<Vec<_>, _>>()?;
        drop(device);

        let depth_extent = vk::Extent3D {
            width: extent.width,
            height: extent.height,
            depth: 1,
        };
        let depth_image = ImageCreateInfo::swapchain_depth_image(depth_extent)
            .build_from_base_structs(device_ref.clone(), allocator_ref)
            .map_err(SwapchainCreateError::DepthImageBuilding)?;

        Ok(Self {
            handle,
            loader,
            extent,
            images,
            depth_image,
            device_ref,
        })
    }
}

impl Drop for Swapchain {
    fn drop(&mut self) {
        let device = self.device_ref.lock();
        log::debug!("Waiting for device to be idle before destroying swapchain");
        unsafe { device.device_wait_idle() }.expect("device should wait before shutting down");

        log::debug!("destroying swapchain");
        for image in &self.images {
            unsafe {
                device.destroy_image_view(image.view, None);
            };
        }
        unsafe {
            self.loader.destroy_swapchain(self.handle, None);
        };
    }
}