summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineVulkan
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-06-06 03:56:10 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-06-06 03:56:10 +0000
commit186151aec243c105d8e9c0fcff6a9c342b7d168b (patch)
tree716cb5be17d6e1c2e33a413a04932743e79871f0 /Graphics/GraphicsEngineVulkan
parentReworked upload heap in Vulkan (diff)
downloadDiligentCore-186151aec243c105d8e9c0fcff6a9c342b7d168b.tar.gz
DiligentCore-186151aec243c105d8e9c0fcff6a9c342b7d168b.zip
Improvements to resource liftime management in Vulkan
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
-rw-r--r--Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h29
-rw-r--r--Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp53
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp32
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp222
-rw-r--r--Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp29
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));
}