Program Listing for File VulkanSwapChain.cpp
↰ Return to documentation for file (Src/GraphicsEngineVulkan/vulkan_base/VulkanSwapChain.cpp
)
#include "vulkan_base/VulkanSwapChain.hpp"
#include <limits>
#include "common/Utilities.hpp"
Kataglyphis::VulkanSwapChain::VulkanSwapChain() {}
void Kataglyphis::VulkanSwapChain::initVulkanContext(VulkanDevice *device,
Kataglyphis::Frontend::Window *window,
const VkSurfaceKHR &surface)
{
this->device = device;
this->window = window;
// get swap chain details so we can pick the best settings
Kataglyphis::VulkanRendererInternals::SwapChainDetails swap_chain_details = device->getSwapchainDetails();
// 1. choose best surface format
// 2. choose best presentation mode
// 3. choose swap chain image resolution
VkSurfaceFormatKHR surface_format = choose_best_surface_format(swap_chain_details.formats);
VkPresentModeKHR present_mode = choose_best_presentation_mode(swap_chain_details.presentation_mode);
VkExtent2D 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;
}
VkSwapchainCreateInfoKHR swap_chain_create_info{};
swap_chain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
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_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT
| VK_IMAGE_USAGE_STORAGE_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT;// 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_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;// dont do blending; everything opaque
swap_chain_create_info.clipped = VK_TRUE;// of course activate clipping ! :)
// get queue family indices
Kataglyphis::VulkanRendererInternals::QueueFamilyIndices indices = device->getQueueFamilies();
// 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[] = { (uint32_t)indices.graphics_family, (uint32_t)indices.presentation_family };
swap_chain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;// 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_SHARING_MODE_EXCLUSIVE;
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 = VK_NULL_HANDLE;
// create swap chain
VkResult result = vkCreateSwapchainKHR(device->getLogicalDevice(), &swap_chain_create_info, nullptr, &swapchain);
ASSERT_VULKAN(result, "Failed create swapchain!");
// store for later reference
swap_chain_image_format = surface_format.format;
swap_chain_extent = extent;
// get swapchain images (first count, then values)
uint32_t swapchain_image_count;
vkGetSwapchainImagesKHR(device->getLogicalDevice(), swapchain, &swapchain_image_count, nullptr);
std::vector<VkImage> images(swapchain_image_count);
vkGetSwapchainImagesKHR(device->getLogicalDevice(), swapchain, &swapchain_image_count, images.data());
swap_chain_images.clear();
for (size_t i = 0; i < images.size(); i++) {
VkImage image = images[static_cast<uint32_t>(i)];
// store image handle
Texture swap_chain_image{};
swap_chain_image.setImage(image);
swap_chain_image.createImageView(device, swap_chain_image_format, VK_IMAGE_ASPECT_COLOR_BIT, 1);
// add to swapchain image list
swap_chain_images.push_back(swap_chain_image);
}
}
void Kataglyphis::VulkanSwapChain::cleanUp()
{
for (Texture &image : swap_chain_images) {
vkDestroyImageView(device->getLogicalDevice(), image.getImageView(), nullptr);
}
vkDestroySwapchainKHR(device->getLogicalDevice(), swapchain, nullptr);
}
Kataglyphis::VulkanSwapChain::~VulkanSwapChain() {}
VkSurfaceFormatKHR Kataglyphis::VulkanSwapChain::choose_best_surface_format(
const std::vector<VkSurfaceFormatKHR> &formats)
{
// best format is subjective, but I go with:
// Format: VK_FORMAT_R8G8B8A8_UNORM (backup-format:
// VK_FORMAT_B8G8R8A8_UNORM) color_space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
// the condition in if means all formats are available (no restrictions)
if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
return { VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
}
// if restricted, search for optimal format
for (const auto &format : formats) {
if ((format.format == VK_FORMAT_R8G8B8A8_UNORM || format.format == VK_FORMAT_B8G8R8A8_UNORM)
&& format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return format;
}
}
// in case just return first one--- but really shouldn't be the case ....
return formats[0];
}
VkPresentModeKHR Kataglyphis::VulkanSwapChain::choose_best_presentation_mode(
const std::vector<VkPresentModeKHR> &presentation_modes)
{
// look for mailbox presentation mode
for (const auto &presentation_mode : presentation_modes) {
if (presentation_mode == VK_PRESENT_MODE_MAILBOX_KHR) { return presentation_mode; }
}
// if can't find, use FIFO as Vulkan spec says it must be present
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D Kataglyphis::VulkanSwapChain::choose_swap_extent(const VkSurfaceCapabilitiesKHR &surface_capabilities)
{
// 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;
} else {
int width, height;
glfwGetFramebufferSize(window->get_window(), &width, &height);
// create new extent using window size
VkExtent2D 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
new_extent.width = std::max(surface_capabilities.minImageExtent.width,
std::min(surface_capabilities.maxImageExtent.width, new_extent.width));
new_extent.height = std::max(surface_capabilities.minImageExtent.height,
std::min(surface_capabilities.maxImageExtent.height, new_extent.height));
return new_extent;
}
}