mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
Vulkan: Simplify command buffer fence tracking
This commit is contained in:
@ -54,7 +54,6 @@ bool CommandBufferManager::CreateCommandBuffers()
|
||||
{
|
||||
resources.init_command_buffer_used = false;
|
||||
resources.semaphore_used = false;
|
||||
resources.needs_fence_wait = false;
|
||||
|
||||
VkCommandPoolCreateInfo pool_info = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, nullptr, 0,
|
||||
g_vulkan_context->GetGraphicsQueueFamilyIndex()};
|
||||
@ -211,43 +210,61 @@ void CommandBufferManager::WaitForWorkerThreadIdle()
|
||||
m_submit_semaphore.Post();
|
||||
}
|
||||
|
||||
void CommandBufferManager::WaitForGPUIdle()
|
||||
void CommandBufferManager::WaitForFenceCounter(u64 fence_counter)
|
||||
{
|
||||
WaitForWorkerThreadIdle();
|
||||
vkDeviceWaitIdle(g_vulkan_context->GetDevice());
|
||||
}
|
||||
|
||||
void CommandBufferManager::WaitForFence(VkFence fence)
|
||||
{
|
||||
// Find the command buffer that this fence corresponds to.
|
||||
u32 command_buffer_index = 0;
|
||||
for (; command_buffer_index < static_cast<u32>(m_frame_resources.size()); command_buffer_index++)
|
||||
{
|
||||
if (m_frame_resources[command_buffer_index].fence == fence)
|
||||
break;
|
||||
}
|
||||
ASSERT(command_buffer_index < m_frame_resources.size());
|
||||
|
||||
// Has this command buffer already been waited for?
|
||||
if (!m_frame_resources[command_buffer_index].needs_fence_wait)
|
||||
if (m_completed_fence_counter >= fence_counter)
|
||||
return;
|
||||
|
||||
// Find the first command buffer which covers this counter value.
|
||||
u32 index = (m_current_frame + 1) % NUM_COMMAND_BUFFERS;
|
||||
while (index != m_current_frame)
|
||||
{
|
||||
if (m_frame_resources[index].fence_counter >= fence_counter)
|
||||
break;
|
||||
|
||||
index = (index + 1) % NUM_COMMAND_BUFFERS;
|
||||
}
|
||||
|
||||
ASSERT(index != m_current_frame);
|
||||
WaitForCommandBufferCompletion(index);
|
||||
}
|
||||
|
||||
void CommandBufferManager::WaitForCommandBufferCompletion(u32 index)
|
||||
{
|
||||
// Ensure this command buffer has been submitted.
|
||||
WaitForWorkerThreadIdle();
|
||||
|
||||
// Wait for this command buffer to be completed.
|
||||
VkResult res =
|
||||
vkWaitForFences(g_vulkan_context->GetDevice(), 1,
|
||||
&m_frame_resources[command_buffer_index].fence, VK_TRUE, UINT64_MAX);
|
||||
VkResult res = vkWaitForFences(g_vulkan_context->GetDevice(), 1, &m_frame_resources[index].fence,
|
||||
VK_TRUE, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
|
||||
// Immediately fire callbacks and cleanups, since the commands has been completed.
|
||||
m_frame_resources[command_buffer_index].needs_fence_wait = false;
|
||||
OnCommandBufferExecuted(command_buffer_index);
|
||||
// Clean up any resources for command buffers between the last known completed buffer and this
|
||||
// now-completed command buffer. If we use >2 buffers, this may be more than one buffer.
|
||||
const u64 now_completed_counter = m_frame_resources[index].fence_counter;
|
||||
u32 cleanup_index = (m_current_frame + 1) % NUM_COMMAND_BUFFERS;
|
||||
while (cleanup_index != m_current_frame)
|
||||
{
|
||||
FrameResources& resources = m_frame_resources[cleanup_index];
|
||||
if (resources.fence_counter > now_completed_counter)
|
||||
break;
|
||||
|
||||
if (resources.fence_counter > m_completed_fence_counter)
|
||||
{
|
||||
for (auto& it : resources.cleanup_resources)
|
||||
it();
|
||||
resources.cleanup_resources.clear();
|
||||
}
|
||||
|
||||
cleanup_index = (cleanup_index + 1) % NUM_COMMAND_BUFFERS;
|
||||
}
|
||||
|
||||
m_completed_fence_counter = now_completed_counter;
|
||||
}
|
||||
|
||||
void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
|
||||
bool wait_for_completion,
|
||||
VkSwapchainKHR present_swap_chain,
|
||||
uint32_t present_image_index)
|
||||
{
|
||||
@ -263,16 +280,13 @@ void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
|
||||
}
|
||||
}
|
||||
|
||||
// This command buffer now has commands, so can't be re-used without waiting.
|
||||
resources.needs_fence_wait = true;
|
||||
|
||||
// Grab the semaphore before submitting command buffer either on-thread or off-thread.
|
||||
// This prevents a race from occurring where a second command buffer is executed
|
||||
// before the worker thread has woken and executed the first one yet.
|
||||
m_submit_semaphore.Wait();
|
||||
|
||||
// Submitting off-thread?
|
||||
if (m_use_threaded_submission && submit_on_worker_thread)
|
||||
if (m_use_threaded_submission && submit_on_worker_thread && !wait_for_completion)
|
||||
{
|
||||
// Push to the pending submit queue.
|
||||
{
|
||||
@ -287,6 +301,8 @@ void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
|
||||
{
|
||||
// Pass through to normal submission path.
|
||||
SubmitCommandBuffer(m_current_frame, present_swap_chain, present_image_index);
|
||||
if (wait_for_completion)
|
||||
WaitForCommandBufferCompletion(m_current_frame);
|
||||
}
|
||||
|
||||
// Switch to next cmdbuffer.
|
||||
@ -365,39 +381,15 @@ void CommandBufferManager::SubmitCommandBuffer(u32 command_buffer_index,
|
||||
m_submit_semaphore.Post();
|
||||
}
|
||||
|
||||
void CommandBufferManager::OnCommandBufferExecuted(u32 index)
|
||||
{
|
||||
FrameResources& resources = m_frame_resources[index];
|
||||
|
||||
// Fire fence tracking callbacks.
|
||||
for (auto iter = m_fence_callbacks.begin(); iter != m_fence_callbacks.end();)
|
||||
{
|
||||
auto backup_iter = iter++;
|
||||
backup_iter->second(resources.fence);
|
||||
}
|
||||
|
||||
// Clean up all objects pending destruction on this command buffer
|
||||
for (auto& it : resources.cleanup_resources)
|
||||
it();
|
||||
resources.cleanup_resources.clear();
|
||||
}
|
||||
|
||||
void CommandBufferManager::BeginCommandBuffer()
|
||||
{
|
||||
// Move to the next command buffer.
|
||||
m_current_frame = (m_current_frame + 1) % NUM_COMMAND_BUFFERS;
|
||||
FrameResources& resources = m_frame_resources[m_current_frame];
|
||||
const u32 next_buffer_index = (m_current_frame + 1) % NUM_COMMAND_BUFFERS;
|
||||
FrameResources& resources = m_frame_resources[next_buffer_index];
|
||||
|
||||
// Wait for the GPU to finish with all resources for this command buffer.
|
||||
if (resources.needs_fence_wait)
|
||||
{
|
||||
VkResult res =
|
||||
vkWaitForFences(g_vulkan_context->GetDevice(), 1, &resources.fence, true, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
|
||||
OnCommandBufferExecuted(m_current_frame);
|
||||
}
|
||||
if (resources.fence_counter > m_completed_fence_counter)
|
||||
WaitForCommandBufferCompletion(next_buffer_index);
|
||||
|
||||
// Reset fence to unsignaled before starting.
|
||||
VkResult res = vkResetFences(g_vulkan_context->GetDevice(), 1, &resources.fence);
|
||||
@ -427,6 +419,8 @@ void CommandBufferManager::BeginCommandBuffer()
|
||||
// Reset upload command buffer state
|
||||
resources.init_command_buffer_used = false;
|
||||
resources.semaphore_used = false;
|
||||
resources.fence_counter = m_next_fence_counter++;
|
||||
m_current_frame = next_buffer_index;
|
||||
}
|
||||
|
||||
void CommandBufferManager::DeferBufferDestruction(VkBuffer object)
|
||||
@ -471,19 +465,5 @@ void CommandBufferManager::DeferImageViewDestruction(VkImageView object)
|
||||
[object]() { vkDestroyImageView(g_vulkan_context->GetDevice(), object, nullptr); });
|
||||
}
|
||||
|
||||
void CommandBufferManager::AddFenceSignaledCallback(const void* key, FenceSignaledCallback callback)
|
||||
{
|
||||
// Shouldn't be adding twice.
|
||||
ASSERT(m_fence_callbacks.find(key) == m_fence_callbacks.end());
|
||||
m_fence_callbacks.emplace(key, std::move(callback));
|
||||
}
|
||||
|
||||
void CommandBufferManager::RemoveFenceSignaledCallback(const void* key)
|
||||
{
|
||||
auto iter = m_fence_callbacks.find(key);
|
||||
ASSERT(iter != m_fence_callbacks.end());
|
||||
m_fence_callbacks.erase(iter);
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandBufferManager> g_command_buffer_mgr;
|
||||
} // namespace Vulkan
|
||||
|
Reference in New Issue
Block a user