gravitron 0.1.2

A GameEngine based on an ECS and Vulkan
Documentation
use ash::{khr, vk};
use gpu_allocator::vulkan;

use crate::{config::app::AppConfig, vulkan::{device::QueueFamilies, surface::Surface}};

pub(crate) struct SwapChain {
  loader: khr::swapchain::Device,
  swapchain: vk::SwapchainKHR,
  images: Vec<vk::Image>,
  image_views: Vec<vk::ImageView>,
  depth_image: vk::Image,
  depth_image_allocation: vulkan::Allocation,
  depth_image_view: vk::ImageView,
  frame_buffers: Vec<vk::Framebuffer>,
  //surface_format: vk::SurfaceFormatKHR,
  extent: vk::Extent2D,
  image_available: Vec<vk::Semaphore>,
  render_finished: Vec<vk::Semaphore>,
  may_begin_drawing: Vec<vk::Fence>,
  amount_of_images: u32,
  current_image: usize,
}

impl SwapChain {
  pub(crate) fn init(
    instance: &ash::Instance,
    physical_device: vk::PhysicalDevice,
    logical_device: &ash::Device,
    surfaces: &Surface,
    queue_families: &QueueFamilies,
    allocator: &mut vulkan::Allocator,
    config: &AppConfig,
  ) -> Result<Self, vk::Result> {
    let surface_capabilities = surfaces.get_capabilities(physical_device)?;
    //let surface_present_modes = surfaces.get_present_modes(physical_device)?;
    let surface_format = *surfaces.get_formats(physical_device)?.first().unwrap();

    let mut extent = surface_capabilities.current_extent;
    if extent.width == u32::MAX || extent.height == u32::MAX {
      extent.width = config.width;
      extent.height = config.height;
    }

    let queue_families = [queue_families.get_graphics_q_index()];
    let image_count =
      if surface_capabilities.min_image_count <= surface_capabilities.max_image_count {
        3.max(surface_capabilities.min_image_count)
          .min(surface_capabilities.max_image_count)
      } else {
        surface_capabilities.min_image_count
      };
    let swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
      .surface(surfaces.get_surface())
      .min_image_count(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 | vk::ImageUsageFlags::TRANSFER_SRC)
      .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
      .queue_family_indices(&queue_families)
      .pre_transform(surface_capabilities.current_transform)
      .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
      .present_mode(vk::PresentModeKHR::MAILBOX);

    let swapchain_loader = khr::swapchain::Device::new(instance, logical_device);
    let swapchain = unsafe { swapchain_loader.create_swapchain(&swapchain_create_info, None) }?;

    let swapchain_images = unsafe { swapchain_loader.get_swapchain_images(swapchain) }?;
    let amount_of_images = swapchain_images.len() as u32;

    let mut swapchain_image_views = Vec::with_capacity(swapchain_images.len());
    for image in &swapchain_images {
      let 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_create_info = vk::ImageViewCreateInfo::default()
        .image(*image)
        .view_type(vk::ImageViewType::TYPE_2D)
        .format(surface_format.format)
        .subresource_range(subresource_range);
      let image_view = unsafe { logical_device.create_image_view(&image_view_create_info, None) }?;
      swapchain_image_views.push(image_view);
    }

    let extend_3d = vk::Extent3D {
      width: extent.width,
      height: extent.height,
      depth: 1,
    };
    let depth_image_create_info = vk::ImageCreateInfo::default()
      .image_type(vk::ImageType::TYPE_2D)
      .format(vk::Format::D32_SFLOAT)
      .extent(extend_3d)
      .mip_levels(1)
      .array_layers(1)
      .samples(vk::SampleCountFlags::TYPE_1)
      .tiling(vk::ImageTiling::OPTIMAL)
      .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
      .sharing_mode(vk::SharingMode::EXCLUSIVE)
      .queue_family_indices(&queue_families);

    let depth_image = unsafe { logical_device.create_image(&depth_image_create_info, None) }?;
    let requirements = unsafe { logical_device.get_image_memory_requirements(depth_image) };
    let allocation_create_desc = vulkan::AllocationCreateDesc {
      requirements,
      location: gpu_allocator::MemoryLocation::GpuOnly,
      linear: true,
      allocation_scheme: vulkan::AllocationScheme::GpuAllocatorManaged,
      name: "Depth Image",
    };
    let depth_image_allocation = allocator.allocate(&allocation_create_desc).unwrap();
    unsafe {
      logical_device.bind_image_memory(
        depth_image,
        depth_image_allocation.memory(),
        depth_image_allocation.offset(),
      )
    }?;

    let subresource_range = vk::ImageSubresourceRange::default()
      .aspect_mask(vk::ImageAspectFlags::DEPTH)
      .base_mip_level(0)
      .level_count(1)
      .base_array_layer(0)
      .layer_count(1);
    let depth_image_view_create_info = vk::ImageViewCreateInfo::default()
      .image(depth_image)
      .view_type(vk::ImageViewType::TYPE_2D)
      .format(vk::Format::D32_SFLOAT)
      .subresource_range(subresource_range);
    let depth_image_view =
      unsafe { logical_device.create_image_view(&depth_image_view_create_info, None) }?;

    let mut image_available = Vec::new();
    let mut render_finished = Vec::new();
    let mut may_begin_drawing = Vec::new();
    let semaphore_create_info = vk::SemaphoreCreateInfo::default();
    let fence_create_info = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED);
    for _ in 0..amount_of_images {
      let image_available_semaphore =
        unsafe { logical_device.create_semaphore(&semaphore_create_info, None) }?;
      let render_finished_semaphore =
        unsafe { logical_device.create_semaphore(&semaphore_create_info, None) }?;
      image_available.push(image_available_semaphore);
      render_finished.push(render_finished_semaphore);
      let fence = unsafe { logical_device.create_fence(&fence_create_info, None) }?;
      may_begin_drawing.push(fence);
    }

