From 46028f2c482f9a64f167b94cccebbafe9d08591a Mon Sep 17 00:00:00 2001 From: Egor Yusov Date: Sat, 22 Sep 2018 11:37:50 -0700 Subject: Reworked upload allocation release process in Vk backend --- .../include/DeviceContextVkImpl.h | 7 +++++ .../include/RenderDeviceVkImpl.h | 3 +- .../include/VulkanUploadHeap.h | 4 +-- .../src/CommandQueueVkImpl.cpp | 2 +- .../src/DeviceContextVkImpl.cpp | 19 ++++++++++-- .../src/RenderDeviceVkImpl.cpp | 13 +++++---- .../GraphicsEngineVulkan/src/VulkanUploadHeap.cpp | 34 ++++------------------ 7 files changed, 42 insertions(+), 40 deletions(-) (limited to 'Graphics/GraphicsEngineVulkan') diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h index 6249c1d6..ed3d2645 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h @@ -243,6 +243,13 @@ private: const Uint32 m_ContextId; const Uint32 m_CommandQueueId; + // This mask indicates which command queues command buffers from this context were submitted to. + // For immediate context, this will always be 1 << m_CommandQueueId. + // For deferred contexts, this will accumulate bits of the queues to which command buffers + // were submitted to before FinishFrame() was called. This mask is used to release resources + // allocated by the context during the frame when FinishFrame() is called. + Uint64 m_SubmittedBuffersCmdQueueMask = 0; + VulkanUtilities::VulkanCommandBufferPool m_CmdPool; // Semaphores are not owned by the command context diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h index 873170fd..a1072089 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h @@ -119,7 +119,8 @@ public: VulkanUtilities::VulkanMemoryManager& GetGlobalMemoryManager() { return m_MemoryMgr; } VulkanDynamicMemoryManager& GetDynamicMemoryManager() { return m_DynamicMemoryManager; } - + void FlushStaleResources(Uint32 CmdQueueIndex); + private: virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat )override final; diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUploadHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanUploadHeap.h index c1d0e5a9..1b05b2f0 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUploadHeap.h +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUploadHeap.h @@ -66,9 +66,9 @@ public: ~VulkanUploadHeap(); VulkanUploadAllocation Allocate(size_t SizeInBytes, size_t Alignment); - void DiscardAllocations(Uint32 CommandQueueIndex, uint64_t FenceValue); + void ReleaseAllocatedPages(Uint64 CmdQueueMask); - size_t GetStaleAllocationsCount()const + size_t GetStalePagesCount()const { return m_Pages.size(); } diff --git a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp index 8ec55707..6e3361d3 100644 --- a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp @@ -58,7 +58,7 @@ Uint64 CommandQueueVkImpl::Submit(const VkSubmitInfo& SubmitInfo) Atomics::AtomicIncrement(m_NextFenceValue); auto vkFence = m_pFence->GetVkFence(); - bool SubmitCount = + uint32_t SubmitCount = (SubmitInfo.waitSemaphoreCount != 0 || SubmitInfo.commandBufferCount != 0 || SubmitInfo.signalSemaphoreCount != 0) ? diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index eb314ec4..d3aba87d 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -70,6 +70,7 @@ namespace Diligent m_CmdListAllocator{ GetRawAllocator(), sizeof(CommandListVkImpl), 64 }, m_ContextId{ContextId}, m_CommandQueueId{CommandQueueId}, + m_SubmittedBuffersCmdQueueMask{bIsDeferred ? 0 : Uint64{1} << CommandQueueId}, // Command pools for deferred contexts must be thread safe because finished command buffers are executed and released from another thread m_CmdPool { @@ -149,7 +150,7 @@ namespace Diligent // There must be no resources in the stale resource list. For immediate context, all stale resources must have been // moved to the release queue by Flush(). For deferred contexts, this should have happened in the last FinishCommandList() // call. - VERIFY(m_UploadHeap.GetStaleAllocationsCount() == 0, "All stale allocations must have been discarded at this point"); + VERIFY(m_UploadHeap.GetStalePagesCount() == 0, "All stale pages must have been discarded at this point"); VERIFY(m_DynamicDescriptorPool.GetStaleAllocationCount() == 0, "All stale dynamic descriptor set allocations must have been discarded at this point"); // TODO: rework ReleaseStaleContextResources(m_NextCmdBuffNumber, m_LastSubmittedFenceValue, pDeviceVkImpl->GetCompletedFenceValue(0)); @@ -752,9 +753,20 @@ namespace Diligent "There are outstanding commands in the deferred device context when finishing the frame. This is an error and may cause unpredicted behaviour. Close all deferred contexts and execute them before finishing the frame" : "There are outstanding commands in the immediate device context when finishing the frame. This is an error and may cause unpredicted behaviour. Call Flush() to submit all commands for execution before finishing the frame"); - m_UploadHeap.DiscardAllocations(m_CommandQueueId, m_LastSubmittedFenceValue); + VERIFY_EXPR(m_bIsDeferred || m_SubmittedBuffersCmdQueueMask == (Uint64{1}<()->FlushStaleResources(m_CommandQueueId); + } + Atomics::AtomicIncrement(m_ContextFrameNumber); } @@ -1440,7 +1452,8 @@ namespace Diligent auto pDeferredCtxVkImpl = pDeferredCtx.RawPtr(); auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, nullptr); pDeferredCtxVkImpl->m_LastSubmittedFenceValue = SubmittedFenceValue; - + // Set the bit in the deferred context cmd queue mask corresponding to cmd queue of this context + pDeferredCtxVkImpl->m_SubmittedBuffersCmdQueueMask |= Uint64{1} << m_CommandQueueId; // It is OK to dispose command buffer from another thread. We are not going to // record any commands and only need to add the buffer to the queue pDeferredCtxVkImpl->DisposeVkCmdBuffer(vkCmdBuff, SubmittedFenceValue); diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index 8d5662e2..a313b44b 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -270,6 +270,13 @@ void RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects) } } +void RenderDeviceVkImpl::FlushStaleResources(Uint32 CmdQueueIndex) +{ + // Submit empty command buffer to the queue. This will effectively signal the fence and + // discard all resources + VkSubmitInfo DummySumbitInfo = {}; + TRenderDeviceBase::SubmitCommandBuffer(0, DummySumbitInfo, true); +} void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) { @@ -277,13 +284,9 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) // no command lists submitted during the frame. All stale resources will // be associated with the submitted fence value and thus will not be released // until the GPU is finished with the current frame - Uint64 SubmittedFenceValue = 0; - Uint64 SubmittedCmdBuffNumber = 0; - VkSubmitInfo DummySubmitInfo = {}; // TODO: Rework - // Submit empty command buffer to set a fence on the GPU - auto CmbBuffInfo = TRenderDeviceBase::SubmitCommandBuffer(0, DummySubmitInfo, true); + FlushStaleResources(0); // TODO: rework this auto CompletedFenceValue = ReleaseAllResources ? std::numeric_limits::max() : m_CommandQueues[0].CmdQueue->GetCompletedFenceValue(); diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp index 4dd0caa0..4b96f3b7 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp @@ -126,36 +126,14 @@ VulkanUploadAllocation VulkanUploadHeap::Allocate(size_t SizeInBytes, size_t Ali return Allocation; } -void VulkanUploadHeap::DiscardAllocations(Uint32 CommandQueueIndex, uint64_t FenceValue) +void VulkanUploadHeap::ReleaseAllocatedPages(Uint64 CmdQueueMask) { - auto& ReleaseQueue = m_RenderDevice.GetReleaseQueue(CommandQueueIndex); - - { - auto AllocIt = m_Pages.begin(); - ReleaseQueue.DiscardResources(FenceValue, [&](VulkanUtilities::VulkanMemoryAllocation& MemAllocation) - { - if(AllocIt != m_Pages.end()) - { - MemAllocation = std::move(AllocIt->MemAllocation); - ++AllocIt; - return true; - } - return false; - }); - } - + // The pages will go into the stale resources queue first, however they will move into the release + // queue rightaway when RenderDeviceVkImpl::FlushStaleResources() is called by the DeviceContextVkImpl::FinishFrame() + for (auto& Page : m_Pages) { - auto AllocIt = m_Pages.begin(); - ReleaseQueue.DiscardResources(FenceValue, [&](VulkanUtilities::BufferWrapper& Buffer) - { - if(AllocIt != m_Pages.end()) - { - Buffer = std::move(AllocIt->Buffer); - ++AllocIt; - return true; - } - return false; - }); + m_RenderDevice.SafeReleaseDeviceObject(std::move(Page.MemAllocation), CmdQueueMask); + m_RenderDevice.SafeReleaseDeviceObject(std::move(Page.Buffer), CmdQueueMask); } m_Pages.clear(); -- cgit v1.2.3