Allow to have multiple frames in flight
This commit is contained in:
@ -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()}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +316,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())
|
||||||
{
|
{
|
||||||
vkDestroySwapchainKHR(m_device, m_swapchain.value(), VK_ALLOCATOR);
|
vkDestroySwapchainKHR(m_device, m_swapchain.value(), VK_ALLOCATOR);
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user