
.. _program_listing_file_Src_GraphicsEngineVulkan_vulkan_base_VulkanSwapChain.cpp:

Program Listing for File VulkanSwapChain.cpp
============================================

|exhale_lsh| :ref:`Return to documentation for file <file_Src_GraphicsEngineVulkan_vulkan_base_VulkanSwapChain.cpp>` (``Src/GraphicsEngineVulkan/vulkan_base/VulkanSwapChain.cpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   module;
   
   #include "renderer/SwapChainDetails.hpp"
   #include <algorithm>
   #include <cstddef>
   #include <cstdint>
   #include <limits>
   #include <vector>
   #include <vulkan/vulkan.hpp>
   
   #include "GLFW/glfw3.h"
   #include "common/Utilities.hpp"
   
   module kataglyphis.vulkan.swapchain;
   
   import kataglyphis.vulkan.queue_family_indices;
   import kataglyphis.vulkan.texture;
   import kataglyphis.vulkan.window;
   
   Kataglyphis::VulkanSwapChain::VulkanSwapChain() = default;
   
   void Kataglyphis::VulkanSwapChain::initVulkanContext(VulkanDevice *in_device,
     Kataglyphis::Frontend::Window *frontend_window,
     const vk::SurfaceKHR &surface)
   {
       this->device = in_device;
       this->window = frontend_window;
   
       // get swap chain details so we can pick the best settings
       Kataglyphis::VulkanRendererInternals::SwapChainDetails const swap_chain_details = device->getSwapchainDetails();
   
       // 1. choose best surface format
       // 2. choose best presentation mode
       // 3. choose swap chain image resolution
   
       vk::SurfaceFormatKHR const surface_format = choose_best_surface_format(swap_chain_details.formats);
       vk::PresentModeKHR const present_mode = choose_best_presentation_mode(swap_chain_details.presentation_mode);
       vk::Extent2D const extent = choose_swap_extent(swap_chain_details.surface_capabilities);
   
       // how many images are in the swap chain; get 1 more than the minimum to allow
       // tiple buffering
       uint32_t image_count = swap_chain_details.surface_capabilities.minImageCount + 1;
   
       // if maxImageCount == 0, then limitless
       if (swap_chain_details.surface_capabilities.maxImageCount > 0
           && swap_chain_details.surface_capabilities.maxImageCount < image_count) {
           image_count = swap_chain_details.surface_capabilities.maxImageCount;
       }
   
       // get queue family indices
       Kataglyphis::VulkanRendererInternals::QueueFamilyIndices const indices = device->getQueueFamilies();
   
       vk::SwapchainCreateInfoKHR swap_chain_create_info{};
       swap_chain_create_info.surface = surface;// swapchain surface
       swap_chain_create_info.imageFormat = surface_format.format;// swapchain format
       swap_chain_create_info.imageColorSpace = surface_format.colorSpace;// swapchain color space
       swap_chain_create_info.presentMode = present_mode;// swapchain presentation mode
       swap_chain_create_info.imageExtent = extent;// swapchain image extents
       swap_chain_create_info.minImageCount = image_count;// minimum images in swapchain
       swap_chain_create_info.imageArrayLayers = 1;// number of layers for each image in chain
       swap_chain_create_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled
                                           | vk::ImageUsageFlagBits::eStorage
                                           | vk::ImageUsageFlagBits::eTransferDst;// what attachment images will be used
                                                                                  // as
       swap_chain_create_info.preTransform =
         swap_chain_details.surface_capabilities.currentTransform;// transform to perform on swap chain images
       swap_chain_create_info.compositeAlpha =
         vk::CompositeAlphaFlagBitsKHR::eOpaque;// dont do blending; everything opaque
       swap_chain_create_info.clipped = vk::True;// of course activate clipping ! :)
   
       // if graphics and presentation families are different then swapchain must let
       // images be shared between families
       if (indices.graphics_family != indices.presentation_family) {
           uint32_t queue_family_indices[] = { static_cast<uint32_t>(indices.graphics_family),
               static_cast<uint32_t>(indices.presentation_family) };
   
           swap_chain_create_info.imageSharingMode = vk::SharingMode::eConcurrent;// image share handling
           swap_chain_create_info.queueFamilyIndexCount = 2;// number of queues to share images between
           swap_chain_create_info.pQueueFamilyIndices = queue_family_indices;// array of queues to share between
   
       } else {
           swap_chain_create_info.imageSharingMode = vk::SharingMode::eExclusive;
           swap_chain_create_info.queueFamilyIndexCount = 0;
           swap_chain_create_info.pQueueFamilyIndices = nullptr;
       }
   
       // if old swap chain been destroyed and this one replaces it then link old one
       // to quickly hand over responsibilities
       swap_chain_create_info.oldSwapchain = nullptr;
   
       // create swap chain
       vk::ResultValue<vk::SwapchainKHR> swapchain_result =
         device->getLogicalDevice().createSwapchainKHR(swap_chain_create_info);
       swapchain = swapchain_result.value;
   
       // store for later reference
       swap_chain_image_format = surface_format.format;
       swap_chain_extent = extent;
   
       // get swapchain images
       vk::ResultValue<std::vector<vk::Image>> images_result = device->getLogicalDevice().getSwapchainImagesKHR(swapchain);
       std::vector<vk::Image> images = images_result.value;
   
       swap_chain_images.clear();
   
       for (size_t i = 0; i < images.size(); i++) {
           vk::Image image = images[static_cast<uint32_t>(i)];
           swap_chain_images.emplace_back();
           Texture &swap_chain_image = swap_chain_images.back();
           swap_chain_image.setImage(image);
           swap_chain_image.createImageView(device, swap_chain_image_format, vk::ImageAspectFlagBits::eColor, 1);
       }
   }
   
   void Kataglyphis::VulkanSwapChain::cleanUp()
   {
       for (Texture &image : swap_chain_images) { device->getLogicalDevice().destroyImageView(image.getImageView()); }
   
       device->getLogicalDevice().destroySwapchainKHR(swapchain);
   }
   
   Kataglyphis::VulkanSwapChain::~VulkanSwapChain() = default;
   
   auto Kataglyphis::VulkanSwapChain::choose_best_surface_format(const std::vector<vk::SurfaceFormatKHR> &formats)
     -> vk::SurfaceFormatKHR
   {
       // best format is subjective, but I go with:
       //  Format:           vk::Format::eR8G8B8A8Unorm (backup-format:
       //  vk::Format::eB8G8R8A8Unorm) color_space:  vk::ColorSpaceKHR::eSrgbNonlinear
       //  the condition in if means all formats are available (no restrictions)
       if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) {
           return { vk::Format::eR8G8B8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear };
       }
   
       // if restricted, search  for optimal format
       for (const auto &format : formats) {
           if ((format.format == vk::Format::eR8G8B8A8Unorm || format.format == vk::Format::eB8G8R8A8Unorm)
               && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) {
               return format;
           }
       }
   
       // in case just return first one--- but really shouldn't be the case ....
       return formats[0];
   }
   
   auto Kataglyphis::VulkanSwapChain::choose_best_presentation_mode(
     const std::vector<vk::PresentModeKHR> &presentation_modes) -> vk::PresentModeKHR
   {
       // look for mailbox presentation mode
       for (const auto &presentation_mode : presentation_modes) {
           if (presentation_mode == vk::PresentModeKHR::eMailbox) { return presentation_mode; }
       }
   
       // if can't find, use FIFO as Vulkan spec says it must be present
       return vk::PresentModeKHR::eFifo;
   }
   
   auto Kataglyphis::VulkanSwapChain::choose_swap_extent(const vk::SurfaceCapabilitiesKHR &surface_capabilities)
     -> vk::Extent2D
   {
       // if current extent is at numeric limits, than extent can vary. Otherwise it
       // is size of window
       if (surface_capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
           return surface_capabilities.currentExtent;
       }
       int width, height;
       glfwGetFramebufferSize(window->get_window(), &width, &height);
   
       // create new extent using window size
       vk::Extent2D new_extent{};
       new_extent.width = static_cast<uint32_t>(width);
       new_extent.height = static_cast<uint32_t>(height);
   
       // surface also defines max and min, so make sure within boundaries bly
       // clamping value
       uint32_t minWidth = surface_capabilities.minImageExtent.width;
       uint32_t maxWidth = surface_capabilities.maxImageExtent.width;
       uint32_t minHeight = surface_capabilities.minImageExtent.height;
       uint32_t maxHeight = surface_capabilities.maxImageExtent.height;
   
       new_extent.width = std::max(minWidth, std::min(maxWidth, new_extent.width));
       new_extent.height = std::max(minHeight, std::min(maxHeight, new_extent.height));
   
       return new_extent;
   }
