summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineVulkan
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-07-31 03:50:15 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-07-31 03:50:15 +0000
commitc3c38e981fe078d2a83d23283c54f0a0f113ce93 (patch)
treeb99e049dbb8fc66a6f4e20e395504a4c582002b7 /Graphics/GraphicsEngineVulkan
parentFixed initialization of DebugMessageCallback to work in Release mode (diff)
downloadDiligentCore-c3c38e981fe078d2a83d23283c54f0a0f113ce93.tar.gz
DiligentCore-c3c38e981fe078d2a83d23283c54f0a0f113ce93.zip
Reworked dynamic resource management in Vulkan to not rely on RingBuffer; added FinishFrame() to device context
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
-rw-r--r--Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h7
-rw-r--r--Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h4
-rw-r--r--Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h8
-rw-r--r--Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h256
-rw-r--r--Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h4
-rw-r--r--Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp22
-rw-r--r--Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp3
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp59
-rw-r--r--Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp2
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp37
-rw-r--r--Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp8
-rw-r--r--Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp1
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp127
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp4
16 files changed, 338 insertions, 208 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
index e94ed304..90148d2b 100644
--- a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
@@ -41,6 +41,7 @@ namespace Diligent
{
class FixedBlockMemoryAllocator;
+class DeviceContextVkImpl;
/// Implementation of the Diligent::IBufferVk interface
class BufferVkImpl : public BufferBase<IBufferVk, RenderDeviceVkImpl, BufferViewVkImpl, FixedBlockMemoryAllocator>
@@ -69,10 +70,10 @@ public:
virtual void Unmap( IDeviceContext* pContext, MAP_TYPE MapType, Uint32 MapFlags )override;
#ifdef DEVELOPMENT
- void DvpVerifyDynamicAllocation(Uint32 ContextId)const;
+ void DvpVerifyDynamicAllocation(DeviceContextVkImpl* pCtx)const;
#endif
- Uint32 GetDynamicOffset(Uint32 CtxId)const
+ Uint32 GetDynamicOffset(Uint32 CtxId, DeviceContextVkImpl* pCtx)const
{
if(m_VulkanBuffer != VK_NULL_HANDLE)
{
@@ -83,7 +84,7 @@ public:
VERIFY(m_Desc.Usage == USAGE_DYNAMIC, "Dynamic buffer is expected");
VERIFY_EXPR(!m_DynamicAllocations.empty());
#ifdef DEVELOPMENT
- DvpVerifyDynamicAllocation(CtxId);
+ DvpVerifyDynamicAllocation(pCtx);
#endif
auto& DynAlloc = m_DynamicAllocations[CtxId];
return static_cast<Uint32>(DynAlloc.Offset);
diff --git a/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h b/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h
index 5a3de325..8e2bb941 100644
--- a/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h
@@ -62,7 +62,7 @@ public:
virtual uint32_t GetQueueFamilyIndex()override final { return m_QueueFamilyIndex; }
- virtual void IdleGPU()override final;
+ virtual Uint64 IdleGPU()override final;
virtual Uint64 GetCompletedFenceValue()override final;
diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
index 312bdaca..e7483c22 100644
--- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
@@ -147,6 +147,7 @@ public:
return m_CommandBuffer;
}
+ virtual void FinishFrame(bool ForceRelease)override final;
void FinishFrame(Uint64 CompletedFenceValue);
DescriptorPoolAllocation AllocateDynamicDescriptorSet(VkDescriptorSetLayout SetLayout)
@@ -159,6 +160,7 @@ public:
VulkanDynamicAllocation AllocateDynamicSpace(Uint32 SizeInBytes);
void ResetRenderTargets();
+ Int64 GetContextFrameNumber()const{return m_ContextFrameNumber;}
private:
void CommitRenderPassAndFramebuffer();
@@ -220,6 +222,8 @@ private:
// Number of the command buffer currently being recorded by the context and that will
// be submitted next
Atomics::AtomicInt64 m_NextCmdBuffNumber;
+ Atomics::AtomicInt64 m_ContextFrameNumber;
+ Uint64 m_LastSubmittedFenceValue = 0;
PipelineLayout::DescriptorSetBindInfo m_DescrSetBindInfo;
VulkanDynamicHeap m_DynamicHeap;
diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
index 11257bf0..9fbe7c32 100644
--- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
@@ -133,8 +133,8 @@ public:
return m_MemoryMgr.Allocate(MemReqs, MemoryProperties);
}
- VulkanRingBuffer& GetDynamicHeapRingBuffer(){return m_DynamicHeapRingBuffer;}
-
+ VulkanDynamicMemoryManager& GetDynamicMemoryManager(){return m_DynamicMemoryManager;}
+
private:
virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat )override final;
void ProcessStaleResources(Uint64 SubmittedCmdBufferNumber, Uint64 SubmittedFenceValue, Uint64 CompletedFenceValue);
@@ -150,7 +150,7 @@ private:
std::unique_ptr<VulkanUtilities::VulkanPhysicalDevice> m_PhysicalDevice;
std::shared_ptr<VulkanUtilities::VulkanLogicalDevice> m_LogicalVkDevice;
- std::mutex m_CmdQueueMutex;
+ std::mutex m_SubmitCmdBufferMutex;
RefCntAutoPtr<ICommandQueueVk> m_pCommandQueue;
EngineVkAttribs m_EngineAttribs;
@@ -198,7 +198,7 @@ private:
VulkanUtilities::VulkanMemoryManager m_MemoryMgr;
ResourceReleaseQueue<DynamicStaleResourceWrapper> m_ReleaseQueue;
- VulkanRingBuffer m_DynamicHeapRingBuffer;
+ VulkanDynamicMemoryManager m_DynamicMemoryManager;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
index b4a0e3b6..1531d851 100644
--- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
+++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
@@ -174,7 +174,7 @@ public:
template<bool VerifyOnly>
void TransitionResources(DeviceContextVkImpl *pCtxVkImpl);
- Uint32 GetDynamicBufferOffsets(Uint32 CtxId, std::vector<uint32_t>& Offsets)const;
+ Uint32 GetDynamicBufferOffsets(DeviceContextVkImpl *pCtxVkImpl, std::vector<uint32_t>& Offsets)const;
private:
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
index a36cb3ba..3e3d0c7f 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
@@ -26,119 +26,246 @@
#include <mutex>
#include "Vulkan.h"
#include "RingBuffer.h"
+#include "VulkanUtilities/VulkanMemoryManager.h"
#include "VulkanUtilities/VulkanLogicalDevice.h"
#include "VulkanUtilities/VulkanObjectWrappers.h"
namespace Diligent
{
-// Vulkand dynamic heap implementation consists of a single ring buffer and a number of dynamic heaps,
-// one per context. Every dynamic heap suballocates chunk of memory from the global ring buffer. Within
-// every chunk, memory is allocated in simple lock-free linear fashion:
-//
-// | <----------------------frame 0---------------------------->|<-----------------frame 1-------------->
-// | Ctx0-f0-Chunk0 | Ctx1-f0-Chunk0 | Ctx0-f0-Chunk1 | ... | Ctx0-f1-Chunk0 | Ctx1-f1-Chunk0 |....
-//
+// Vulkand dynamic heap implementation consists of a number of dynamic heaps, one per context.
+// Every dynamic heap suballocates chunk of memory from the global memory manager. Within
+// every chunk, memory is allocated in a simple lock-free linear fashion. All used allocations are discarded
+// when FinishFrame() is called
class RenderDeviceVkImpl;
class VulkanRingBuffer;
+class VulkanDynamicMemoryManager;
// sizeof(VulkanDynamicAllocation) must be at least 16 to avoid false cache line sharing problems
struct VulkanDynamicAllocation
{
- VulkanDynamicAllocation(){}
+ VulkanDynamicAllocation()noexcept{}
- VulkanDynamicAllocation(VulkanRingBuffer& _ParentHeap, size_t _Offset, size_t _Size) :
- pParentDynamicHeap(&_ParentHeap),
- Offset (_Offset),
- Size (_Size)
+ VulkanDynamicAllocation(VulkanDynamicMemoryManager& _DynamicMemMgr, size_t _Offset, size_t _Size)noexcept :
+ pDynamicMemMgr(&_DynamicMemMgr),
+ Offset (_Offset),
+ Size (_Size)
{}
VulkanDynamicAllocation (const VulkanDynamicAllocation&) = delete;
VulkanDynamicAllocation& operator = (const VulkanDynamicAllocation&) = delete;
VulkanDynamicAllocation (VulkanDynamicAllocation&& rhs)noexcept :
- pParentDynamicHeap(rhs.pParentDynamicHeap),
- Offset (rhs.Offset),
- Size (rhs.Size)
-#ifdef _DEBUG
- , dbgFrameNumber(rhs.dbgFrameNumber)
+ pDynamicMemMgr(rhs.pDynamicMemMgr),
+ Offset (rhs.Offset),
+ Size (rhs.Size)
+#ifdef DEVELOPMENT
+ , dvpFrameNumber(rhs.dvpFrameNumber)
#endif
{
- rhs.pParentDynamicHeap = nullptr;
+ rhs.pDynamicMemMgr = nullptr;
rhs.Offset = 0;
rhs.Size = 0;
-#ifdef _DEBUG
- rhs.dbgFrameNumber = 0;
+#ifdef DEVELOPMENT
+ rhs.dvpFrameNumber = 0;
#endif
}
VulkanDynamicAllocation& operator = (VulkanDynamicAllocation&& rhs)noexcept // Must be noexcept on MSVC, so can't use = default
{
- pParentDynamicHeap = rhs.pParentDynamicHeap;
- Offset = rhs.Offset;
- Size = rhs.Size;
- rhs.pParentDynamicHeap = nullptr;
- rhs.Offset = 0;
- rhs.Size = 0;
-#ifdef _DEBUG
- dbgFrameNumber = rhs.dbgFrameNumber;
- rhs.dbgFrameNumber = 0;
+ pDynamicMemMgr = rhs.pDynamicMemMgr;
+ Offset = rhs.Offset;
+ Size = rhs.Size;
+ rhs.pDynamicMemMgr = nullptr;
+ rhs.Offset = 0;
+ rhs.Size = 0;
+#ifdef DEVELOPMENT
+ dvpFrameNumber = rhs.dvpFrameNumber;
+ rhs.dvpFrameNumber = 0;
#endif
return *this;
}
- VulkanRingBuffer* pParentDynamicHeap = nullptr;
- size_t Offset = 0; // Offset from the start of the buffer resource
- size_t Size = 0; // Reserved size of this allocation
-#ifdef _DEBUG
- Uint64 dbgFrameNumber = 0;
+ VulkanDynamicMemoryManager* pDynamicMemMgr = nullptr;
+ size_t Offset = 0; // Offset from the start of the buffer resource
+ size_t Size = 0; // Reserved size of this allocation
+#ifdef DEVELOPMENT
+ Int64 dvpFrameNumber = 0;
#endif
};
-class VulkanRingBuffer
+
+// Having global ring buffer shared between all contexts is inconvinient because all contexts
+// must share the same frame. Having individual ring bufer per context may result in a lot of unused
+// memory. As a result, ring buffer is not currently used for dynamic memory management.
+// Instead, every dynamic heap allocates pages from the global dynamic memory manager.
+class RingBufferAllocationStrategy
{
public:
- VulkanRingBuffer(IMemoryAllocator& Allocator,
- class RenderDeviceVkImpl& DeviceVk,
- Uint32 Size);
- ~VulkanRingBuffer();
+ using OffsetType = RingBuffer::OffsetType;
+ static constexpr const OffsetType InvalidOffset = RingBuffer::InvalidOffset;
+
+ RingBufferAllocationStrategy(IMemoryAllocator& Allocator,
+ Uint32 Size) :
+ m_RingBuffer(Size, Allocator)
+ {}
- VulkanRingBuffer (const VulkanRingBuffer&) = delete;
- VulkanRingBuffer (VulkanRingBuffer&&) = delete;
- VulkanRingBuffer& operator= (const VulkanRingBuffer&) = delete;
- VulkanRingBuffer& operator= (VulkanRingBuffer&&) = delete;
+ RingBufferAllocationStrategy (const RingBufferAllocationStrategy&) = delete;
+ RingBufferAllocationStrategy (RingBufferAllocationStrategy&&) = delete;
+ RingBufferAllocationStrategy& operator= (const RingBufferAllocationStrategy&) = delete;
+ RingBufferAllocationStrategy& operator= (RingBufferAllocationStrategy&&) = delete;
- void FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue);
- void Destroy();
+ void DiscardAllocations(const std::vector<std::pair<OffsetType, OffsetType>>& Allocations, Uint64 FenceValue)
+ {
+ std::lock_guard<std::mutex> Lock(m_RingBufferMtx);
+ m_RingBuffer.FinishCurrentFrame(FenceValue);
+ }
+
+ void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue)
+ {
+ std::lock_guard<std::mutex> Lock(m_RingBufferMtx);
+ m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
+ }
+
+ OffsetType Allocate(OffsetType SizeInBytes)
+ {
+ std::lock_guard<std::mutex> Lock(m_RingBufferMtx);
+ return m_RingBuffer.Allocate(SizeInBytes);
+ }
+
+ OffsetType GetSize() const{return m_RingBuffer.GetMaxSize();}
+ OffsetType GetUsedSize()const{return m_RingBuffer.GetUsedSize();}
+private:
+ std::mutex m_RingBufferMtx;
+ RingBuffer m_RingBuffer;
+};
+
+
+class ListBasedAllocationStrategy
+{
+public:
+ using OffsetType = VariableSizeAllocationsManager::OffsetType;
+ static constexpr const OffsetType InvalidOffset = VariableSizeAllocationsManager::InvalidOffset;
+
+ ListBasedAllocationStrategy(IMemoryAllocator& Allocator,
+ Uint32 Size) :
+ m_AllocationsMgr(Size, Allocator)
+ {}
+
+ ListBasedAllocationStrategy (const ListBasedAllocationStrategy&) = delete;
+ ListBasedAllocationStrategy (ListBasedAllocationStrategy&&) = delete;
+ ListBasedAllocationStrategy& operator= (const ListBasedAllocationStrategy&) = delete;
+ ListBasedAllocationStrategy& operator= (ListBasedAllocationStrategy&&) = delete;
+
+ void DiscardAllocations(const std::vector<std::pair<OffsetType, OffsetType>>& Allocations, Uint64 FenceValue)
+ {
+ std::lock_guard<std::mutex> Lock(m_ReleaseQueueMtx);
+ for(const auto& Allocation : Allocations)
+ m_ReleaseQueue.emplace_back(Allocation.first, Allocation.second, FenceValue);
+ }
+
+ void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue)
+ {
+ std::lock_guard<std::mutex> MgrLock(m_AllocationsMgrMtx);
+ std::lock_guard<std::mutex> QueueLock(m_ReleaseQueueMtx);
+ while (!m_ReleaseQueue.empty())
+ {
+ auto &FirstAllocation = m_ReleaseQueue.front();
+ if (FirstAllocation.FenceValue <= LastCompletedFenceValue)
+ {
+ m_AllocationsMgr.Free(FirstAllocation.Offset, FirstAllocation.Size);
+ m_ReleaseQueue.pop_front();
+ }
+ else
+ break;
+ }
+ }
+
+ OffsetType Allocate(OffsetType SizeInBytes)
+ {
+ std::lock_guard<std::mutex> Lock(m_AllocationsMgrMtx);
+ return m_AllocationsMgr.Allocate(SizeInBytes);
+ }
+ OffsetType GetSize() const{return m_AllocationsMgr.GetMaxSize();}
+ OffsetType GetUsedSize()const{return m_AllocationsMgr.GetUsedSize();}
+
+private:
+ std::mutex m_AllocationsMgrMtx;
+ VariableSizeAllocationsManager m_AllocationsMgr;
+
+ struct StaleAllocationInfo
+ {
+ const OffsetType Offset;
+ const OffsetType Size;
+ const Uint64 FenceValue;
+ StaleAllocationInfo(OffsetType _Offset,
+ OffsetType _Size,
+ Uint64 _FenceValue) :
+ Offset (_Offset),
+ Size (_Size),
+ FenceValue(_FenceValue)
+ {}
+ };
+ std::deque< StaleAllocationInfo > m_ReleaseQueue;
+ std::mutex m_ReleaseQueueMtx;
+};
+
+// We cannot use global memory manager for dynamic resources because they
+// need to use the same Vulkan buffer
+class VulkanDynamicMemoryManager
+{
+public:
+ using AllocationStategy = ListBasedAllocationStrategy; // or RingBufferAllocationStrategy
+ using OffsetType = AllocationStategy::OffsetType;
+
+ VulkanDynamicMemoryManager(IMemoryAllocator& Allocator,
+ class RenderDeviceVkImpl& DeviceVk,
+ Uint32 Size);
+ ~VulkanDynamicMemoryManager();
+
+ VulkanDynamicMemoryManager (const VulkanDynamicMemoryManager&) = delete;
+ VulkanDynamicMemoryManager (VulkanDynamicMemoryManager&&) = delete;
+ VulkanDynamicMemoryManager& operator= (const VulkanDynamicMemoryManager&) = delete;
+ VulkanDynamicMemoryManager& operator= (VulkanDynamicMemoryManager&&) = delete;
+
+ void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue)
+ {
+ m_AllocationStrategy.ReleaseStaleAllocations(LastCompletedFenceValue);
+ }
+
+ void DiscardAllocations(const std::vector<std::pair<OffsetType, OffsetType>>& Allocations, Uint64 FenceValue)
+ {
+ m_AllocationStrategy.DiscardAllocations(Allocations, FenceValue);
+ }
VkBuffer GetVkBuffer() const{return m_VkBuffer;}
Uint8* GetCPUAddress()const{return m_CPUAddress;}
-private:
- friend class VulkanDynamicHeap;
+ void Destroy();
static constexpr const Uint32 MinAlignment = 1024;
- RingBuffer::OffsetType Allocate(size_t SizeInBytes);
- std::mutex m_RingBuffMtx;
- RingBuffer m_RingBuffer;
- RenderDeviceVkImpl& m_DeviceVk;
+private:
+ friend class VulkanDynamicHeap;
+ OffsetType Allocate(OffsetType SizeInBytes);
+ RenderDeviceVkImpl& m_DeviceVk;
VulkanUtilities::BufferWrapper m_VkBuffer;
VulkanUtilities::DeviceMemoryWrapper m_BufferMemory;
Uint8* m_CPUAddress;
const VkDeviceSize m_DefaultAlignment;
- RingBuffer::OffsetType m_TotalPeakSize = 0;
- RingBuffer::OffsetType m_CurrentFrameSize = 0;
- RingBuffer::OffsetType m_FramePeakSize = 0;
+
+ AllocationStategy m_AllocationStrategy;
+
+ OffsetType m_TotalPeakSize = 0;
};
class VulkanDynamicHeap
{
public:
- VulkanDynamicHeap(VulkanRingBuffer& ParentRingBuffer, std::string HeapName, Uint32 PageSize) :
- m_ParentRingBuffer(ParentRingBuffer),
+ VulkanDynamicHeap(VulkanDynamicMemoryManager& DynamicMemMgr, std::string HeapName, Uint32 PageSize) :
+ m_DynamicMemMgr(DynamicMemMgr),
m_HeapName(std::move(HeapName)),
m_PagSize(PageSize)
{}
@@ -151,21 +278,18 @@ public:
~VulkanDynamicHeap();
VulkanDynamicAllocation Allocate(Uint32 SizeInBytes, Uint32 Alignment);
+ void FinishFrame(Uint64 FenceValue);
- void Reset()
- {
- m_CurrOffset = RingBuffer::InvalidOffset;
- m_AvailableSize = 0;
-
- m_CurrAllocatedSize = 0;
- m_CurrUsedSize = 0;
- }
+ using OffsetType = VulkanDynamicMemoryManager::AllocationStategy::OffsetType;
+ static constexpr OffsetType InvalidOffset = VulkanDynamicMemoryManager::AllocationStategy::InvalidOffset;
private:
- VulkanRingBuffer& m_ParentRingBuffer;
+ VulkanDynamicMemoryManager& m_DynamicMemMgr;
const std::string m_HeapName;
- RingBuffer::OffsetType m_CurrOffset = RingBuffer::InvalidOffset;
+ std::vector<std::pair<OffsetType, OffsetType>> m_Allocations;
+
+ OffsetType m_CurrOffset = InvalidOffset;
const Uint32 m_PagSize;
Uint32 m_AvailableSize = 0;
diff --git a/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h b/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h
index a7c58096..3a5fb616 100644
--- a/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h
+++ b/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h
@@ -63,7 +63,9 @@ public:
virtual Uint64 GetCompletedFenceValue() = 0;
/// Blocks execution until all pending GPU commands are complete
- virtual void IdleGPU() = 0;
+
+ /// \return Last completed fence value
+ virtual UINT64 IdleGPU() = 0;
/// Signals the given fence
virtual void SignalFence(VkFence vkFence) = 0;
diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
index 93ac343b..104dc021 100644
--- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
@@ -332,7 +332,7 @@ void BufferVkImpl :: Map(IDeviceContext* pContext, MAP_TYPE MapType, Uint32 MapF
#endif
auto& DynAllocation = m_DynamicAllocations[pDeviceContextVk->GetContextId()];
- if ( (MapFlags & MAP_FLAG_DISCARD) != 0 || DynAllocation.pParentDynamicHeap == nullptr )
+ if ( (MapFlags & MAP_FLAG_DISCARD) != 0 || DynAllocation.pDynamicMemMgr == nullptr )
{
DynAllocation = pDeviceContextVk->AllocateDynamicSpace(m_Desc.uiSizeInBytes);
}
@@ -342,10 +342,9 @@ void BufferVkImpl :: Map(IDeviceContext* pContext, MAP_TYPE MapType, Uint32 MapF
// Reuse the same allocation
}
- if (DynAllocation.pParentDynamicHeap != nullptr)
+ if (DynAllocation.pDynamicMemMgr != nullptr)
{
- const auto& DynamicHeap = m_pDevice->GetDynamicHeapRingBuffer();
- auto* CPUAddress = DynamicHeap.GetCPUAddress();
+ auto* CPUAddress = DynAllocation.pDynamicMemMgr->GetCPUAddress();
pMappedData = CPUAddress + DynAllocation.Offset;
}
else
@@ -419,7 +418,7 @@ void BufferVkImpl::Unmap( IDeviceContext* pContext, MAP_TYPE MapType, Uint32 Map
if(m_VulkanBuffer != VK_NULL_HANDLE)
{
auto &DynAlloc = m_DynamicAllocations[CtxId];
- auto vkSrcBuff = DynAlloc.pParentDynamicHeap->GetVkBuffer();
+ auto vkSrcBuff = DynAlloc.pDynamicMemMgr->GetVkBuffer();
pDeviceContextVk->UpdateBufferRegion(this, 0, m_Desc.uiSizeInBytes, vkSrcBuff, DynAlloc.Offset);
}
}
@@ -492,19 +491,18 @@ VkBuffer BufferVkImpl::GetVkBuffer()const
else
{
VERIFY(m_Desc.Usage == USAGE_DYNAMIC, "Dynamic buffer expected");
- return m_pDevice->GetDynamicHeapRingBuffer().GetVkBuffer();
+ return m_pDevice->GetDynamicMemoryManager().GetVkBuffer();
}
}
#ifdef DEVELOPMENT
-void BufferVkImpl::DvpVerifyDynamicAllocation(Uint32 ContextId)const
+void BufferVkImpl::DvpVerifyDynamicAllocation(DeviceContextVkImpl* pCtx)const
{
+ auto ContextId = pCtx->GetContextId();
const auto& DynAlloc = m_DynamicAllocations[ContextId];
- if (DynAlloc.pParentDynamicHeap == nullptr)
- LOG_ERROR_MESSAGE("Dynamic buffer '", m_Desc.Name, "' was not mapped before its first use. Context Id: ", ContextId);
- auto CurrentFrame = m_pDevice->GetCurrentFrameNumber();
- if (DynAlloc.dbgFrameNumber != CurrentFrame)
- LOG_ERROR_MESSAGE("Dynamic allocation is out-of-date. Dynamic buffer '", m_Desc.Name, "' must be mapped in the same frame it is used.");
+ DEV_CHECK_ERR(DynAlloc.pDynamicMemMgr != nullptr, "Dynamic buffer '", m_Desc.Name, "' was not mapped before its first use. Context Id: ", ContextId);
+ auto CurrentFrame = pCtx->GetContextFrameNumber();
+ DEV_CHECK_ERR(DynAlloc.dvpFrameNumber == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer '", m_Desc.Name, "' must be mapped in the same frame it is used.");
}
#endif
diff --git a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
index e75d4aaf..6e789ee5 100644
--- a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
@@ -90,7 +90,7 @@ Uint64 CommandQueueVkImpl::ExecuteCommandBuffer(VkCommandBuffer cmdBuffer)
return ExecuteCommandBuffer(SubmitInfo);
}
-void CommandQueueVkImpl::IdleGPU()
+Uint64 CommandQueueVkImpl::IdleGPU()
{
std::lock_guard<std::mutex> Lock(m_QueueMutex);
@@ -102,6 +102,7 @@ void CommandQueueVkImpl::IdleGPU()
// For some reason after idling the queue not all fences are signaled
m_pFence->Wait();
m_pFence->Reset(LastCompletedFenceValue);
+ return LastCompletedFenceValue;
}
Uint64 CommandQueueVkImpl::GetCompletedFenceValue()
diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
index a1e858e5..c8b95867 100644
--- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
@@ -107,9 +107,10 @@ namespace Diligent
Attribs.DynamicDescriptorPoolSize.MaxDescriptorSets,
},
m_NextCmdBuffNumber(0),
+ m_ContextFrameNumber(0),
m_DynamicHeap
{
- pDeviceVkImpl->GetDynamicHeapRingBuffer(),
+ pDeviceVkImpl->GetDynamicMemoryManager(),
GetDynamicHeapName(bIsDeferred, ContextId),
bIsDeferred ? Attribs.DeferredCtxDynamicHeapPageSize : Attribs.ImmediateCtxDynamicHeapPageSize
},
@@ -138,14 +139,14 @@ namespace Diligent
// We need to idle when destroying deferred contexts as well since some resources may still be in use.
pDeviceVkImpl->IdleGPU(true);
- DisposeCurrentCmdBuffer(pDeviceVkImpl->GetNextFenceValue());
+ DisposeCurrentCmdBuffer(m_LastSubmittedFenceValue);
// 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_ReleaseQueue.GetStaleResourceCount() == 0, "All stale resources 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");
- ReleaseStaleContextResources(m_NextCmdBuffNumber, pDeviceVkImpl->GetNextFenceValue(), pDeviceVkImpl->GetCompletedFenceValue());
+ ReleaseStaleContextResources(m_NextCmdBuffNumber, m_LastSubmittedFenceValue, pDeviceVkImpl->GetCompletedFenceValue());
// Since we idled the GPU, all stale resources must have been destroyed now
VERIFY(m_ReleaseQueue.GetPendingReleaseResourceCount() == 0, "All stale resources must have been destroyed at this point");
VERIFY(m_DynamicDescriptorPool.GetPendingReleaseAllocationCount() == 0, "All stale descriptor set allocations must have been destroyed at this point");
@@ -316,7 +317,7 @@ namespace Diligent
{
DynamicBufferPresent = true;
#ifdef DEVELOPMENT
- pBufferVk->DvpVerifyDynamicAllocation(m_ContextId);
+ pBufferVk->DvpVerifyDynamicAllocation(this);
#endif
}
if (!pBufferVk->CheckAccessFlags(VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT))
@@ -325,7 +326,7 @@ namespace Diligent
// Device context keeps strong references to all vertex buffers.
vkVertexBuffers[slot] = pBufferVk->GetVkBuffer();
- Offsets[slot] = CurrStream.Offset + pBufferVk->GetDynamicOffset(m_ContextId);
+ Offsets[slot] = CurrStream.Offset + pBufferVk->GetDynamicOffset(m_ContextId, this);
}
//GraphCtx.FlushResourceBarriers();
@@ -410,7 +411,7 @@ namespace Diligent
DEV_CHECK_ERR(DrawAttribs.IndexType == VT_UINT16 || DrawAttribs.IndexType == VT_UINT32, "Unsupported index format. Only R16_UINT and R32_UINT are allowed.");
VkIndexType vkIndexType = DrawAttribs.IndexType == VT_UINT16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;
- m_CommandBuffer.BindIndexBuffer(pBuffVk->GetVkBuffer(), m_IndexDataStartOffset + pBuffVk->GetDynamicOffset(m_ContextId), vkIndexType);
+ m_CommandBuffer.BindIndexBuffer(pBuffVk->GetVkBuffer(), m_IndexDataStartOffset + pBuffVk->GetDynamicOffset(m_ContextId, this), vkIndexType);
}
if (m_State.CommittedVBsUpToDate)
@@ -461,15 +462,15 @@ namespace Diligent
{
#ifdef DEVELOPMENT
if (pBufferVk->GetDesc().Usage == USAGE_DYNAMIC)
- pBufferVk->DvpVerifyDynamicAllocation(m_ContextId);
+ pBufferVk->DvpVerifyDynamicAllocation(this);
#endif
if (!pBufferVk->CheckAccessFlags(VK_ACCESS_INDIRECT_COMMAND_READ_BIT))
BufferMemoryBarrier(*pBufferVk, VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
if ( DrawAttribs.IsIndexed )
- m_CommandBuffer.DrawIndexedIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
+ m_CommandBuffer.DrawIndexedIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId, this) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
else
- m_CommandBuffer.DrawIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
+ m_CommandBuffer.DrawIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId, this) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
}
}
else
@@ -522,14 +523,14 @@ namespace Diligent
{
#ifdef DEVELOPMENT
if (pBufferVk->GetDesc().Usage == USAGE_DYNAMIC)
- pBufferVk->DvpVerifyDynamicAllocation(m_ContextId);
+ pBufferVk->DvpVerifyDynamicAllocation(this);
#endif
// Buffer memory barries must be executed outside of render pass
if (!pBufferVk->CheckAccessFlags(VK_ACCESS_INDIRECT_COMMAND_READ_BIT))
BufferMemoryBarrier(*pBufferVk, VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
- m_CommandBuffer.DispatchIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId) + DispatchAttrs.DispatchArgsByteOffset);
+ m_CommandBuffer.DispatchIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId, this) + DispatchAttrs.DispatchArgsByteOffset);
}
else
{
@@ -754,12 +755,23 @@ namespace Diligent
++m_State.NumCommands;
}
+ void DeviceContextVkImpl::FinishFrame(bool ForceRelease)
+ {
+ FinishFrame(ForceRelease ? std::numeric_limits<Uint64>::max() : m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetCompletedFenceValue());
+ }
+
void DeviceContextVkImpl::FinishFrame(Uint64 CompletedFenceValue)
{
+ if(GetNumCommandsInCtx() != 0)
+ LOG_ERROR_MESSAGE(m_bIsDeferred ?
+ "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_ReleaseQueue.Purge(CompletedFenceValue);
m_UploadHeap.ShrinkMemory();
m_DynamicDescriptorPool.ReleaseStaleAllocations(CompletedFenceValue);
- m_DynamicHeap.Reset();
+ m_DynamicHeap.FinishFrame(m_LastSubmittedFenceValue);
+ Atomics::AtomicIncrement(m_ContextFrameNumber);
}
void DeviceContextVkImpl::ReleaseStaleContextResources(Uint64 SubmittedCmdBufferNumber, Uint64 SubmittedFenceValue, Uint64 CompletedFenceValue)
@@ -814,7 +826,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)
- auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, &m_PendingFences);
+ m_LastSubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, &m_PendingFences);
m_WaitSemaphores.clear();
m_WaitDstStageMasks.clear();
@@ -823,14 +835,14 @@ namespace Diligent
if (vkCmdBuff != VK_NULL_HANDLE)
{
- DisposeCurrentCmdBuffer(SubmittedFenceValue);
+ DisposeCurrentCmdBuffer(m_LastSubmittedFenceValue);
}
// Release temporary resources that were used by this context while recording the last command buffer
auto SubmittedCmdBuffNumber = m_NextCmdBuffNumber;
Atomics::AtomicIncrement(m_NextCmdBuffNumber);
auto CompletedFenceValue = pDeviceVkImpl->GetCompletedFenceValue();
- ReleaseStaleContextResources(SubmittedCmdBuffNumber, SubmittedFenceValue, CompletedFenceValue);
+ ReleaseStaleContextResources(SubmittedCmdBuffNumber, m_LastSubmittedFenceValue, CompletedFenceValue);
m_State = ContextState{};
m_DescrSetBindInfo.Reset();
@@ -1112,11 +1124,11 @@ namespace Diligent
if (!pDstBuffVk->CheckAccessFlags(VK_ACCESS_TRANSFER_WRITE_BIT))
BufferMemoryBarrier(*pDstBuffVk, VK_ACCESS_TRANSFER_WRITE_BIT);
VkBufferCopy CopyRegion;
- CopyRegion.srcOffset = SrcOffset + pSrcBuffVk->GetDynamicOffset(m_ContextId);
+ CopyRegion.srcOffset = SrcOffset + pSrcBuffVk->GetDynamicOffset(m_ContextId, this);
CopyRegion.dstOffset = DstOffset;
CopyRegion.size = NumBytes;
VERIFY(pDstBuffVk->m_VulkanBuffer != VK_NULL_HANDLE, "Copy destination buffer must not be suballocated");
- VERIFY_EXPR(pDstBuffVk->GetDynamicOffset(m_ContextId) == 0);
+ VERIFY_EXPR(pDstBuffVk->GetDynamicOffset(m_ContextId, this) == 0);
m_CommandBuffer.CopyBuffer(pSrcBuffVk->GetVkBuffer(), pDstBuffVk->GetVkBuffer(), 1, &CopyRegion);
++m_State.NumCommands;
}
@@ -1275,9 +1287,10 @@ namespace Diligent
SubmitInfo.pCommandBuffers = &vkCmdBuff;
auto pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>();
VERIFY_EXPR(m_PendingFences.empty());
- auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, nullptr);
-
auto pDeferredCtxVkImpl = pDeferredCtx.RawPtr<DeviceContextVkImpl>();
+ auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, nullptr);
+ pDeferredCtxVkImpl->m_LastSubmittedFenceValue = SubmittedFenceValue;
+
// 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);
@@ -1356,7 +1369,7 @@ namespace Diligent
EnsureVkCmdBuffer();
VERIFY(BufferVk.m_VulkanBuffer != VK_NULL_HANDLE, "Cannot transition suballocated buffer");
- VERIFY_EXPR(BufferVk.GetDynamicOffset(m_ContextId) == 0);
+ VERIFY_EXPR(BufferVk.GetDynamicOffset(m_ContextId, this) == 0);
auto vkBuff = BufferVk.GetVkBuffer();
m_CommandBuffer.BufferMemoryBarrier(vkBuff, BufferVk.m_AccessFlags, NewAccessFlags);
BufferVk.SetAccessFlags(NewAccessFlags);
@@ -1392,11 +1405,9 @@ namespace Diligent
VulkanDynamicAllocation DeviceContextVkImpl::AllocateDynamicSpace(Uint32 SizeInBytes)
{
- auto *pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>();
-
auto DynAlloc = m_DynamicHeap.Allocate(SizeInBytes, 0);
-#ifdef _DEBUG
- DynAlloc.dbgFrameNumber = pDeviceVkImpl->GetCurrentFrameNumber();
+#ifdef DEVELOPMENT
+ DynAlloc.dvpFrameNumber = m_ContextFrameNumber;
#endif
return DynAlloc;
}
diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
index 6ee46d16..e34a9305 100644
--- a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
@@ -506,7 +506,7 @@ void PipelineLayout::BindDescriptorSetsWithDynamicOffsets(DeviceContextVkImpl*
VERIFY_EXPR(BindInfo.DynamicOffsets.size() >= BindInfo.DynamicOffsetCount);
#endif
- auto NumOffsetsWritten = BindInfo.pResourceCache->GetDynamicBufferOffsets(pCtxVkImpl->GetContextId(), BindInfo.DynamicOffsets);
+ auto NumOffsetsWritten = BindInfo.pResourceCache->GetDynamicBufferOffsets(pCtxVkImpl, BindInfo.DynamicOffsets);
VERIFY_EXPR(NumOffsetsWritten == BindInfo.DynamicOffsetCount);
auto& CmdBuffer = pCtxVkImpl->GetCommandBuffer();
diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
index 6095b2e8..476766aa 100644
--- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
@@ -91,7 +91,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters*
m_TransientCmdPoolMgr(*m_LogicalVkDevice, pCmdQueue->GetQueueFamilyIndex(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT),
m_MemoryMgr("Global resource memory manager", *m_LogicalVkDevice, *m_PhysicalDevice, GetRawAllocator(), CreationAttribs.DeviceLocalMemoryPageSize, CreationAttribs.HostVisibleMemoryPageSize, CreationAttribs.DeviceLocalMemoryReserveSize, CreationAttribs.HostVisibleMemoryReserveSize),
m_ReleaseQueue(GetRawAllocator()),
- m_DynamicHeapRingBuffer
+ m_DynamicMemoryManager
{
GetRawAllocator(),
*this,
@@ -110,7 +110,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters*
RenderDeviceVkImpl::~RenderDeviceVkImpl()
{
// Explicitly destroy dynamic heap
- m_DynamicHeapRingBuffer.Destroy();
+ m_DynamicMemoryManager.Destroy();
// Finish current frame. This will release resources taken by previous frames, and
// will move all stale resources to the release queues. The resources will not be
// release until the next call to FinishFrame()
@@ -209,7 +209,8 @@ void RenderDeviceVkImpl::SubmitCommandBuffer(const VkSubmitInfo& SubmitInfo,
std::vector<std::pair<Uint64, RefCntAutoPtr<IFence> > >* pFences // List of fences to signal
)
{
- std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
+ // The lock is required to atomically update m_NextCmdBuffNumber
+ std::lock_guard<std::mutex> LockGuard(m_SubmitCmdBufferMutex);
auto NextFenceValue = m_pCommandQueue->GetNextFenceValue();
// Submit the command list to the queue
SubmittedFenceValue = m_pCommandQueue->ExecuteCommandBuffer(SubmitInfo);
@@ -265,11 +266,9 @@ Uint64 RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects)
Uint64 SubmittedCmdBuffNumber = 0;
{
- // Lock the command queue to avoid other threads interfering with the GPU
- std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
- SubmittedFenceValue = m_pCommandQueue->GetNextFenceValue();
- // CommandQueueVkImpl::IdleGPU increments next fence value
- m_pCommandQueue->IdleGPU();
+ // CommandQueueVkImpl::IdleGPU increments next fence value and returns
+ // the previous value
+ SubmittedFenceValue = m_pCommandQueue->IdleGPU();
m_LogicalVkDevice->WaitIdle();
@@ -311,26 +310,6 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
{
auto CompletedFenceValue = ReleaseAllResources ? std::numeric_limits<Uint64>::max() : GetCompletedFenceValue();
- {
- if (auto pImmediateCtx = m_wpImmediateContext.Lock())
- {
- auto pImmediateCtxVk = pImmediateCtx.RawPtr<DeviceContextVkImpl>();
- if(pImmediateCtxVk->GetNumCommandsInCtx() != 0)
- LOG_ERROR_MESSAGE("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");
- pImmediateCtxVk->FinishFrame(ReleaseAllResources);
- }
-
- for (auto wpDeferredCtx : m_wpDeferredContexts)
- {
- if (auto pDeferredCtx = wpDeferredCtx.Lock())
- {
- auto pDeferredCtxVk = pDeferredCtx.RawPtr<DeviceContextVkImpl>();
- if(pDeferredCtxVk->GetNumCommandsInCtx() != 0)
- LOG_ERROR_MESSAGE("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");
- pDeferredCtxVk->FinishFrame(CompletedFenceValue);
- }
- }
- }
Uint64 SubmittedFenceValue = 0;
Uint64 SubmittedCmdBuffNumber = 0;
@@ -344,7 +323,7 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
// until the GPU is finished with the current frame
ProcessStaleResources(SubmittedCmdBuffNumber, SubmittedFenceValue, CompletedFenceValue);
- m_DynamicHeapRingBuffer.FinishFrame(SubmittedFenceValue, CompletedFenceValue);
+ m_DynamicMemoryManager.ReleaseStaleAllocations(CompletedFenceValue);
Atomics::AtomicIncrement(m_FrameNumber);
}
diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
index c4d57b54..82039178 100644
--- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
@@ -330,8 +330,10 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWrite
return DescrImgInfo;
}
-Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<uint32_t>& Offsets)const
+Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(DeviceContextVkImpl *pCtxVkImpl, std::vector<uint32_t>& Offsets)const
{
+ auto CtxId = pCtxVkImpl->GetContextId();
+
// If any of the sets being bound include dynamic uniform or storage buffers, then
// pDynamicOffsets includes one element for each array element in each dynamic descriptor
// type binding in each set. Values are taken from pDynamicOffsets in an order such that
@@ -353,7 +355,7 @@ Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<
break;
const auto* pBufferVk = Res.pObject.RawPtr<const BufferVkImpl>();
- auto Offset = pBufferVk->GetDynamicOffset(CtxId);
+ auto Offset = pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl);
Offsets[OffsetInd++] = Offset;
++res;
@@ -367,7 +369,7 @@ Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<
const auto* pBufferVkView = Res.pObject.RawPtr<const BufferViewVkImpl>();
const auto* pBufferVk = pBufferVkView->GetBufferVk();
- auto Offset = pBufferVk->GetDynamicOffset(CtxId);
+ auto Offset = pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl);
Offsets[OffsetInd++] = Offset;
++res;
diff --git a/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp
index e1d1810e..3f59f2ee 100644
--- a/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp
@@ -460,6 +460,7 @@ void SwapChainVkImpl::Present(Uint32 SyncInterval)
VERIFY(Result == VK_SUCCESS, "Present failed");
}
+ pImmediateCtxVk->FinishFrame(false);
pDeviceVk->FinishFrame();
if (!m_IsMinimized)
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
index 4dc335d3..429c174c 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
@@ -37,12 +37,12 @@ static VkDeviceSize GetDefaultAlignment(const VulkanUtilities::VulkanPhysicalDev
return std::max(std::max(Limits.minUniformBufferOffsetAlignment, Limits.minTexelBufferOffsetAlignment), Limits.minStorageBufferOffsetAlignment);
}
-VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
- RenderDeviceVkImpl& DeviceVk,
- Uint32 Size) :
- m_RingBuffer(Size, Allocator),
+VulkanDynamicMemoryManager::VulkanDynamicMemoryManager(IMemoryAllocator& Allocator,
+ RenderDeviceVkImpl& DeviceVk,
+ Uint32 Size) :
m_DeviceVk(DeviceVk),
- m_DefaultAlignment(GetDefaultAlignment(DeviceVk.GetPhysicalDevice()))
+ m_DefaultAlignment(GetDefaultAlignment(DeviceVk.GetPhysicalDevice())),
+ m_AllocationStrategy(Allocator, Size)
{
VERIFY( (Size & (MinAlignment-1)) == 0, "Heap size is not min aligned");
VkBufferCreateInfo VkBuffCI = {};
@@ -100,7 +100,7 @@ VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
LOG_INFO_MESSAGE("GPU dynamic heap created. Total buffer size: ", FormatMemorySize(Size, 2) );
}
-void VulkanRingBuffer::Destroy()
+void VulkanDynamicMemoryManager::Destroy()
{
if (m_VkBuffer)
{
@@ -111,44 +111,40 @@ void VulkanRingBuffer::Destroy()
m_CPUAddress = nullptr;
}
-VulkanRingBuffer::~VulkanRingBuffer()
+VulkanDynamicMemoryManager::~VulkanDynamicMemoryManager()
{
VERIFY(m_BufferMemory == VK_NULL_HANDLE && m_VkBuffer == VK_NULL_HANDLE, "Vulkan resources must be explcitly released with Destroy()");
- LOG_INFO_MESSAGE("Dynamic heap ring buffer usage stats:\n"
- " Total size: ", FormatMemorySize(m_RingBuffer.GetMaxSize(), 2),
- ". Peak allocated size: ", FormatMemorySize(m_TotalPeakSize, 2, m_RingBuffer.GetMaxSize()),
- ". Peak frame size: ", FormatMemorySize(m_FramePeakSize, 2, m_RingBuffer.GetMaxSize()),
- ". Peak utilization: ", std::fixed, std::setprecision(1), static_cast<double>(m_TotalPeakSize) / static_cast<double>(std::max(m_RingBuffer.GetMaxSize(), size_t{1})) * 100.0, '%' );
+ auto Size = m_AllocationStrategy.GetSize();
+ LOG_INFO_MESSAGE("Dynamic memory manager usage stats:\n"
+ " Total size: ", FormatMemorySize(Size, 2),
+ ". Peak allocated size: ", FormatMemorySize(m_TotalPeakSize, 2, Size),
+ ". Peak utilization: ", std::fixed, std::setprecision(1), static_cast<double>(m_TotalPeakSize) / static_cast<double>(std::max(Size, size_t{1})) * 100.0, '%' );
}
-RingBuffer::OffsetType VulkanRingBuffer::Allocate(size_t SizeInBytes)
+VulkanDynamicMemoryManager::OffsetType VulkanDynamicMemoryManager::Allocate(OffsetType SizeInBytes)
{
VERIFY( (SizeInBytes & (MinAlignment-1)) == 0, "Allocation size is not minimally aligned" );
- if (SizeInBytes > m_RingBuffer.GetMaxSize())
+ if (SizeInBytes > m_AllocationStrategy.GetSize())
{
- LOG_ERROR("Requested dynamic allocation size ", SizeInBytes, " exceeds maximum ring buffer size ", m_RingBuffer.GetMaxSize(), ". The app should increase dynamic heap size.");
- return RingBuffer::InvalidOffset;
+ LOG_ERROR("Requested dynamic allocation size ", SizeInBytes, " exceeds maximum dynamic memory size ", m_AllocationStrategy.GetSize(), ". The app should increase dynamic heap size.");
+ return AllocationStategy::InvalidOffset;
}
-
- std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
- RingBuffer::OffsetType Offset = m_RingBuffer.Allocate(SizeInBytes);
- if(Offset == RingBuffer::InvalidOffset)
+ auto Offset = m_AllocationStrategy.Allocate(SizeInBytes);
+ if(Offset == AllocationStategy::InvalidOffset)
{
- // Failed to allocate space in the ring buffer. Try to wait for GPU to finish pening frames
- // to release some space
+ // Allocation failed. Try to wait for GPU to finish pending frames to release some space
auto StartIdleTime = std::chrono::high_resolution_clock::now();
static constexpr const auto SleepPeriod = std::chrono::milliseconds(1);
static constexpr const auto MaxIdleDuration = std::chrono::duration<double>{60.0 / 1000.0}; // 60 ms
std::chrono::duration<double> IdleDuration;
Uint32 SleepIterations = 0;
- while(Offset == RingBuffer::InvalidOffset && IdleDuration < MaxIdleDuration)
+ while(Offset == AllocationStategy::InvalidOffset && IdleDuration < MaxIdleDuration)
{
auto LastCompletedFenceValue = m_DeviceVk.GetCompletedFenceValue();
- m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
-
- Offset = m_RingBuffer.Allocate(SizeInBytes);
- if (Offset == RingBuffer::InvalidOffset)
+ m_AllocationStrategy.ReleaseStaleAllocations(LastCompletedFenceValue);
+ Offset = m_AllocationStrategy.Allocate(SizeInBytes);
+ if (Offset == AllocationStategy::InvalidOffset)
{
std::this_thread::sleep_for(SleepPeriod);
++SleepIterations;
@@ -158,48 +154,47 @@ RingBuffer::OffsetType VulkanRingBuffer::Allocate(size_t SizeInBytes)
IdleDuration = std::chrono::duration_cast<std::chrono::duration<double>>(CurrTime - StartIdleTime);
}
- if(Offset == RingBuffer::InvalidOffset)
+ if(Offset == AllocationStategy::InvalidOffset)
{
- LOG_ERROR_MESSAGE("Space in dynamic heap is exausted! After idling for ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms still no space is available. Increase the size of the ring buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ // Last resort - idle GPU (there seems to be a driver bug: vkQueueWaitIdle() deadlocks and never returns)
+ //m_DeviceVk.IdleGPU(true);
+ //auto LastCompletedFenceValue = m_DeviceVk.GetCompletedFenceValue();
+ //m_AllocationStrategy.ReleaseStaleAllocations(LastCompletedFenceValue);
+ //Offset = m_AllocationStrategy.Allocate(SizeInBytes);
+ if (Offset == AllocationStategy::InvalidOffset)
+ {
+ LOG_ERROR_MESSAGE("Space in dynamic heap is exausted! After idling for ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms still no space is available. Increase the size of the heap by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ }
+ //else
+ //{
+ // LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted. Allocation forced idling the GPU. Increase the size of the heap by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ //}
}
else
{
if(SleepIterations == 0)
{
- LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted forcing mid-frame ring buffer shrinkage. Increase the size of the ring buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted forcing mid-frame shrinkage. Increase the size of the heap buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
}
else
{
- LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted. Allocation from the ring buffer forced idle time of ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms. Increase the size of the ring buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted. Allocation forced wait time of ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms. Increase the size of the heap by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
}
}
}
- if (Offset != RingBuffer::InvalidOffset)
+ if (Offset != AllocationStategy::InvalidOffset)
{
- m_CurrentFrameSize += SizeInBytes;
- m_FramePeakSize = std::max(m_FramePeakSize, m_CurrentFrameSize);
- m_TotalPeakSize = std::max(m_TotalPeakSize, m_RingBuffer.GetUsedSize());
+ m_TotalPeakSize = std::max(m_TotalPeakSize, m_AllocationStrategy.GetUsedSize());
}
return Offset;
}
-void VulkanRingBuffer::FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue)
-{
- //
- // Deferred contexts must not map dynamic buffers across several frames!
- //
-
- std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
- m_RingBuffer.FinishCurrentFrame(FenceValue);
- m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
- m_CurrentFrameSize = 0;
-}
VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 Alignment)
{
if (Alignment == 0)
- Alignment = static_cast<Uint32>(m_ParentRingBuffer.m_DefaultAlignment);
+ Alignment = static_cast<Uint32>(m_DynamicMemMgr.m_DefaultAlignment);
const Uint32 AlignmentMask = Alignment - 1;
// Assert that it's a power of two.
@@ -208,23 +203,23 @@ VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 A
// Align the allocation
Uint32 AlignedSize = (SizeInBytes + AlignmentMask) & ~AlignmentMask;
- //
- // Deferred contexts must not map dynamic buffers across several frames!
- //
- auto Offset = RingBuffer::InvalidOffset;
- if(AlignedSize > m_PagSize)
+ auto Offset = InvalidOffset;
+ if(AlignedSize > m_PagSize/2)
{
- // Allocate directly from the ring buffer
- Offset = m_ParentRingBuffer.Allocate(AlignedSize);
+ AlignedSize = (SizeInBytes + VulkanDynamicMemoryManager::MinAlignment) & ~(VulkanDynamicMemoryManager::MinAlignment-1);
+ // Allocate directly from the memory manager
+ Offset = m_DynamicMemMgr.Allocate(AlignedSize);
+ m_Allocations.emplace_back(Offset, AlignedSize);
}
else
{
- if(m_CurrOffset == RingBuffer::InvalidOffset || AlignedSize > m_AvailableSize)
+ if(m_CurrOffset == InvalidOffset || AlignedSize > m_AvailableSize)
{
- m_CurrOffset = m_ParentRingBuffer.Allocate(m_PagSize);
+ m_CurrOffset = m_DynamicMemMgr.Allocate(m_PagSize);
+ m_Allocations.emplace_back(m_CurrOffset, m_PagSize);
m_AvailableSize = m_PagSize;
}
- if(m_CurrOffset != RingBuffer::InvalidOffset)
+ if(m_CurrOffset != InvalidOffset)
{
Offset = m_CurrOffset;
m_AvailableSize -= AlignedSize;
@@ -233,23 +228,35 @@ VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 A
}
// Every device context uses its own dynamic heap, so there is no need to lock
- if(Offset != RingBuffer::InvalidOffset)
+ if(Offset != InvalidOffset)
{
m_CurrAllocatedSize += AlignedSize;
m_CurrUsedSize += SizeInBytes;
m_PeakAllocatedSize = std::max(m_PeakAllocatedSize, m_CurrAllocatedSize);
m_PeakUsedSize = std::max(m_PeakUsedSize, m_CurrUsedSize);
- return VulkanDynamicAllocation{ m_ParentRingBuffer, Offset, SizeInBytes };
+ return VulkanDynamicAllocation{ m_DynamicMemMgr, Offset, SizeInBytes };
}
else
return VulkanDynamicAllocation{};
}
+void VulkanDynamicHeap::FinishFrame(Uint64 FenceValue)
+{
+ m_DynamicMemMgr.DiscardAllocations(m_Allocations, FenceValue);
+ m_Allocations.clear();
+
+ m_CurrOffset = InvalidOffset;
+ m_AvailableSize = 0;
+
+ m_CurrAllocatedSize = 0;
+ m_CurrUsedSize = 0;
+}
+
VulkanDynamicHeap::~VulkanDynamicHeap()
{
LOG_INFO_MESSAGE(m_HeapName, " usage stats:\n"
- " Peak used/peak allocated size: ", FormatMemorySize(m_PeakUsedSize, 2, m_PeakAllocatedSize), '/', FormatMemorySize(m_PeakAllocatedSize, 2, m_PeakAllocatedSize),
+ " Peak used/peak allocated size: ", FormatMemorySize(m_PeakUsedSize, 2, m_PeakAllocatedSize), '/', FormatMemorySize(m_PeakAllocatedSize, 2, m_PeakAllocatedSize),
". Peak utilization: ", std::fixed, std::setprecision(1), static_cast<double>(m_PeakUsedSize) / static_cast<double>(std::max(m_PeakAllocatedSize, 1U)) * 100.0, '%');
}
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
index 38fd781f..b4660819 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
@@ -202,10 +202,10 @@ void VulkanMemoryManager::OnFreeAllocation(VkDeviceSize Size, bool IsHostVisble)
VulkanMemoryManager::~VulkanMemoryManager()
{
LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "' stats:\n"
- " Peak used/peak allocated device-local memory size: ",
+ " Peak used/peak allocated device-local memory size: ",
Diligent::FormatMemorySize(m_PeakUsedSize[0], 2, m_PeakAllocatedSize[0]), "/",
Diligent::FormatMemorySize( m_PeakAllocatedSize[0], 2, m_PeakAllocatedSize[0]),
- "\n Peak used/peak allocated host-visible memory size: ",
+ "\n Peak used/peak allocated host-visible memory size: ",
Diligent::FormatMemorySize(m_PeakUsedSize[1], 2, m_PeakAllocatedSize[1]), "/",
Diligent::FormatMemorySize(m_PeakAllocatedSize[1], 2, m_PeakAllocatedSize[1]));