.. _program_listing_file_Src_GraphicsEngineVulkan_vulkan_base_VulkanSwapChain.cpp: Program Listing for File VulkanSwapChain.cpp ============================================ |exhale_lsh| :ref:`Return to documentation for file ` (``Src/GraphicsEngineVulkan/vulkan_base/VulkanSwapChain.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "vulkan_base/VulkanSwapChain.hpp" #include #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 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(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 &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 &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::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(width); new_extent.height = static_cast(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; } }