Program Listing for File VulkanSwapChain.cpp#
↰ Return to documentation for file (Src/GraphicsEngineVulkan/vulkan_base/VulkanSwapChain.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;
}