From 7fdb6c8aab66a5f76aa2592ac400621380af824b Mon Sep 17 00:00:00 2001 From: Draklaw Date: Sun, 2 Jan 2022 00:24:04 +0100 Subject: [PATCH] Move swapchain-related code to Vulkan::Swapchain. --- CMakeLists.txt | 1 + src/Vulkan/Context.cpp | 341 ++------------------------------- src/Vulkan/Context.h | 62 +----- src/Vulkan/Swapchain.cpp | 393 +++++++++++++++++++++++++++++++++++++++ src/Vulkan/Swapchain.h | 109 +++++++++++ src/VulkanTutorial.cpp | 43 +++-- src/VulkanTutorial.h | 2 + src/utils.h | 36 ++++ 8 files changed, 582 insertions(+), 405 deletions(-) create mode 100644 src/Vulkan/Swapchain.cpp create mode 100644 src/Vulkan/Swapchain.h diff --git a/CMakeLists.txt b/CMakeLists.txt index db2731d..d41fb30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ endfunction() add_executable(vk_expe src/Vulkan/Context.cpp + src/Vulkan/Swapchain.cpp src/main.cpp src/utils.cpp src/Logger.cpp diff --git a/src/Vulkan/Context.cpp b/src/Vulkan/Context.cpp index 6537f6f..95a25cb 100644 --- a/src/Vulkan/Context.cpp +++ b/src/Vulkan/Context.cpp @@ -86,16 +86,24 @@ uint32_t Context::queue_family(size_t queue_index) const { return m_queue_families[queue_index]; } +uint32_t Context::presentation_queue_family() const { + return m_presentation_queue_family; +} + VkQueue Context::queue(size_t queue_index) { return m_queues[queue_index]; } -VkSurfaceKHR Context::surface() { - return m_surface; +VkQueue Context::presentation_queue() { + return m_presentation_queue; +} + +SDL_Window* Context::window() { + return m_window; } -VkSwapchainKHR Context::swapchain() { - return m_swapchain; +VkSurfaceKHR Context::surface() { + return m_surface; } VkSurfaceFormatKHR Context::surface_format() const { @@ -106,44 +114,6 @@ VkPresentModeKHR Context::present_mode() const { return m_present_mode; } -VkExtent2D Context::swapchain_extent() const { - return m_swapchain_extent; -} - -size_t Context::swapchain_image_count() const { - return m_image_resources.size(); -} - -uint32_t Context::current_image_index() const { - return m_current_image_index; -} - -VkImage Context::swapchain_image() { - assert(m_current_image_index != CURRENT_IMAGE_INDEX); - return m_image_resources[m_current_image_index].image; -} - -VkImage Context::swapchain_image(size_t image_index) { - return m_image_resources[image_index].image; -} - -VkImageView Context::swapchain_image_view() { - assert(m_current_image_index != CURRENT_IMAGE_INDEX); - return m_image_resources[m_current_image_index].view; -} - -VkImageView Context::swapchain_image_view(size_t image_index) { - return m_image_resources[image_index].view; -} - -VkSemaphore Context::ready_to_render() { - return m_frame_resources[m_frame_resources_index].ready_to_render; -} - -VkFence Context::render_done() { - return m_frame_resources[m_frame_resources_index].render_done; -} - void Context::initialize(const ContextSettings& settings) { m_window = settings.window(); @@ -151,7 +121,6 @@ void Context::initialize(const ContextSettings& settings) { create_surface(settings); choose_physical_device(settings); create_device(settings); - create_swapchain(settings); } void Context::shutdown() { @@ -163,120 +132,12 @@ void Context::shutdown() { for(auto& callback: m_context_destruction_callbacks) callback(); - destroy_swapchain(); - destroy_device(m_device); destroy_surface(m_surface); destroy_debug_messenger(m_debug_messenger); destroy_instance(m_instance); } -void Context::begin_frame() { - assert(m_current_image_index == CURRENT_IMAGE_INDEX); - - auto& frame_resources = m_frame_resources[m_frame_resources_index]; - - // frame_resources are used cyclically. We wait for the current one to be - // done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at - // the same time. - if(frame_resources.render_done != VK_NULL_HANDLE) { - vkWaitForFences( - m_device, - 1, &frame_resources.render_done, - VK_TRUE, - UINT64_MAX - ); - } - - bool acquired_image = false; - while(!acquired_image) { - VkResult const result = vkAcquireNextImageKHR( - m_device, - m_swapchain, - UINT64_MAX, - frame_resources.ready_to_render, - VK_NULL_HANDLE, - &m_current_image_index - ); - if(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) { - acquired_image = true; - } - else if(result == VK_ERROR_OUT_OF_DATE_KHR) { - recreate_swapchain(); - } - else { - throw std::runtime_error("failed to acquire swapchain image"); - } - } - - logger.info() << "begin frame " << m_frame_index - << ": image " << m_current_image_index - << ", frame: " << m_frame_resources_index; - - auto& image_resources = m_image_resources[m_current_image_index]; - - // In case vkAcquireNextImageKHR doesn't return images in-order, we wait - // again to be sure. - if(image_resources.render_done != VK_NULL_HANDLE) { - vkWaitForFences( - m_device, - 1, &image_resources.render_done, - VK_TRUE, - UINT64_MAX - ); - } - - // Remember the right frame to wait for. - frame_resources.render_done = image_resources.render_done; - - vkResetFences(m_device, 1, &frame_resources.render_done); -} - -void Context::swap_buffers(uint32_t semaphore_count, VkSemaphore* wait_semaphores) { - assert(m_current_image_index != CURRENT_IMAGE_INDEX); - - VkPresentInfoKHR present_info { - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = semaphore_count, - .pWaitSemaphores = wait_semaphores, - .swapchainCount = 1, - .pSwapchains = &m_swapchain, - .pImageIndices = &m_current_image_index, - .pResults = nullptr, - }; - - VkResult const result = vkQueuePresentKHR(m_presentation_queue, &present_info); - if(result == VK_SUCCESS) { - // Nothing to see here ! - } - else if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - m_invalid_swapchain = true; - } - else { - throw std::runtime_error("failed to present swapchain image"); - } - - if(m_invalid_swapchain) - recreate_swapchain(); - - m_frame_index += 1; - m_frame_resources_index = m_frame_index % MAX_FRAMES_IN_FLIGHT; - - m_current_image_index = CURRENT_IMAGE_INDEX; -} - -void Context::invalidate_swapchain() { - m_invalid_swapchain = true; -} - -void Context::register_swapchain_creation_callback(SwapchainCreationCallback callback) { - m_swapchain_creation_callbacks.emplace_back(std::move(callback)); -} - -void Context::register_swapchain_destruction_callback(SwapchainDestructionCallback callback) { - m_swapchain_destruction_callbacks.emplace_back(std::move(callback)); -} - void Context::register_context_destruction_callback(ContextDestructionCallback callback) { m_context_destruction_callbacks.emplace_back(std::move(callback)); } @@ -778,184 +639,6 @@ void Context::create_device(const ContextSettings& settings) { vkGetDeviceQueue(m_device, m_presentation_queue_family, 0, &m_presentation_queue); } -void Context::create_swapchain(const ContextSettings& settings) { - m_swapchain_queue_families = { - m_presentation_queue_family, - }; - for(size_t index = 0; index < m_queue_families.size(); index += 1) { - if(settings.queues()[index].use_swapchain_images) - m_swapchain_queue_families.push_back(m_queue_families[index]); - } - std::sort(m_swapchain_queue_families.begin(), m_swapchain_queue_families.end()); - m_swapchain_queue_families.erase( - std::unique(m_swapchain_queue_families.begin(), m_swapchain_queue_families.end()), - m_swapchain_queue_families.end() - ); - - create_swapchain(); -} - -void Context::create_swapchain() { - VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physical_device, m_surface, &capabilities); - - m_swapchain_extent = capabilities.currentExtent; - if(m_swapchain_extent.width == UINT32_MAX) { - int width; - int height; - SDL_Vulkan_GetDrawableSize(m_window, &width, &height); - - m_swapchain_extent.width = std::clamp( - uint32_t(width), - capabilities.minImageExtent.width, - capabilities.maxImageExtent.width - ); - m_swapchain_extent.height = std::clamp( - uint32_t(height), - capabilities.minImageExtent.height, - capabilities.maxImageExtent.height - ); - } - logger.debug() << "swapchain extent: " << m_swapchain_extent.width - << ", " << m_swapchain_extent.height; - - uint32_t image_count = capabilities.minImageCount; - if(capabilities.maxImageCount != 0) - image_count = std::min(image_count, capabilities.maxImageCount); - logger.debug() << "swapchain image count: " << image_count; - - bool share_image = m_swapchain_queue_families.size() > 1; - - VkSwapchainCreateInfoKHR swapchain_info { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = m_surface, - .minImageCount = image_count, - .imageFormat = m_surface_format.format, - .imageColorSpace = m_surface_format.colorSpace, - .imageExtent = m_swapchain_extent, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .imageSharingMode = share_image? - VK_SHARING_MODE_CONCURRENT: - VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = share_image? - uint32_t(m_swapchain_queue_families.size()): - 0u, - .pQueueFamilyIndices = share_image? - m_swapchain_queue_families.data(): - nullptr, - .preTransform = capabilities.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = m_present_mode, - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, - }; - - if(vkCreateSwapchainKHR(m_device, &swapchain_info, nullptr, &m_swapchain) != VK_SUCCESS) - throw std::runtime_error("failed to create swapchain"); - - vkGetSwapchainImagesKHR(m_device, m_swapchain, &image_count, nullptr); - VkImage swapchain_images[16]; - vkGetSwapchainImagesKHR( - m_device, - m_swapchain, - &image_count, - swapchain_images - ); - - m_image_resources.resize(image_count, {}); - for(size_t index = 0; index < image_count; index += 1) { - auto& image_resources = m_image_resources[index]; - - image_resources.image = swapchain_images[index]; - - VkImageViewCreateInfo view_info { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = image_resources.image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = m_surface_format.format, - .components = { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - if(vkCreateImageView( - m_device, - &view_info, - nullptr, - &image_resources.view - ) != VK_SUCCESS) { - throw std::runtime_error("failed to create swapchain image view"); - } - - VkFenceCreateInfo fence_info { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .flags = VK_FENCE_CREATE_SIGNALED_BIT, - }; - if(vkCreateFence( - m_device, - &fence_info, - nullptr, - &image_resources.render_done - ) != VK_SUCCESS) - throw std::runtime_error("failed to create fence"); - } - - m_frame_resources.resize(MAX_FRAMES_IN_FLIGHT, {}); - - int index = 0; - for(auto& frame_resources: m_frame_resources) { - VkSemaphoreCreateInfo semaphore_info { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - }; - if(vkCreateSemaphore( - m_device, - &semaphore_info, - nullptr, - &frame_resources.ready_to_render - ) != VK_SUCCESS) - throw std::runtime_error("failed to create semaphore"); - - index += 1; - } - - for(auto& callback: m_swapchain_creation_callbacks) - callback(image_count); -} - -void Context::destroy_swapchain() { - for(auto& callback: m_swapchain_destruction_callbacks) - callback(); - - for(auto& frame_resources: m_frame_resources) { - destroy_semaphore(frame_resources.ready_to_render); - // This is a reference to an ImageResources.render_done - frame_resources.render_done = VK_NULL_HANDLE; - } - - for(auto& image_resources: m_image_resources) { - destroy_fence(image_resources.render_done); - destroy_image_view(image_resources.view); - } - - destroy_swapchain(m_swapchain); -} - -void Context::recreate_swapchain() { - destroy_swapchain(); - create_swapchain(); -} - void Context::initialize_extension_functions() { uint32_t errors_count = 0; diff --git a/src/Vulkan/Context.h b/src/Vulkan/Context.h index 39a71b6..b6e001b 100644 --- a/src/Vulkan/Context.h +++ b/src/Vulkan/Context.h @@ -52,11 +52,6 @@ constexpr uint32_t INVALID_QUEUE_FAMILY = UINT32_MAX; class Context { public: - static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX; - static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2; - - using SwapchainCreationCallback = std::function; - using SwapchainDestructionCallback = std::function; using ContextDestructionCallback = std::function; public: @@ -69,36 +64,21 @@ public: VkInstance instance(); VkPhysicalDevice physical_device(); VkDevice device(); + size_t queue_family_count() const; uint32_t queue_family(size_t queue_index) const; + uint32_t presentation_queue_family() const; VkQueue queue(size_t queue_index); + VkQueue presentation_queue(); + SDL_Window* window(); VkSurfaceKHR surface(); - VkSwapchainKHR swapchain(); - VkSurfaceFormatKHR surface_format() const; VkPresentModeKHR present_mode() const; - VkExtent2D swapchain_extent() const; - size_t swapchain_image_count() const; - uint32_t current_image_index() const; - VkImage swapchain_image(); - VkImage swapchain_image(size_t image_index); - VkImageView swapchain_image_view(); - VkImageView swapchain_image_view(size_t image_index); - // VkFramebuffer framebuffer(); - VkSemaphore ready_to_render(); - VkFence render_done(); void initialize(const ContextSettings& settings); void shutdown(); - void begin_frame(); - void swap_buffers(uint32_t semaphore_count, VkSemaphore* wait_semaphores); - - void invalidate_swapchain(); - - void register_swapchain_creation_callback(SwapchainCreationCallback callback); - void register_swapchain_destruction_callback(SwapchainDestructionCallback callback); void register_context_destruction_callback(ContextDestructionCallback callback); @@ -145,18 +125,6 @@ public: PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr; PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = nullptr; -private: - struct ImageResources { - VkImage image = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - VkFence render_done = VK_NULL_HANDLE; - }; - - struct FrameResources { - VkSemaphore ready_to_render = VK_NULL_HANDLE; - VkFence render_done = VK_NULL_HANDLE; - }; - private: void create_instance(const ContextSettings& settings); void create_surface(const ContextSettings& settings); @@ -166,11 +134,6 @@ private: const ContextSettings& settings ); void create_device(const ContextSettings& settings); - void create_swapchain(const ContextSettings& settings); - void create_swapchain(); - - void destroy_swapchain(); - void recreate_swapchain(); void initialize_extension_functions(); @@ -197,25 +160,10 @@ private: VkQueue m_presentation_queue = nullptr; VkSurfaceKHR m_surface = VK_NULL_HANDLE; - VkSwapchainKHR m_swapchain = VK_NULL_HANDLE; - VkSurfaceFormatKHR m_surface_format; VkPresentModeKHR m_present_mode; - VkExtent2D m_swapchain_extent; - std::vector m_swapchain_queue_families; - std::vector m_image_resources; - std::vector m_frame_resources; - bool m_invalid_swapchain = false; - - std::vector m_swapchain_creation_callbacks; - std::vector m_swapchain_destruction_callbacks; - std::vector m_context_destruction_callbacks; - - uint32_t m_current_image_index = CURRENT_IMAGE_INDEX; - size_t m_frame_index = 0; - size_t m_frame_resources_index = 0; - friend class ContextSettings; + std::vector m_context_destruction_callbacks; }; diff --git a/src/Vulkan/Swapchain.cpp b/src/Vulkan/Swapchain.cpp new file mode 100644 index 0000000..d256b73 --- /dev/null +++ b/src/Vulkan/Swapchain.cpp @@ -0,0 +1,393 @@ +#include + +#include + +#include + +#include +#include +#include +#include + + +namespace Vulkan { + + +SwapchainSettings::SwapchainSettings(Context* context) + : m_context(context) +{} + +SwapchainSettings::~SwapchainSettings() { +} + +Context* SwapchainSettings::context() const { + return m_context; +} + +std::vector SwapchainSettings::queue_families() const { + return m_queue_families; +} + +SwapchainSettings& SwapchainSettings::with_queue(uint32_t queue_family) { + m_queue_families.push_back(queue_family); + return *this; +} + + +////////////////////////////////////////////////////////////////////////////// +// Swapchain + + +Swapchain::Swapchain() { +} + +Swapchain::~Swapchain() { + shutdown(); +} + +VkSwapchainKHR Swapchain::swapchain() { + return m_swapchain; +} + +VkExtent2D Swapchain::extent() const { + return m_extent; +} + +size_t Swapchain::image_count() const { + return m_image_resources.size(); +} + +uint32_t Swapchain::current_image_index() const { + return m_current_image_index; +} + +VkImage Swapchain::image() { + assert(m_current_image_index != CURRENT_IMAGE_INDEX); + return m_image_resources[m_current_image_index].image; +} + +VkImage Swapchain::image(size_t image_index) { + return m_image_resources[image_index].image; +} + +VkImageView Swapchain::image_view() { + assert(m_current_image_index != CURRENT_IMAGE_INDEX); + return m_image_resources[m_current_image_index].view; +} + +VkImageView Swapchain::image_view(size_t image_index) { + return m_image_resources[image_index].view; +} + +VkSemaphore Swapchain::ready_to_render() { + return m_frame_resources[m_frame_resources_index].ready_to_render; +} + +VkFence Swapchain::render_done() { + return m_frame_resources[m_frame_resources_index].render_done; +} + +void Swapchain::initialize(const SwapchainSettings& settings) { + assert(m_context == nullptr); + m_context = settings.context(); + + create(settings); +} + +void Swapchain::shutdown() { + if(!m_swapchain) + return; + + destroy(); + + m_context = nullptr; +} + +void Swapchain::begin_frame() { + assert(m_current_image_index == CURRENT_IMAGE_INDEX); + + auto& frame_resources = m_frame_resources[m_frame_resources_index]; + + // frame_resources are used cyclically. We wait for the current one to be + // done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at + // the same time. + if(frame_resources.render_done != VK_NULL_HANDLE) { + vkWaitForFences( + m_context->device(), + 1, &frame_resources.render_done, + VK_TRUE, + UINT64_MAX + ); + } + + bool acquired_image = false; + while(!acquired_image) { + VkResult const result = vkAcquireNextImageKHR( + m_context->device(), + m_swapchain, + UINT64_MAX, + frame_resources.ready_to_render, + VK_NULL_HANDLE, + &m_current_image_index + ); + if(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) { + acquired_image = true; + } + else if(result == VK_ERROR_OUT_OF_DATE_KHR) { + recreate(); + } + else { + throw std::runtime_error("failed to acquire swapchain image"); + } + } + + logger.info() << "begin frame " << m_frame_index + << ": image " << m_current_image_index + << ", frame: " << m_frame_resources_index; + + auto& image_resources = m_image_resources[m_current_image_index]; + + // In case vkAcquireNextImageKHR doesn't return images in-order, we wait + // again to be sure. + if(image_resources.render_done != VK_NULL_HANDLE) { + vkWaitForFences( + m_context->device(), + 1, &image_resources.render_done, + VK_TRUE, + UINT64_MAX + ); + } + + // Remember the right frame to wait for. + frame_resources.render_done = image_resources.render_done; + + vkResetFences(m_context->device(), 1, &frame_resources.render_done); +} + +void Swapchain::swap_buffers(Array wait_semaphores) { + assert(m_current_image_index != CURRENT_IMAGE_INDEX); + + VkPresentInfoKHR present_info { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = uint32_t(wait_semaphores.size()), + .pWaitSemaphores = wait_semaphores.data(), + .swapchainCount = 1, + .pSwapchains = &m_swapchain, + .pImageIndices = &m_current_image_index, + .pResults = nullptr, + }; + + VkResult const result = vkQueuePresentKHR( + m_context->presentation_queue(), + &present_info + ); + if(result == VK_SUCCESS) { + // Nothing to see here ! + } + else if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + m_invalid = true; + } + else { + throw std::runtime_error("failed to present swapchain image"); + } + + if(m_invalid) + recreate(); + + m_frame_index += 1; + m_frame_resources_index = m_frame_index % MAX_FRAMES_IN_FLIGHT; + + m_current_image_index = CURRENT_IMAGE_INDEX; +} + +void Swapchain::invalidate() { + m_invalid = true; +} + +void Swapchain::register_creation_callback(CreationCallback callback) { + m_creation_callbacks.emplace_back(std::move(callback)); +} + +void Swapchain::register_destruction_callback(DestructionCallback callback) { + m_destruction_callbacks.emplace_back(std::move(callback)); +} + +void Swapchain::create(const SwapchainSettings& settings) { + m_queue_families = settings.queue_families(); + m_queue_families.push_back(m_context->presentation_queue_family()); + std::sort(m_queue_families.begin(), m_queue_families.end()); + m_queue_families.erase( + std::unique(m_queue_families.begin(), m_queue_families.end()), + m_queue_families.end() + ); + + create(); +} + +void Swapchain::create() { + VkSurfaceCapabilitiesKHR capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + m_context->physical_device(), + m_context->surface(), + &capabilities + ); + + m_extent = capabilities.currentExtent; + if(m_extent.width == UINT32_MAX) { + int width; + int height; + SDL_Vulkan_GetDrawableSize(m_context->window(), &width, &height); + + m_extent.width = std::clamp( + uint32_t(width), + capabilities.minImageExtent.width, + capabilities.maxImageExtent.width + ); + m_extent.height = std::clamp( + uint32_t(height), + capabilities.minImageExtent.height, + capabilities.maxImageExtent.height + ); + } + logger.debug() << "swapchain extent: " << m_extent.width + << ", " << m_extent.height; + + uint32_t image_count = capabilities.minImageCount; + if(capabilities.maxImageCount != 0) + image_count = std::min(image_count, capabilities.maxImageCount); + logger.debug() << "swapchain image count: " << image_count; + + bool share_image = m_queue_families.size() > 1; + + VkSwapchainCreateInfoKHR swapchain_info { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = m_context->surface(), + .minImageCount = image_count, + .imageFormat = m_context->surface_format().format, + .imageColorSpace = m_context->surface_format().colorSpace, + .imageExtent = m_extent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = share_image? + VK_SHARING_MODE_CONCURRENT: + VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = share_image? + uint32_t(m_queue_families.size()): + 0u, + .pQueueFamilyIndices = share_image? + m_queue_families.data(): + nullptr, + .preTransform = capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = m_context->present_mode(), + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + + if(vkCreateSwapchainKHR(m_context->device(), &swapchain_info, nullptr, &m_swapchain) != VK_SUCCESS) + throw std::runtime_error("failed to create swapchain"); + + vkGetSwapchainImagesKHR(m_context->device(), m_swapchain, &image_count, nullptr); + VkImage swapchain_images[16]; + vkGetSwapchainImagesKHR( + m_context->device(), + m_swapchain, + &image_count, + swapchain_images + ); + + m_image_resources.resize(image_count, {}); + for(size_t index = 0; index < image_count; index += 1) { + auto& image_resources = m_image_resources[index]; + + image_resources.image = swapchain_images[index]; + + VkImageViewCreateInfo view_info { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image_resources.image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = m_context->surface_format().format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + if(vkCreateImageView( + m_context->device(), + &view_info, + nullptr, + &image_resources.view + ) != VK_SUCCESS) { + throw std::runtime_error("failed to create swapchain image view"); + } + + VkFenceCreateInfo fence_info { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }; + if(vkCreateFence( + m_context->device(), + &fence_info, + nullptr, + &image_resources.render_done + ) != VK_SUCCESS) + throw std::runtime_error("failed to create fence"); + } + + m_frame_resources.resize(MAX_FRAMES_IN_FLIGHT, {}); + + int index = 0; + for(auto& frame_resources: m_frame_resources) { + VkSemaphoreCreateInfo semaphore_info { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + if(vkCreateSemaphore( + m_context->device(), + &semaphore_info, + nullptr, + &frame_resources.ready_to_render + ) != VK_SUCCESS) + throw std::runtime_error("failed to create semaphore"); + + index += 1; + } + + for(auto& callback: m_creation_callbacks) + callback(image_count); +} + +void Swapchain::destroy() { + for(auto& callback: m_destruction_callbacks) + callback(); + + for(auto& frame_resources: m_frame_resources) { + m_context->destroy_semaphore(frame_resources.ready_to_render); + // This is a reference to an ImageResources.render_done + frame_resources.render_done = VK_NULL_HANDLE; + } + + for(auto& image_resources: m_image_resources) { + m_context->destroy_fence(image_resources.render_done); + m_context->destroy_image_view(image_resources.view); + } + + m_context->destroy_swapchain(m_swapchain); +} + +void Swapchain::recreate() { + destroy(); + create(); +} + + +} diff --git a/src/Vulkan/Swapchain.h b/src/Vulkan/Swapchain.h new file mode 100644 index 0000000..5b7336f --- /dev/null +++ b/src/Vulkan/Swapchain.h @@ -0,0 +1,109 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include +#include +#include + + +namespace Vulkan { + + +class Swapchain; + +class SwapchainSettings { +public: + SwapchainSettings(Context* context); + ~SwapchainSettings(); + + Context* context() const; + + std::vector queue_families() const; + SwapchainSettings& with_queue(uint32_t queue_family); + +private: + Context* m_context; + std::vector m_queue_families; +}; + +class Swapchain { +public: + static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX; + static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2; + + using CreationCallback = std::function; + using DestructionCallback = std::function; + +public: + Swapchain(); + Swapchain(const Swapchain&) = delete; + ~Swapchain(); + + Swapchain& operator=(const Swapchain&) = delete; + + VkSwapchainKHR swapchain(); + VkExtent2D extent() const; + size_t image_count() const; + uint32_t current_image_index() const; + VkImage image(); + VkImage image(size_t image_index); + VkImageView image_view(); + VkImageView image_view(size_t image_index); + VkSemaphore ready_to_render(); + VkFence render_done(); + + void initialize(const SwapchainSettings& settings); + void shutdown(); + + void begin_frame(); + void swap_buffers(Array wait_semaphores); + + void invalidate(); + + void register_creation_callback(CreationCallback callback); + void register_destruction_callback(DestructionCallback callback); + +private: + struct ImageResources { + VkImage image = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + VkFence render_done = VK_NULL_HANDLE; + }; + + struct FrameResources { + VkSemaphore ready_to_render = VK_NULL_HANDLE; + VkFence render_done = VK_NULL_HANDLE; + }; + +private: + void create(const SwapchainSettings& settings); + void create(); + void destroy(); + void recreate(); + +private: + Context* m_context = nullptr; + + VkSwapchainKHR m_swapchain = VK_NULL_HANDLE; + VkExtent2D m_extent; + std::vector m_queue_families; + std::vector m_image_resources; + std::vector m_frame_resources; + bool m_invalid = false; + + std::vector m_creation_callbacks; + std::vector m_destruction_callbacks; + + uint32_t m_current_image_index = CURRENT_IMAGE_INDEX; + size_t m_frame_index = 0; + size_t m_frame_resources_index = 0; +}; + + +} diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index 66ba3f2..e10a6db 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -47,9 +47,9 @@ const std::vector vertices = { VulkanTutorial::VulkanTutorial() { - m_context.register_swapchain_creation_callback( + m_swapchain.register_creation_callback( std::bind(&VulkanTutorial::create_swapchain_objects, this, std::placeholders::_1)); - m_context.register_swapchain_destruction_callback( + m_swapchain.register_destruction_callback( std::bind(&VulkanTutorial::destroy_swapchain_objects, this)); } @@ -58,13 +58,17 @@ VulkanTutorial::~VulkanTutorial() { } void VulkanTutorial::initialize(SDL_Window* window) { - auto const settings = Vulkan::ContextSettings() + auto const context_settings = Vulkan::ContextSettings() #if defined(VKEXPE_ENABLE_VALIDATION) || !defined(NDEBUG) .with_debug(true) #endif .with_queue(GRAPHIC_QUEUE, VK_QUEUE_GRAPHICS_BIT) .with_window(window); - m_context.initialize(settings); + m_context.initialize(context_settings); + + auto const swapchain_settings = Vulkan::SwapchainSettings(&m_context) + .with_queue(m_context.queue_family(GRAPHIC_QUEUE)); + m_swapchain.initialize(swapchain_settings); create_command_pool(); create_vertex_buffer(); @@ -87,15 +91,16 @@ void VulkanTutorial::shutdown() { m_context.destroy_semaphore(semaphore); m_render_done.clear(); + m_swapchain.shutdown(); m_context.shutdown(); } void VulkanTutorial::draw_frame() { - m_context.begin_frame(); - auto const image_index = m_context.current_image_index(); + m_swapchain.begin_frame(); + auto const image_index = m_swapchain.current_image_index(); VkSemaphore wait_semaphores[] = { - m_context.ready_to_render(), + m_swapchain.ready_to_render(), }; VkPipelineStageFlags stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -114,15 +119,15 @@ void VulkanTutorial::draw_frame() { if(vkQueueSubmit( m_context.queue(GRAPHIC_QUEUE), 1, &submit_info, - m_context.render_done() + m_swapchain.render_done() )) throw std::runtime_error("failed to submit draw command buffer"); - m_context.swap_buffers(1, &m_render_done[image_index]); + m_swapchain.swap_buffers({1, &m_render_done[image_index]}); } void VulkanTutorial::invalidate_swapchain() { - m_context.invalidate_swapchain(); + m_swapchain.invalidate(); } void VulkanTutorial::create_swapchain_objects(uint32_t image_count) { @@ -131,7 +136,7 @@ void VulkanTutorial::create_swapchain_objects(uint32_t image_count) { create_graphic_pipeline(); create_command_buffers(); - m_render_done.resize(m_context.swapchain_image_count()); + m_render_done.resize(m_swapchain.image_count()); VkSemaphoreCreateInfo semaphore_info { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, @@ -268,15 +273,15 @@ void VulkanTutorial::create_graphic_pipeline() { VkViewport viewport { .x = 0.0f, .y = 0.0f, - .width = float(m_context.swapchain_extent().width), - .height = float(m_context.swapchain_extent().height), + .width = float(m_swapchain.extent().width), + .height = float(m_swapchain.extent().height), .minDepth = 0.0f, .maxDepth = 1.0f, }; VkRect2D scissor = { .offset = { 0, 0 }, - .extent = m_context.swapchain_extent(), + .extent = m_swapchain.extent(), }; VkPipelineViewportStateCreateInfo viewport_info { @@ -381,18 +386,18 @@ void VulkanTutorial::create_graphic_pipeline() { } void VulkanTutorial::create_framebuffers() { - m_framebuffers.resize(m_context.swapchain_image_count()); + m_framebuffers.resize(m_swapchain.image_count()); for(size_t index = 0; index < m_framebuffers.size(); index += 1) { - VkImageView view = m_context.swapchain_image_view(index); + VkImageView view = m_swapchain.image_view(index); VkFramebufferCreateInfo framebuffer_info { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .renderPass = m_render_pass, .attachmentCount = 1, .pAttachments = &view, - .width = m_context.swapchain_extent().width, - .height = m_context.swapchain_extent().height, + .width = m_swapchain.extent().width, + .height = m_swapchain.extent().height, .layers = 1, }; @@ -547,7 +552,7 @@ void VulkanTutorial::create_command_buffers() { .framebuffer = m_framebuffers[index], .renderArea = { .offset = { 0, 0 }, - .extent = m_context.swapchain_extent(), + .extent = m_swapchain.extent(), }, .clearValueCount = 1, .pClearValues = &clear_color, diff --git a/src/VulkanTutorial.h b/src/VulkanTutorial.h index 31794b8..125ecae 100644 --- a/src/VulkanTutorial.h +++ b/src/VulkanTutorial.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -54,6 +55,7 @@ private: private: Vulkan::Context m_context; + Vulkan::Swapchain m_swapchain; VkQueue m_graphic_queue = nullptr; VkQueue m_presentation_queue = nullptr; diff --git a/src/utils.h b/src/utils.h index 04d6b5c..3e14689 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -38,3 +39,38 @@ std::string cat(Args&&... args) { } std::vector read_binary_file(const char* path); + +template +class Array { +public: + Array() { + } + + Array(size_t size, T* data) + : m_size(size) + , m_data(data) + { + assert((size == 0 && m_data == nullptr) || (size != 0 && m_data != nullptr)); + } + + Array(const std::vector& vector) + : m_size(vector.size()) + , m_data(vector.data()) + {} + + explicit operator bool() const { + return m_size != 0 && m_data != nullptr; + } + + size_t size() const { + return m_size; + } + + T* data() const { + return m_data; + } + +private: + size_t m_size = 0; + T* m_data = nullptr; +};