Allow to have multiple frames in flight

This commit is contained in:
2025-03-03 23:46:33 +01:00
parent 1e1f61b9e6
commit c6a11711c4
2 changed files with 110 additions and 17 deletions

View File

@ -5,6 +5,7 @@
#include "hk21/game/gpu.hpp" #include "hk21/game/gpu.hpp"
#include <asl/containers/buffer.hpp> #include <asl/containers/buffer.hpp>
#include <asl/containers/intrusive_list.hpp>
#include <asl/formatting/format.hpp> #include <asl/formatting/format.hpp>
#include <asl/types/option.hpp> #include <asl/types/option.hpp>
#include <asl/logging/logging.hpp> #include <asl/logging/logging.hpp>
@ -258,6 +259,12 @@ static asl::status_or<VkDevice> create_device(VkPhysicalDevice physical_device,
return device; return device;
} }
struct FrameResources : asl::intrusive_list_node<FrameResources>
{
VkFence complete_fence;
VkCommandPool command_pool;
};
class GpuImpl : public Gpu class GpuImpl : public Gpu
{ {
bool m_destroyed{}; bool m_destroyed{};
@ -272,6 +279,12 @@ class GpuImpl : public Gpu
asl::option<VkSwapchainKHR> m_swapchain; asl::option<VkSwapchainKHR> m_swapchain;
asl::buffer<VkImage> m_swapchain_images; asl::buffer<VkImage> 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<FrameResources> m_in_flight_frames;
public: public:
GpuImpl( GpuImpl(
VkInstance instance, VkInstance instance,
@ -286,6 +299,8 @@ public:
, m_queue_family_index{queue_family_index} , m_queue_family_index{queue_family_index}
, m_device{device} , m_device{device}
, m_queue{queue} , 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); ASL_ASSERT(!m_destroyed);
m_destroyed = true; 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()) if (m_swapchain.has_value())
{ {
@ -417,21 +439,54 @@ public:
return asl::ok(); 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, .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
}; };
VkSemaphore acquire_semaphore{}; VkSemaphore semaphore{};
VkSemaphore queue_semaphore{}; vkCreateSemaphore(m_device, &semaphore_create_info, VK_ALLOCATOR, &semaphore);
vkCreateSemaphore(m_device, &semaphore_create_info, VK_ALLOCATOR, &acquire_semaphore); return semaphore;
vkCreateSemaphore(m_device, &semaphore_create_info, VK_ALLOCATOR, &queue_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{}; 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) if (res != VK_SUCCESS)
{ {
return asl::runtime_error("Couldn't acquire swapchain image: {}", res); 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; VkPipelineStageFlags wait_dst_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkFence fence = create_fence();
VkSubmitInfo submit_info{ VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr, .pNext = nullptr,
.waitSemaphoreCount = 1, .waitSemaphoreCount = 1,
.pWaitSemaphores = &acquire_semaphore, .pWaitSemaphores = &m_swapchain_image_acquire_semaphore,
.pWaitDstStageMask = &wait_dst_stage_mask, .pWaitDstStageMask = &wait_dst_stage_mask,
.commandBufferCount = 1, .commandBufferCount = 1,
.pCommandBuffers = &command_buffer, .pCommandBuffers = &command_buffer,
.signalSemaphoreCount = 1, .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) if (res != VK_SUCCESS)
{ {
return asl::runtime_error("Couldn't submit queue: {}", res); return asl::runtime_error("Couldn't submit queue: {}", res);
@ -565,7 +622,7 @@ public:
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr, .pNext = nullptr,
.waitSemaphoreCount = 1, .waitSemaphoreCount = 1,
.pWaitSemaphores = &queue_semaphore, .pWaitSemaphores = &m_queue_complete_semaphore,
.swapchainCount = 1, .swapchainCount = 1,
.pSwapchains = &m_swapchain.value(), .pSwapchains = &m_swapchain.value(),
.pImageIndices = &image_index, .pImageIndices = &image_index,
@ -578,20 +635,52 @@ public:
return asl::runtime_error("Couldn't present queue: {}", res); return asl::runtime_error("Couldn't present queue: {}", res);
} }
vkDeviceWaitIdle(m_device); auto* frame_resources = asl::alloc_new<FrameResources>(m_allocator);
vkDestroySemaphore(m_device, acquire_semaphore, VK_ALLOCATOR); frame_resources->command_pool = command_pool;
vkDestroySemaphore(m_device, queue_semaphore, VK_ALLOCATOR); frame_resources->complete_fence = fence;
vkDestroyCommandPool(m_device, command_pool, VK_ALLOCATOR);
m_in_flight_frames.push_front(frame_resources);
return asl::ok(); 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 void frame() override
{ {
recycle_resources();
auto s = frame_opt(); auto s = frame_opt();
if (!s.ok()) if (!s.ok())
{ {
ASL_LOG_ERROR("{}", s); ASL_LOG_ERROR("Frame error: {}", s);
} }
} }
}; };

View File

@ -39,4 +39,8 @@
FN(vkCreateSemaphore) \ FN(vkCreateSemaphore) \
FN(vkDestroySemaphore) \ FN(vkDestroySemaphore) \
FN(vkCmdPipelineBarrier) \ FN(vkCmdPipelineBarrier) \
FN(vkCmdClearColorImage) FN(vkCmdClearColorImage) \
FN(vkCreateFence) \
FN(vkDestroyFence) \
FN(vkGetFenceStatus)