From c6a11711c4dcf3fedc658a895c7f1c6722ef08b6 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Mon, 3 Mar 2025 23:46:33 +0100 Subject: Allow to have multiple frames in flight --- hk21/game/gpu.cpp | 121 +++++++++++++++++++++++++++++++++++++++------ hk21/vulkan_loader/fns.hpp | 6 ++- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/hk21/game/gpu.cpp b/hk21/game/gpu.cpp index 207ce85..4e0a65a 100644 --- a/hk21/game/gpu.cpp +++ b/hk21/game/gpu.cpp @@ -5,6 +5,7 @@ #include "hk21/game/gpu.hpp" #include +#include #include #include #include @@ -258,6 +259,12 @@ static asl::status_or create_device(VkPhysicalDevice physical_device, return device; } +struct FrameResources : asl::intrusive_list_node +{ + VkFence complete_fence; + VkCommandPool command_pool; +}; + class GpuImpl : public Gpu { bool m_destroyed{}; @@ -272,6 +279,12 @@ class GpuImpl : public Gpu asl::option m_swapchain; asl::buffer m_swapchain_images; + VkSemaphore m_swapchain_image_acquire_semaphore = VK_NULL_HANDLE; + VkSemaphore m_queue_complete_semaphore = VK_NULL_HANDLE; + + asl::GlobalHeap m_allocator; // @Todo Make this configurable + asl::IntrusiveList m_in_flight_frames; + public: GpuImpl( VkInstance instance, @@ -286,6 +299,8 @@ public: , m_queue_family_index{queue_family_index} , m_device{device} , m_queue{queue} + , m_swapchain_image_acquire_semaphore{create_semaphore()} + , m_queue_complete_semaphore{create_semaphore()} { } @@ -300,6 +315,13 @@ public: { ASL_ASSERT(!m_destroyed); m_destroyed = true; + + vkDeviceWaitIdle(m_device); + + recycle_resources(); + + destroy_semaphore(m_swapchain_image_acquire_semaphore); + destroy_semaphore(m_queue_complete_semaphore); if (m_swapchain.has_value()) { @@ -417,21 +439,54 @@ public: return asl::ok(); } - asl::status frame_opt() + VkSemaphore create_semaphore() { - VkSemaphoreCreateInfo semaphore_create_info{ + static constexpr VkSemaphoreCreateInfo semaphore_create_info{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = nullptr, .flags = 0, }; - VkSemaphore acquire_semaphore{}; - VkSemaphore queue_semaphore{}; - vkCreateSemaphore(m_device, &semaphore_create_info, VK_ALLOCATOR, &acquire_semaphore); - vkCreateSemaphore(m_device, &semaphore_create_info, VK_ALLOCATOR, &queue_semaphore); + VkSemaphore semaphore{}; + vkCreateSemaphore(m_device, &semaphore_create_info, VK_ALLOCATOR, &semaphore); + return semaphore; + } + + void destroy_semaphore(VkSemaphore semaphore) + { + vkDestroySemaphore(m_device, semaphore, VK_ALLOCATOR); + } + + VkFence create_fence() + { + static constexpr VkFenceCreateInfo fence_create_info{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + }; + + VkFence fence{}; + vkCreateFence(m_device, &fence_create_info, VK_ALLOCATOR, &fence); + return fence; + } + + void destroy_fence(VkFence fence) + { + vkDestroyFence(m_device, fence, VK_ALLOCATOR); + } + asl::status frame_opt() + { uint32_t image_index{}; - VkResult res = vkAcquireNextImageKHR(m_device, m_swapchain.value(), 0xffff'ffff'ffff'ffffLLU, acquire_semaphore, VK_NULL_HANDLE, &image_index); + + VkResult res = vkAcquireNextImageKHR( + m_device, + m_swapchain.value(), + 0xffff'ffff'ffff'ffffLLU, + m_swapchain_image_acquire_semaphore, + VK_NULL_HANDLE, + &image_index); + if (res != VK_SUCCESS) { return asl::runtime_error("Couldn't acquire swapchain image: {}", res); @@ -543,19 +598,21 @@ public: VkPipelineStageFlags wait_dst_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkFence fence = create_fence(); + VkSubmitInfo submit_info{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = 1, - .pWaitSemaphores = &acquire_semaphore, + .pWaitSemaphores = &m_swapchain_image_acquire_semaphore, .pWaitDstStageMask = &wait_dst_stage_mask, .commandBufferCount = 1, .pCommandBuffers = &command_buffer, .signalSemaphoreCount = 1, - .pSignalSemaphores = &queue_semaphore, + .pSignalSemaphores = &m_queue_complete_semaphore, }; - res = vkQueueSubmit(m_queue, 1, &submit_info, VK_NULL_HANDLE); + res = vkQueueSubmit(m_queue, 1, &submit_info, fence); if (res != VK_SUCCESS) { return asl::runtime_error("Couldn't submit queue: {}", res); @@ -565,7 +622,7 @@ public: .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = nullptr, .waitSemaphoreCount = 1, - .pWaitSemaphores = &queue_semaphore, + .pWaitSemaphores = &m_queue_complete_semaphore, .swapchainCount = 1, .pSwapchains = &m_swapchain.value(), .pImageIndices = &image_index, @@ -578,20 +635,52 @@ public: return asl::runtime_error("Couldn't present queue: {}", res); } - vkDeviceWaitIdle(m_device); - vkDestroySemaphore(m_device, acquire_semaphore, VK_ALLOCATOR); - vkDestroySemaphore(m_device, queue_semaphore, VK_ALLOCATOR); - vkDestroyCommandPool(m_device, command_pool, VK_ALLOCATOR); + auto* frame_resources = asl::alloc_new(m_allocator); + frame_resources->command_pool = command_pool; + frame_resources->complete_fence = fence; + + m_in_flight_frames.push_front(frame_resources); return asl::ok(); } + // @Todo Make fences recyclable + // @Todo Make frame structure recyclable + + void recycle_resources() + { + while (!m_in_flight_frames.is_empty()) + { + auto* frame = m_in_flight_frames.tail(); + auto status = vkGetFenceStatus(m_device, frame->complete_fence); + + if (status == VK_NOT_READY) + { + return; + } + + if (status != VK_SUCCESS) + { + ASL_LOG_ERROR("Error on frame fence query: {}", status); + return; + } + + destroy_fence(frame->complete_fence); + vkDestroyCommandPool(m_device, frame->command_pool, VK_ALLOCATOR); + + m_in_flight_frames.pop_back(); + asl::alloc_delete(m_allocator, frame); + } + } + void frame() override { + recycle_resources(); + auto s = frame_opt(); if (!s.ok()) { - ASL_LOG_ERROR("{}", s); + ASL_LOG_ERROR("Frame error: {}", s); } } }; diff --git a/hk21/vulkan_loader/fns.hpp b/hk21/vulkan_loader/fns.hpp index 0e03349..2870397 100644 --- a/hk21/vulkan_loader/fns.hpp +++ b/hk21/vulkan_loader/fns.hpp @@ -39,4 +39,8 @@ FN(vkCreateSemaphore) \ FN(vkDestroySemaphore) \ FN(vkCmdPipelineBarrier) \ - FN(vkCmdClearColorImage) + FN(vkCmdClearColorImage) \ + FN(vkCreateFence) \ + FN(vkDestroyFence) \ + FN(vkGetFenceStatus) + -- cgit