diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2018-06-06 03:56:10 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2018-06-06 03:56:10 +0000 |
| commit | 186151aec243c105d8e9c0fcff6a9c342b7d168b (patch) | |
| tree | 716cb5be17d6e1c2e33a413a04932743e79871f0 /Graphics/GraphicsEngineVulkan | |
| parent | Reworked upload heap in Vulkan (diff) | |
| download | DiligentCore-186151aec243c105d8e9c0fcff6a9c342b7d168b.tar.gz DiligentCore-186151aec243c105d8e9c0fcff6a9c342b7d168b.zip | |
Improvements to resource liftime management in Vulkan
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
6 files changed, 193 insertions, 174 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h index e14ef3c1..0cc0ebb7 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h @@ -143,7 +143,7 @@ private: void CommitRenderTargets(); void CommitViewports(); void CommitScissorRects(); - + inline void EnsureVkCmdBuffer(); inline void DisposeVkCmdBuffer(VkCommandBuffer vkCmdBuff); inline void DisposeCurrentCmdBuffer(); diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h index 7ab4df4e..bda52b94 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h @@ -96,12 +96,11 @@ public: ICommandQueueVk *GetCmdQueue(){return m_pCommandQueue;} void IdleGPU(bool ReleaseStaleObjects); - void ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, bool DiscardStaleObjects); - void ExecuteCommandBuffer(VkCommandBuffer CmdBuff, bool DiscardStaleObjects); - - void AllocateTransientCmdPool(VulkanUtilities::CommandPoolWrapper& CmdPool, VkCommandBuffer& vkCmdBuff, const Char* DebugPoolName = nullptr); - void DisposeTransientCmdPool(VulkanUtilities::CommandPoolWrapper&& CmdPool); + // pImmediateCtx parameter is only used to make sure the command buffer is submitted from the immediate context + void ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, class DeviceContextVkImpl* pImmediateCtx); + void AllocateTransientCmdPool(VulkanUtilities::CommandPoolWrapper& CmdPool, VkCommandBuffer& vkCmdBuff, const Char* DebugPoolName = nullptr); + void ExecuteAndDisposeTransientCmdBuff(VkCommandBuffer vkCmdBuff, VulkanUtilities::CommandPoolWrapper&& CmdPool); template<typename ObjectType> void SafeReleaseVkObject(ObjectType&& Object); @@ -138,7 +137,14 @@ public: private: virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat )override final; void ProcessReleaseQueue(Uint64 CompletedFenceValue); - void DiscardStaleVkObjects(Uint64 CmdListNumber, Uint64 FenceValue); + void DiscardStaleVkObjects(Uint64 CmdBuffNumber, Uint64 FenceValue); + + // Submits command buffer for execution to the command queue + // Returns the submitted command buffer number and the fence value that has been set to signal by GPU + // Parameters: + // * SubmittedCmdBuffNumber - submitted command buffer number + // * SubmittedFenceValue - fence value associated with the submitted command buffer + void SubmitCommandBuffer(const VkSubmitInfo& SubmitInfo, Uint64& SubmittedCmdBuffNumber, Uint64& SubmittedFenceValue); std::shared_ptr<VulkanUtilities::VulkanInstance> m_VulkanInstance; std::unique_ptr<VulkanUtilities::VulkanPhysicalDevice> m_PhysicalDevice; @@ -150,13 +156,12 @@ private: EngineVkAttribs m_EngineAttribs; Atomics::AtomicInt64 m_FrameNumber; - Atomics::AtomicInt64 m_NextCmdListNumber; -#if 0 + Atomics::AtomicInt64 m_NextCmdBuffNumber; + // The following basic requirement guarantees correctness of resource deallocation: // // A resource is never released before the last draw command referencing it is invoked on the immediate context // - // See http://diligentgraphics.com/diligent-engine/architecture/Vk/managing-resource-lifetimes/ // // CPU @@ -180,12 +185,6 @@ private: // Resource X can // be released - CommandListManager m_CmdListManager; - - typedef std::unique_ptr<CommandContext, STDDeleterRawMem<CommandContext> > ContextPoolElemType; - std::vector< ContextPoolElemType, STDAllocatorRawMem<ContextPoolElemType> > m_ContextPool; - std::deque<CommandContext*, STDAllocatorRawMem<CommandContext*> > m_AvailableContexts; -#endif std::mutex m_ReleaseQueueMutex; diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp index fa5170ae..99d5267e 100644 --- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp @@ -156,31 +156,33 @@ BufferVkImpl :: BufferVkImpl(IReferenceCounters *pRefCounters, BuffCopy.size = VkBuffCI.size; vkCmdCopyBuffer(vkCmdBuff, StagingBuffer, m_VulkanBuffer, 1, &BuffCopy); - // Command list fence should only be signaled when submitting cmd list - // from the immediate context, otherwise the basic requirement will be violated - // as in the scenario below - // - // Signaled Fence | Immediate Context | InitContext | - // | | | - // N | Draw(ResourceX) | | - // | Release(ResourceX) | | - // | - (ResourceX, N) -> Release Queue | | - // | | CopyResource() | - // N+1 | | CloseAndExecuteCommandContext() | - // | | | - // N+2 | CloseAndExecuteCommandContext() | | - // | - Cmd list is submitted with number | | - // | N+1, but resource it references | | - // | was added to the delete queue | | - // | with value N | | - pRenderDeviceVk->ExecuteCommandBuffer(vkCmdBuff, false); - // Dispose command pool. No need to dispose cmd buffer as the - // pool will be reset and all buffer resources will be reclaimed - pRenderDeviceVk->DisposeTransientCmdPool(std::move(CmdPool)); - - // Add reference to the object to the release queue to keep it alive - // until copy operation is complete. This must be done after - // submitting command list for execution! + pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(vkCmdBuff, std::move(CmdPool)); + + + // After command buffer is submitted, safe-release staging resources. This strategy + // is little overconservative as the resources will only be released after the + // first command buffer submitted through the immediate context is complete + + // Next Cmd Buff| Next Fence | This Thread | Immediate Context + // | | | + // N | F | | + // | | | + // | | ExecuteAndDisposeTransientCmdBuff(vkCmdBuff) | + // | | - SubmittedCmdBuffNumber = N | + // | | - SubmittedFenceValue = F | + // N+1 - - | - F+1 - | | + // | | Release(StagingBuffer) | + // | | - {N+1, StagingBuffer} -> Stale Objects | + // | | | + // | | | + // | | | ExecuteCommandBuffer() + // | | | - SubmittedCmdBuffNumber = N+1 + // | | | - SubmittedFenceValue = F+1 + // N+2 - - | - F+2 - | - - - - - - - - - - - - | + // | | | - DiscardStaleVkObjects(N+1, F+1) + // | | | - {F+1, StagingBuffer} -> Release Queue + // | | | + pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBuffer)); pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingMemoryAllocation)); } @@ -254,6 +256,7 @@ BufferVkImpl :: BufferVkImpl(IReferenceCounters *pRefCounters, } #endif } + BufferVkImpl :: ~BufferVkImpl() { auto *pDeviceVkImpl = ValidatedCast<RenderDeviceVkImpl>(GetDevice()); diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 252142e1..cc41a10c 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -761,7 +761,7 @@ namespace Diligent // Submit command buffer even if there are no commands to release stale resources. //if(SubmitInfo.commandBufferCount != 0 || SubmitInfo.waitSemaphoreCount !=0 || SubmitInfo.signalSemaphoreCount != 0) { - pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, true); + pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this); } m_WaitSemaphores.clear(); @@ -1056,6 +1056,9 @@ namespace Diligent void DeviceContextVkImpl::FinishCommandList(class ICommandList **ppCommandList) { auto vkCmdBuff = m_CommandBuffer.GetVkCmdBuffer(); + auto err = vkEndCommandBuffer(vkCmdBuff); + VERIFY(err == VK_SUCCESS, "Failed to end command buffer"); + CommandListVkImpl *pCmdListVk( NEW_RC_OBJ(m_CmdListAllocator, "CommandListVkImpl instance", CommandListVkImpl) (m_pDevice, this, vkCmdBuff) ); pCmdListVk->QueryInterface( IID_CommandList, reinterpret_cast<IObject**>(ppCommandList) ); @@ -1079,8 +1082,27 @@ namespace Diligent } // First execute commands in this context + + // Note that not discarding resources when flushing the context does not help in case of multiple + // deferred contexts, and resources must not be released until the command list is executed via + // immediate context. + + // Next Cmd Buff| Next Fence | Deferred Context 1 | Deferred Contex 2 | Immediate Context + // | | | | + // N | F | | | + // | | Draw(ResourceX) | | + // | | Release(ResourceX) | Draw(ResourceY) | + // | | - {N, ResourceX} -> Stale Objs | Release(ResourceY) | + // | | | - {N, ResourceY} -> Stale Objs | + // | | | | ExecuteCmdList(CmdList1) + // | | | | {F, ResourceX}-> Release queue + // | | | | {F, ResourceY}-> Release queue + // N+1 | F+1 | | | + // | | | | ExecuteCmdList(CmdList2) + // | | | | - ResourceY is in release queue + // | | | | Flush(); - + InvalidateState(); CommandListVkImpl* pCmdListVk = ValidatedCast<CommandListVkImpl>(pCommandList); @@ -1089,7 +1111,11 @@ namespace Diligent pCmdListVk->Close(vkCmdBuff, pDeferredCtx); VERIFY(vkCmdBuff != VK_NULL_HANDLE, "Trying to execute empty command buffer"); VERIFY_EXPR(pDeferredCtx); - m_pDevice.RawPtr<RenderDeviceVkImpl>()->ExecuteCommandBuffer(vkCmdBuff, true); + VkSubmitInfo SubmitInfo = {}; + SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + SubmitInfo.commandBufferCount = 1; + SubmitInfo.pCommandBuffers = &vkCmdBuff; + m_pDevice.RawPtr<RenderDeviceVkImpl>()->ExecuteCommandBuffer(SubmitInfo, this); // 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 pDeferredCtx.RawPtr<DeviceContextVkImpl>()->DisposeVkCmdBuffer(vkCmdBuff); diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index 2433d657..e46254ec 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -52,7 +52,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters, m_pCommandQueue(pCmdQueue), m_EngineAttribs(CreationAttribs), m_FrameNumber(0), - m_NextCmdListNumber(0), + m_NextCmdBuffNumber(0), /*m_CmdListManager(this), m_ContextPool(STD_ALLOCATOR_RAW_MEM(ContextPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<CommandContext>>")), m_AvailableContexts(STD_ALLOCATOR_RAW_MEM(CommandContext*, GetRawAllocator(), "Allocator for vector<CommandContext*>")),*/ @@ -156,73 +156,120 @@ RenderDeviceVkImpl::~RenderDeviceVkImpl() } +void RenderDeviceVkImpl::AllocateTransientCmdPool(VulkanUtilities::CommandPoolWrapper& CmdPool, VkCommandBuffer& vkCmdBuff, const Char *DebugPoolName) +{ + auto CompletedFenceValue = GetCompletedFenceValue(); + CmdPool = m_TransientCmdPoolMgr.AllocateCommandPool(CompletedFenceValue, DebugPoolName); -void RenderDeviceVkImpl::ExecuteCommandBuffer(VkCommandBuffer CmdBuff, bool DiscardStaleObjects) + // Allocate command buffer from the cmd pool + VkCommandBufferAllocateInfo BuffAllocInfo = {}; + BuffAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + BuffAllocInfo.pNext = nullptr; + BuffAllocInfo.commandPool = CmdPool; + BuffAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + BuffAllocInfo.commandBufferCount = 1; + vkCmdBuff = m_LogicalVkDevice->AllocateVkCommandBuffer(BuffAllocInfo); + VERIFY_EXPR(vkCmdBuff != VK_NULL_HANDLE); + + VkCommandBufferBeginInfo CmdBuffBeginInfo = {}; + CmdBuffBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + CmdBuffBeginInfo.pNext = nullptr; + CmdBuffBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; // Each recording of the command buffer will only be + // submitted once, and the command buffer will be reset + // and recorded again between each submission. + CmdBuffBeginInfo.pInheritanceInfo = nullptr; // Ignored for a primary command buffer + vkBeginCommandBuffer(vkCmdBuff, &CmdBuffBeginInfo); +} + + +void RenderDeviceVkImpl::ExecuteAndDisposeTransientCmdBuff(VkCommandBuffer vkCmdBuff, VulkanUtilities::CommandPoolWrapper&& CmdPool) { - VERIFY_EXPR(CmdBuff != VK_NULL_HANDLE); - auto err = vkEndCommandBuffer(CmdBuff); + VERIFY_EXPR(vkCmdBuff != VK_NULL_HANDLE); + + auto err = vkEndCommandBuffer(vkCmdBuff); VERIFY(err == VK_SUCCESS, "Failed to end command buffer"); VkSubmitInfo SubmitInfo = {}; SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; SubmitInfo.commandBufferCount = 1; - SubmitInfo.pCommandBuffers = &CmdBuff; - ExecuteCommandBuffer(SubmitInfo, DiscardStaleObjects); + SubmitInfo.pCommandBuffers = &vkCmdBuff; + + Uint64 SubmittedFenceValue = 0; + Uint64 SubmittedCmdBuffNumber = 0; + SubmitCommandBuffer(SubmitInfo, SubmittedCmdBuffNumber, SubmittedFenceValue); + + + // We MUST NOT discard stale objects when executing transient command buffer, + // otherwise a resource can be destroyed while still being used by the GPU: + // + // + // Next Cmd Buff| Next Fence | Immediate Context | This thread | + // | | | | + // N | F | | | + // | | Draw(ResourceX) | | + // N - - | - - - | Release(ResourceX) | | + // | | - {N, ResourceX} -> Stale Objects | | + // | | | | + // | | | SubmitCommandBuffer() | + // | | | - SubmittedCmdBuffNumber = N | + // | | | - SubmittedFenceValue = F | + // N+1 | F+1 | | - DiscardStaleVkObjects(N, F) | + // | | | - {F, ResourceX} -> Release Queue | + // | | | | + // N+2 - -| - F+2 - | ExecuteCommandBuffer() | | + // | | - SubmitCommandBuffer() | | + // | | - ResourceX is already in release | | + // | | queue with fence value F, and | | + // | | F < SubmittedFenceValue==F+1 | | + + + // Dispose command pool + m_TransientCmdPoolMgr.DisposeCommandPool(std::move(CmdPool), SubmittedFenceValue); } +void RenderDeviceVkImpl::SubmitCommandBuffer(const VkSubmitInfo& SubmitInfo, + Uint64& SubmittedCmdBuffNumber, // Number of the submitted command buffer + Uint64& SubmittedFenceValue // Fence value associated with the submitted command buffer + ) +{ + std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); + auto NextFenceValue = m_pCommandQueue->GetNextFenceValue(); + // Submit the command list to the queue + SubmittedFenceValue = m_pCommandQueue->ExecuteCommandBuffer(SubmitInfo); + VERIFY(SubmittedFenceValue >= NextFenceValue, "Fence value of the executed command list is less than the next fence value previously queried through GetNextFenceValue()"); + SubmittedFenceValue = std::max(SubmittedFenceValue, NextFenceValue); + SubmittedCmdBuffNumber = m_NextCmdBuffNumber; + Atomics::AtomicIncrement(m_NextCmdBuffNumber); +} -void RenderDeviceVkImpl::ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, bool DiscardStaleObjects) +void RenderDeviceVkImpl::ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, DeviceContextVkImpl* pImmediateCtx) { - Uint64 FenceValue = 0; - Uint64 CmdListNumber = 0; - { - std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); - auto NextFenceValue = m_pCommandQueue->GetNextFenceValue(); - // Submit the command list to the queue - FenceValue = m_pCommandQueue->ExecuteCommandBuffer(SubmitInfo); - VERIFY(FenceValue >= NextFenceValue, "Fence value of the executed command list is less than the next fence value previously queried through GetNextFenceValue()"); - FenceValue = std::max(FenceValue, NextFenceValue); - CmdListNumber = m_NextCmdListNumber; - Atomics::AtomicIncrement(m_NextCmdListNumber); - } + // pImmediateCtx parameter is only used to make sure the command buffer is submitted from the immediate context + // Stale objects MUST only be discarded when submitting cmd list from the immediate context + VERIFY(!pImmediateCtx->IsDeferred(), "Command buffers must be submitted from immediate context only"); + + Uint64 SubmittedFenceValue = 0; + Uint64 SubmittedCmdBuffNumber = 0; + SubmitCommandBuffer(SubmitInfo, SubmittedCmdBuffNumber, SubmittedFenceValue); + + // The following basic requirement guarantees correctness of resource deallocation: + // + // A resource is never released before the last draw command referencing it is invoked on the immediate context + // + + // Move stale objects into the release queue. + // Note that objects are moved from stale list to release queue based on the cmd buffer number, + // not fence value. This makes sure that basic requirement is met even when the fence value is + // not incremented while executing the command buffer (as is the case with Unity command queue). + + // As long as resources used by deferred contexts are not released until the command list + // is executed through immediate context, this stategy always works. + + DiscardStaleVkObjects(SubmittedCmdBuffNumber, SubmittedFenceValue); + auto CompletedFenceValue = GetCompletedFenceValue(); + ProcessReleaseQueue(CompletedFenceValue); + m_MemoryMgr.ShrinkMemory(); - if (DiscardStaleObjects) - { - // The following basic requirement guarantees correctness of resource deallocation: - // - // A resource is never released before the last draw command referencing it is invoked on the immediate context - // - // See http://diligentgraphics.com/diligent-engine/architecture/Vk/managing-resource-lifetimes/ - - // Stale objects should only be discarded when submitting cmd list from - // the immediate context, otherwise the basic requirement may be violated - // as in the following scenario - // - // Signaled | | - // Fence Value | Immediate Context | InitContext | - // | | | - // N | Draw(ResourceX) | | - // | Release(ResourceX) | | - // | - (ResourceX, N) -> Release Queue | | - // | | CopyResource() | - // N+1 | | CloseAndExecuteCommandContext() | - // | | | - // N+2 | CloseAndExecuteCommandContext() | | - // | - Cmd list is submitted with number | | - // | N+1, but resource it references | | - // | was added to the delete queue | | - // | with number N | | - - // Move stale objects into the release queue. - // Note that objects are moved from stale list to release queue based on the - // cmd list number, not the fence value. This makes sure that basic requirement - // is met even when the fence value is not incremented while executing - // the command list (as is the case with Unity command queue). - DiscardStaleVkObjects(CmdListNumber, FenceValue); - auto CompletedFenceValue = GetCompletedFenceValue(); - ProcessReleaseQueue(CompletedFenceValue); - m_MemoryMgr.ShrinkMemory(); - } #if 0 // DiscardAllocator() is thread-safe m_CmdListManager.DiscardAllocator(FenceValue, pAllocator); @@ -240,7 +287,7 @@ void RenderDeviceVkImpl::ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, bo void RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects) { Uint64 FenceValue = 0; - Uint64 CmdListNumber = 0; + Uint64 CmdBuffNumber = 0; { // Lock the command queue to avoid other threads interfering with the GPU @@ -253,8 +300,8 @@ void RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects) // Increment cmd list number while keeping queue locked. // This guarantees that any Vk object released after the lock // is released, will be associated with the incremented cmd list number - CmdListNumber = m_NextCmdListNumber; - Atomics::AtomicIncrement(m_NextCmdListNumber); + CmdBuffNumber = m_NextCmdBuffNumber; + Atomics::AtomicIncrement(m_NextCmdBuffNumber); } if (ReleaseStaleObjects) @@ -263,9 +310,10 @@ void RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects) // This is necessary to release outstanding references to the // swap chain buffers when it is resized in the middle of the frame. // Since GPU has been idled, it it is safe to do so - DiscardStaleVkObjects(CmdListNumber, FenceValue); + DiscardStaleVkObjects(CmdBuffNumber, FenceValue); // FenceValue has now been signaled by the GPU since we waited for it - ProcessReleaseQueue(FenceValue); + auto CompletedFenceValue = FenceValue; + ProcessReleaseQueue(CompletedFenceValue); m_MemoryMgr.ShrinkMemory(); } } @@ -310,7 +358,7 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) // command list was submitted for execution (Unity only // increments fence value once per frame) Uint64 NextFenceValue = 0; - Uint64 CmdListNumber = 0; + Uint64 CmdBuffNumber = 0; { // Lock the command queue to avoid other threads interfering with the GPU std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); @@ -318,8 +366,8 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) // Increment cmd list number while keeping queue locked. // This guarantees that any Vk object released after the lock // is released, will be associated with the incremented cmd list number - CmdListNumber = m_NextCmdListNumber; - Atomics::AtomicIncrement(m_NextCmdListNumber); + CmdBuffNumber = m_NextCmdBuffNumber; + Atomics::AtomicIncrement(m_NextCmdBuffNumber); } { @@ -360,49 +408,13 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) // Discard all remaining objects. This is important to do if there were // no command lists submitted during the frame - DiscardStaleVkObjects(CmdListNumber, NextFenceValue); + DiscardStaleVkObjects(CmdBuffNumber, NextFenceValue); ProcessReleaseQueue(CompletedFenceValue); m_MemoryMgr.ShrinkMemory(); Atomics::AtomicIncrement(m_FrameNumber); } -void RenderDeviceVkImpl::AllocateTransientCmdPool(VulkanUtilities::CommandPoolWrapper& CmdPool, VkCommandBuffer& vkCmdBuff, const Char *DebugPoolName) -{ - auto CompletedFenceValue = GetCompletedFenceValue(); - CmdPool = m_TransientCmdPoolMgr.AllocateCommandPool(CompletedFenceValue, DebugPoolName); - - // Allocate command buffer from the cmd pool - VkCommandBufferAllocateInfo BuffAllocInfo = {}; - BuffAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - BuffAllocInfo.pNext = nullptr; - BuffAllocInfo.commandPool = CmdPool; - BuffAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - BuffAllocInfo.commandBufferCount = 1; - vkCmdBuff = m_LogicalVkDevice->AllocateVkCommandBuffer(BuffAllocInfo); - VERIFY_EXPR(vkCmdBuff != VK_NULL_HANDLE); - - VkCommandBufferBeginInfo CmdBuffBeginInfo = {}; - CmdBuffBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - CmdBuffBeginInfo.pNext = nullptr; - CmdBuffBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; // Each recording of the command buffer will only be - // submitted once, and the command buffer will be reset - // and recorded again between each submission. - CmdBuffBeginInfo.pInheritanceInfo = nullptr; // Ignored for a primary command buffer - vkBeginCommandBuffer(vkCmdBuff, &CmdBuffBeginInfo); -} - -void RenderDeviceVkImpl::DisposeTransientCmdPool(VulkanUtilities::CommandPoolWrapper&& CmdPool) -{ - // Command pools must be disposed after the corresponding command buffer has been submitted to the queue. - // At this point the fence value has already been incremented, so the pool can be added to the queue. - // There is no need to go through stale objects queue as GetNextFenceValue() will be at least - // one greater than the fence value when the cmd buffer was submitted - m_TransientCmdPoolMgr.DisposeCommandPool(std::move(CmdPool), m_pCommandQueue->GetNextFenceValue()); -} - - - template<typename ObjectType> void RenderDeviceVkImpl::SafeReleaseVkObject(ObjectType&& vkObject) { @@ -426,7 +438,7 @@ void RenderDeviceVkImpl::SafeReleaseVkObject(ObjectType&& vkObject) // stale objects list. The list is moved into a release queue // after the next command list is executed. std::lock_guard<std::mutex> LockGuard(m_StaleObjectsMutex); - m_StaleVkObjects.emplace_back(m_NextCmdListNumber, new StaleVulkanObject{std::move(vkObject)} ); + m_StaleVkObjects.emplace_back(m_NextCmdBuffNumber, new StaleVulkanObject{std::move(vkObject)} ); } #define INSTANTIATE_SAFE_RELEASE_VK_OBJECT(Type) template void RenderDeviceVkImpl::SafeReleaseVkObject<Type>(Type &&Object) @@ -451,16 +463,16 @@ INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::VulkanUploadAllocation); #undef INSTANTIATE_SAFE_RELEASE_VK_OBJECT -void RenderDeviceVkImpl::DiscardStaleVkObjects(Uint64 CmdListNumber, Uint64 FenceValue) +void RenderDeviceVkImpl::DiscardStaleVkObjects(Uint64 CmdBuffNumber, Uint64 FenceValue) { - // Only discard these stale objects that were released before CmdListNumber + // Only discard these stale objects that were released before CmdBuffNumber // was executed std::lock_guard<std::mutex> StaleObjectsLock(m_StaleObjectsMutex); std::lock_guard<std::mutex> ReleaseQueueLock(m_ReleaseQueueMutex); while (!m_StaleVkObjects.empty() ) { auto &FirstStaleObj = m_StaleVkObjects.front(); - if (FirstStaleObj.first <= CmdListNumber) + if (FirstStaleObj.first <= CmdBuffNumber) { m_VkObjReleaseQueue.emplace_back(FenceValue, std::move(FirstStaleObj.second)); m_StaleVkObjects.pop_front(); diff --git a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp index c43dc5c9..9272fb0c 100644 --- a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp @@ -367,32 +367,11 @@ TextureVkImpl :: TextureVkImpl(IReferenceCounters *pRefCounters, m_CurrentLayout, // dstImageLayout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL (18.4) static_cast<uint32_t>(Regions.size()), Regions.data()); + pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(vkCmdBuff, std::move(CmdPool)); - // Command list fence should only be signaled when submitting cmd list - // from the immediate context, otherwise the basic requirement will be violated - // as in the scenario below - // - // Signaled Fence | Immediate Context | InitContext | - // | | | - // N | Draw(ResourceX) | | - // | Release(ResourceX) | | - // | - (ResourceX, N) -> Release Queue | | - // | | CopyResource() | - // N+1 | | CloseAndExecuteCommandContext() | - // | | | - // N+2 | CloseAndExecuteCommandContext() | | - // | - Cmd list is submitted with number | | - // | N+1, but resource it references | | - // | was added to the delete queue | | - // | with value N | | - pRenderDeviceVk->ExecuteCommandBuffer(vkCmdBuff, false); - // Dispose command pool. No need to dispose cmd buffer as the - // pool will be reset and all buffer resources will be reclaimed - pRenderDeviceVk->DisposeTransientCmdPool(std::move(CmdPool)); - - // Add reference to the object to the release queue to keep it alive - // until copy operation is complete. This must be done after - // submitting command list for execution! + // After command buffer is submitted, safe-release resources. This strategy + // is little overconservative as the resources will be released after the first + // command buffer submitted through the immediate context will be completed pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBuffer)); pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingMemoryAllocation)); } |
