diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp | 114 |
1 files changed, 47 insertions, 67 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index 1f2fcd01c7..f00f6001cb 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -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()); -} + if (m_completed_fence_counter >= fence_counter) + return; -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++) + // 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[command_buffer_index].fence == fence) + if (m_frame_resources[index].fence_counter >= fence_counter) break; + + index = (index + 1) % NUM_COMMAND_BUFFERS; } - 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) - return; + 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 |