    Ok(Self {
      loader: swapchain_loader,
      swapchain,
      images: swapchain_images,
      image_views: swapchain_image_views,
      frame_buffers: Vec::new(),
      //surface_format,
      extent,
      image_available,
      render_finished,
      amount_of_images,
      current_image: 0,
      may_begin_drawing,
      depth_image,
      depth_image_allocation,
      depth_image_view,
    })
  }

  pub(crate) fn get_extent(&self) -> vk::Extent2D {
    self.extent
  }

  pub(crate) fn destroy(
    &mut self,
    logical_device: &ash::Device,
    allocator: &mut vulkan::Allocator,
  ) {
    unsafe {
      logical_device.destroy_image_view(self.depth_image_view, None);
      logical_device.destroy_image(self.depth_image, None);
      allocator
        .free(std::mem::take(&mut self.depth_image_allocation))
        .unwrap();

      for fence in &self.may_begin_drawing {
        logical_device.destroy_fence(*fence, None);
      }
      for semaphore in &self.image_available {
        logical_device.destroy_semaphore(*semaphore, None);
      }
      for semaphore in &self.render_finished {
        logical_device.destroy_semaphore(*semaphore, None);
      }
      for frame_buffer in &self.frame_buffers {
        logical_device.destroy_framebuffer(*frame_buffer, None);
      }
      for image_view in &self.image_views {
        logical_device.destroy_image_view(*image_view, None);
      }
      self.loader.destroy_swapchain(self.swapchain, None);
    }
  }

  pub(crate) fn create_frame_buffers(
    &mut self,
    logical_device: &ash::Device,
    render_pass: vk::RenderPass,
  ) -> Result<(), vk::Result> {
    for image_view in &self.image_views {
      let vi = [*image_view, self.depth_image_view];
      let frame_buffer_create_info = vk::FramebufferCreateInfo::default()
        .render_pass(render_pass)
        .attachments(&vi)
        .width(self.extent.width)
        .height(self.extent.height)
        .layers(1);
      let frame_buffer =
        unsafe { logical_device.create_framebuffer(&frame_buffer_create_info, None) }?;
      self.frame_buffers.push(frame_buffer);
    }
    Ok(())
  }
}