summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineVulkan
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-06-16 18:57:12 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-06-16 18:57:12 +0000
commit50f26d4c6c75e11ae1cc44aad6c795681de76124 (patch)
treeb32f2071103c294b8d09486fb47ddb87c42bff2f /Graphics/GraphicsEngineVulkan
parentReworked dynamic buffer mapping using ring buffers (diff)
downloadDiligentCore-50f26d4c6c75e11ae1cc44aad6c795681de76124.tar.gz
DiligentCore-50f26d4c6c75e11ae1cc44aad6c795681de76124.zip
Reworked vulkan dynamic heap implementation
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
-rw-r--r--Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h1
-rw-r--r--Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h5
-rw-r--r--Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h104
-rw-r--r--Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp4
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp20
-rw-r--r--Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp2
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp23
-rw-r--r--Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp2
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp148
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp21
11 files changed, 210 insertions, 122 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
index 0f2fe325..4eb5920b 100644
--- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
@@ -205,6 +205,7 @@ private:
Atomics::AtomicInt64 m_NextCmdBuffNumber;
std::vector<uint32_t> m_DynamicBufferOffsets;
+ VulkanDynamicHeap m_DynamicHeap;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
index 10d77889..a076d6ff 100644
--- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
@@ -130,8 +130,7 @@ public:
return m_MemoryMgr.Allocate(MemReqs, MemoryProperties);
}
- VulkanDynamicAllocation AllocateDynamicSpace(Uint32 CtxId, Uint32 SizeInBytes);
- const VulkanDynamicHeap& GetDynamicHeap()const{return m_DynamicHeap;}
+ VulkanRingBuffer& GetDynamicHeapRingBuffer(){return m_DynamicHeapRingBuffer;}
private:
virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat )override final;
@@ -195,7 +194,7 @@ private:
VulkanUtilities::VulkanMemoryManager m_MemoryMgr;
ResourceReleaseQueue<DynamicStaleResourceWrapper> m_ReleaseQueue;
- VulkanDynamicHeap m_DynamicHeap;
+ VulkanRingBuffer m_DynamicHeapRingBuffer;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
index b5dbb2b7..92f01236 100644
--- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
+++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
@@ -173,7 +173,7 @@ public:
template<bool VerifyOnly>
void TransitionResources(DeviceContextVkImpl *pCtxVkImpl);
- void GetDynamicBufferOffset(Uint32 CtxId, std::vector<uint32_t>& Offsets)const;
+ void GetDynamicBufferOffsets(Uint32 CtxId, std::vector<uint32_t>& Offsets)const;
private:
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
index 2127ac3a..d36aa26a 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
@@ -23,6 +23,7 @@
#pragma once
+#include <mutex>
#include "Vulkan.h"
#include "RingBuffer.h"
#include "VulkanUtilities/VulkanLogicalDevice.h"
@@ -31,14 +32,23 @@
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 |....
+//
+
class RenderDeviceVkImpl;
-class VulkanDynamicHeap;
+class VulkanRingBuffer;
+// sizeof(VulkanDynamicAllocation) must be at least 16 to avoid false cache line sharing problems
struct VulkanDynamicAllocation
{
VulkanDynamicAllocation(){}
- VulkanDynamicAllocation(VulkanDynamicHeap& _ParentHeap, size_t _Offset, size_t _Size) :
+ VulkanDynamicAllocation(VulkanRingBuffer& _ParentHeap, size_t _Offset, size_t _Size) :
pParentDynamicHeap(&_ParentHeap),
Offset (_Offset),
Size (_Size)
@@ -77,7 +87,7 @@ struct VulkanDynamicAllocation
return *this;
}
- VulkanDynamicHeap* pParentDynamicHeap = nullptr;
+ 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
@@ -85,22 +95,18 @@ struct VulkanDynamicAllocation
#endif
};
-class VulkanDynamicHeap
+class VulkanRingBuffer
{
public:
- VulkanDynamicHeap(IMemoryAllocator& Allocator,
- class RenderDeviceVkImpl* pDeviceVk,
- Uint32 ImmediateCtxHeapSize,
- Uint32 DeferredCtxHeapSize,
- Uint32 DeferredCtxCount);
- ~VulkanDynamicHeap();
+ VulkanRingBuffer(IMemoryAllocator& Allocator,
+ class RenderDeviceVkImpl* pDeviceVk,
+ Uint32 Size);
+ ~VulkanRingBuffer();
- VulkanDynamicHeap (const VulkanDynamicHeap&) = delete;
- VulkanDynamicHeap (VulkanDynamicHeap&&) = delete;
- VulkanDynamicHeap& operator= (const VulkanDynamicHeap&) = delete;
- VulkanDynamicHeap& operator= (VulkanDynamicHeap&&) = delete;
-
- VulkanDynamicAllocation Allocate( Uint32 CtxId, size_t SizeInBytes, size_t Alignment = 0);
+ VulkanRingBuffer (const VulkanRingBuffer&) = delete;
+ VulkanRingBuffer (VulkanRingBuffer&&) = delete;
+ VulkanRingBuffer& operator= (const VulkanRingBuffer&) = delete;
+ VulkanRingBuffer& operator= (VulkanRingBuffer&&) = delete;
void FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue);
void Destroy();
@@ -109,22 +115,64 @@ public:
Uint8* GetCPUAddress()const{return m_CPUAddress;}
private:
- struct VulkanRingBuffer
- {
- VulkanRingBuffer(Uint32 Size, IMemoryAllocator &Allocator, Uint32 _BaseOffset) :
- RingBuff(Size, Allocator),
- BaseOffset(_BaseOffset)
- {}
- RingBuffer RingBuff;
- const Uint32 BaseOffset;
- };
- std::vector<VulkanRingBuffer> m_RingBuffers;
- RenderDeviceVkImpl* const m_pDeviceVk;
+ friend class VulkanDynamicHeap;
+
+ static constexpr const Uint32 MinAlignment = 1024;
+ RingBuffer::OffsetType Allocate(size_t SizeInBytes);
+
+ std::mutex m_RingBuffMtx;
+ RingBuffer m_RingBuffer;
+ RenderDeviceVkImpl* const m_pDeviceVk;
VulkanUtilities::BufferWrapper m_VkBuffer;
VulkanUtilities::DeviceMemoryWrapper m_BufferMemory;
Uint8* m_CPUAddress;
- const uint32_t m_DefaultAlignment;
+ const VkDeviceSize m_DefaultAlignment;
+ RingBuffer::OffsetType m_TotalPeakSize = 0;
+ RingBuffer::OffsetType m_CurrentFrameSize = 0;
+ RingBuffer::OffsetType m_FramePeakSize = 0;
+};
+
+
+class VulkanDynamicHeap
+{
+public:
+ VulkanDynamicHeap(VulkanRingBuffer& ParentRingBuffer, std::string HeapName, Uint32 PageSize) :
+ m_ParentRingBuffer(ParentRingBuffer),
+ m_HeapName(std::move(HeapName)),
+ m_PagSize(PageSize)
+ {}
+
+ VulkanDynamicHeap (const VulkanDynamicHeap&) = delete;
+ VulkanDynamicHeap (VulkanDynamicHeap&&) = delete;
+ VulkanDynamicHeap& operator= (const VulkanDynamicHeap&) = delete;
+ VulkanDynamicHeap& operator= (VulkanDynamicHeap&&) = delete;
+
+ ~VulkanDynamicHeap();
+
+ VulkanDynamicAllocation Allocate(Uint32 SizeInBytes, Uint32 Alignment);
+
+ void Reset()
+ {
+ m_CurrOffset = RingBuffer::InvalidOffset;
+ m_AvailableSize = 0;
+
+ m_CurrAllocatedSize = 0;
+ m_CurrUsedSize = 0;
+ }
+
+private:
+ VulkanRingBuffer& m_ParentRingBuffer;
+ const std::string m_HeapName;
+
+ RingBuffer::OffsetType m_CurrOffset = RingBuffer::InvalidOffset;
+ const Uint32 m_PagSize;
+ Uint32 m_AvailableSize = 0;
+
+ Uint32 m_CurrAllocatedSize = 0;
+ Uint32 m_CurrUsedSize = 0;
+ Uint32 m_PeakAllocatedSize = 0;
+ Uint32 m_PeakUsedSize = 0;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
index f9e4b840..f199d80f 100644
--- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
@@ -354,7 +354,7 @@ void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapF
{
VERIFY(MapFlags & MAP_FLAG_DISCARD, "Vk buffer must be mapped for writing with MAP_FLAG_DISCARD flag");
auto DynAlloc = pDeviceContextVk->AllocateDynamicSpace(m_Desc.uiSizeInBytes);
- const auto& DynamicHeap = pDeviceVk->GetDynamicHeap();
+ const auto& DynamicHeap = pDeviceVk->GetDynamicHeapRingBuffer();
auto* CPUAddress = DynamicHeap.GetCPUAddress();
pMappedData = CPUAddress + DynAlloc.Offset;
m_DynamicAllocations[pDeviceContextVk->GetContextId()] = std::move(DynAlloc);
@@ -485,7 +485,7 @@ VkBuffer BufferVkImpl::GetVkBuffer()const
else
{
VERIFY(m_Desc.Usage == USAGE_DYNAMIC, "Dynamic buffer expected");
- return GetDevice<RenderDeviceVkImpl>()->GetDynamicHeap().GetVkBuffer();
+ return GetDevice<RenderDeviceVkImpl>()->GetDynamicHeapRingBuffer().GetVkBuffer();
}
}
diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
index daf257b4..1f9abad0 100644
--- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
@@ -83,7 +83,13 @@ namespace Diligent
},
Attribs.DynamicDescriptorPoolSize.MaxDescriptorSets,
},
- m_NextCmdBuffNumber(0)
+ m_NextCmdBuffNumber(0),
+ m_DynamicHeap
+ {
+ pDeviceVkImpl->GetDynamicHeapRingBuffer(),
+ bIsDeferred ? "Dynamic heap for immediate context" : "Dynamic heap for deferred context",
+ bIsDeferred ? Attribs.DeferredCtxDynamicHeapPageSize : Attribs.ImmediateCtxDynamicHeapPageSize
+ }
{
m_DynamicBufferOffsets.reserve(64);
}
@@ -742,6 +748,7 @@ namespace Diligent
m_ReleaseQueue.Purge(CompletedFenceValue);
m_UploadHeap.ShrinkMemory();
m_DynamicDescriptorPool.ReleaseStaleAllocations(CompletedFenceValue);
+ m_DynamicHeap.Reset();
}
void DeviceContextVkImpl::ReleaseStaleContextResources(Uint64 SubmittedCmdBufferNumber, Uint64 SubmittedFenceValue, Uint64 CompletedFenceValue)
@@ -1261,6 +1268,15 @@ namespace Diligent
VulkanDynamicAllocation DeviceContextVkImpl::AllocateDynamicSpace(Uint32 SizeInBytes)
{
auto *pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>();
- return pDeviceVkImpl->AllocateDynamicSpace(m_ContextId, SizeInBytes);
+
+ auto DynAlloc = m_DynamicHeap.Allocate(SizeInBytes, 0);
+ if (DynAlloc.pParentDynamicHeap == nullptr)
+ {
+ UNSUPPORTED("Failed to allocate dynamic memory");
+ }
+#ifdef _DEBUG
+ DynAlloc.dbgFrameNumber = pDeviceVkImpl->GetCurrentFrameNumber();
+#endif
+ return DynAlloc;
}
}
diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
index 7bc5daff..6ec059fe 100644
--- a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
@@ -455,7 +455,7 @@ void PipelineLayout::BindDescriptorSets(DeviceContextVkImpl* pCtxVkImpl,
auto& DynamicOffsets = pCtxVkImpl->GetDynamicBufferOffsets();
DynamicOffsets.resize(TotalDynamicDescriptors);
- ResourceCache.GetDynamicBufferOffset(pCtxVkImpl->GetContextId(), DynamicOffsets);
+ ResourceCache.GetDynamicBufferOffsets(pCtxVkImpl->GetContextId(), DynamicOffsets);
auto& CmdBuffer = pCtxVkImpl->GetCommandBuffer();
// vkCmdBindDescriptorSets causes the sets numbered [firstSet .. firstSet+descriptorSetCount-1] to use the
diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
index 08d248a6..cb30f577 100644
--- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
@@ -88,13 +88,11 @@ 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_DynamicHeap
+ m_DynamicHeapRingBuffer
{
GetRawAllocator(),
this,
- CreationAttribs.ImmediateCtxDynamicHeapSize,
- CreationAttribs.DeferredCtxDynamicHeapSize,
- NumDeferredContexts
+ CreationAttribs.DynamicHeapSize
}
{
m_DeviceCaps.DevType = DeviceType::Vulkan;
@@ -109,7 +107,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters*
RenderDeviceVkImpl::~RenderDeviceVkImpl()
{
// Explicitly destroy dynamic heap
- m_DynamicHeap.Destroy();
+ m_DynamicHeapRingBuffer.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()
@@ -343,7 +341,7 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
// until the next command buffer is finished by the GPU
ProcessStaleResources(SubmittedCmdBuffNumber, NextFenceValue, CompletedFenceValue);
- m_DynamicHeap.FinishFrame(NextFenceValue, CompletedFenceValue);
+ m_DynamicHeapRingBuffer.FinishFrame(NextFenceValue, CompletedFenceValue);
Atomics::AtomicIncrement(m_FrameNumber);
}
@@ -358,19 +356,6 @@ void RenderDeviceVkImpl::ProcessStaleResources(Uint64 SubmittedCmdBufferNumber,
}
-VulkanDynamicAllocation RenderDeviceVkImpl::AllocateDynamicSpace(Uint32 CtxId, Uint32 SizeInBytes)
-{
- auto DynAlloc = m_DynamicHeap.Allocate(CtxId, SizeInBytes);
- if (DynAlloc.pParentDynamicHeap == nullptr)
- {
- UNSUPPORTED("Failed to allocate dynamic memory");
- }
-#ifdef _DEBUG
- DynAlloc.dbgFrameNumber = m_FrameNumber;
-#endif
- return DynAlloc;
-}
-
void RenderDeviceVkImpl::TestTextureFormat( TEXTURE_FORMAT TexFormat )
{
auto &TexFormatInfo = m_TextureFormatsInfo[TexFormat];
diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
index fa510f60..4b16539e 100644
--- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
@@ -284,7 +284,7 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWrite
return DescrImgInfo;
}
-void ShaderResourceCacheVk::GetDynamicBufferOffset(Uint32 CtxId, std::vector<uint32_t>& Offsets)const
+void ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<uint32_t>& Offsets)const
{
// 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
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
index 51c71604..a62368c6 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
@@ -28,28 +28,33 @@
namespace Diligent
{
-static uint32_t GetDefaultAlignment(const VulkanUtilities::VulkanPhysicalDevice& PhysicalDevice)
+static VkDeviceSize GetDefaultAlignment(const VulkanUtilities::VulkanPhysicalDevice& PhysicalDevice)
{
const auto& Props = PhysicalDevice.GetProperties();
const auto& Limits = Props.limits;
return std::max(std::max(Limits.minUniformBufferOffsetAlignment, Limits.minTexelBufferOffsetAlignment), Limits.minStorageBufferOffsetAlignment);
}
-VulkanDynamicHeap::VulkanDynamicHeap(IMemoryAllocator& Allocator,
+VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
RenderDeviceVkImpl* pDeviceVk,
- Uint32 ImmediateCtxHeapSize,
- Uint32 DeferredCtxHeapSize,
- Uint32 DeferredCtxCount) :
+ Uint32 Size) :
+ m_RingBuffer(Size, Allocator),
m_pDeviceVk(pDeviceVk),
m_DefaultAlignment(GetDefaultAlignment(pDeviceVk->GetPhysicalDevice()))
{
- Uint32 BufferSize = ImmediateCtxHeapSize + DeferredCtxHeapSize * DeferredCtxCount;
+ VERIFY( (Size & (MinAlignment-1)) == 0, "Heap size is not min aligned");
VkBufferCreateInfo VkBuffCI = {};
VkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
VkBuffCI.pNext = nullptr;
VkBuffCI.flags = 0; // VK_BUFFER_CREATE_SPARSE_BINDING_BIT, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT
- VkBuffCI.size = BufferSize;
- VkBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
+ VkBuffCI.size = Size;
+ VkBuffCI.usage =
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
VkBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkBuffCI.queueFamilyIndexCount = 0;
VkBuffCI.pQueueFamilyIndices = nullptr;
@@ -90,18 +95,10 @@ VulkanDynamicHeap::VulkanDynamicHeap(IMemoryAllocator& Allocator,
err = LogicalDevice.BindBufferMemory(m_VkBuffer, m_BufferMemory, 0 /*offset*/);
CHECK_VK_ERROR_AND_THROW(err, "Failed to bind bufer memory");
- LOG_INFO_MESSAGE("GPU dynamic heap created. Total buffer size: ", BufferSize);
-
- m_RingBuffers.reserve(1 + DeferredCtxCount);
- Uint32 BaseOffset = 0;
- for(Uint32 ctx = 0; ctx < 1 + DeferredCtxCount; ++ctx)
- {
- Uint32 HeapSize = ctx == 0 ? ImmediateCtxHeapSize : DeferredCtxHeapSize;
- m_RingBuffers.emplace_back( HeapSize, Allocator, BaseOffset );
- }
+ LOG_INFO_MESSAGE("GPU dynamic heap created. Total buffer size: ", HeapSize);
}
-void VulkanDynamicHeap::Destroy()
+void VulkanRingBuffer::Destroy()
{
if (m_VkBuffer)
{
@@ -112,63 +109,106 @@ void VulkanDynamicHeap::Destroy()
m_CPUAddress = nullptr;
}
-VulkanDynamicHeap::~VulkanDynamicHeap()
+VulkanRingBuffer::~VulkanRingBuffer()
{
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: ", SizeFormatter{ m_RingBuffer.GetMaxSize(), 2 },
+ ". Peak buffer size: ", SizeFormatter{ m_TotalPeakSize, 2, m_RingBuffer.GetMaxSize() },
+ ". Peak frame size: ", SizeFormatter{ 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, '%' );
}
-
-VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 CtxId, size_t SizeInBytes, size_t Alignment /*= 0*/)
+RingBuffer::OffsetType VulkanRingBuffer::Allocate(size_t SizeInBytes)
{
- VERIFY_EXPR(CtxId < m_RingBuffers.size());
-
- if(Alignment == 0)
- Alignment = m_DefaultAlignment;
+ VERIFY( (SizeInBytes & (MinAlignment-1)) == 0, "Allocation size is not minimally aligned" );
- auto& RingBuff = m_RingBuffers[CtxId].RingBuff;
- if (SizeInBytes > RingBuff.GetMaxSize())
+ if (SizeInBytes > m_RingBuffer.GetMaxSize())
{
- LOG_ERROR("Requested dynamic allocation size ", SizeInBytes, " exceeds maximum ring buffer size ", RingBuff.GetMaxSize(), ". The app should increase dynamic heap size.");
- return VulkanDynamicAllocation{};
+ 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;
}
+
+ std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
+ auto Offset = m_RingBuffer.Allocate(SizeInBytes);
+ if(Offset == RingBuffer::InvalidOffset)
+ {
+ UNEXPECTED("Allocation failed");
+ }
+ m_CurrentFrameSize += SizeInBytes;
+ m_FramePeakSize = std::max(m_FramePeakSize, m_CurrentFrameSize);
+ m_TotalPeakSize = std::max(m_TotalPeakSize, m_RingBuffer.GetUsedSize());
+ return Offset;
+}
- // Every device context uses its own upload heap, so there is no need to lock
- //std::lock_guard<std::mutex> Lock(m_Mutex);
-
+void VulkanRingBuffer::FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue)
+{
//
- // Deferred contexts must not update resources or map dynamic buffers
- // across several frames!
+ // Deferred contexts must not map dynamic buffers across several frames!
//
- const size_t AlignmentMask = Alignment - 1;
- // Assert that it's a power of two.
- VERIFY_EXPR((AlignmentMask & Alignment) == 0);
- // Align the allocation
- const size_t AlignedSize = (SizeInBytes + AlignmentMask) & ~AlignmentMask;
- auto Offset = RingBuff.Allocate(AlignedSize);
- while(Offset == RingBuffer::InvalidOffset)
- {
- VulkanDynamicAllocation{};
- }
-
- return VulkanDynamicAllocation{ *this, m_RingBuffers[CtxId].BaseOffset + Offset, SizeInBytes };
+ std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
+ m_RingBuffer.FinishCurrentFrame(FenceValue);
+ m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
+ m_CurrentFrameSize = 0;
}
-void VulkanDynamicHeap::FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue)
+VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 Alignment)
{
- // Every device context has its own upload heap, so there is no need to lock
- //std::lock_guard<std::mutex> Lock(m_Mutex);
+ if (Alignment == 0)
+ Alignment = static_cast<Uint32>(m_ParentRingBuffer.m_DefaultAlignment);
+
+ const Uint32 AlignmentMask = Alignment - 1;
+ // Assert that it's a power of two.
+ VERIFY_EXPR((AlignmentMask & Alignment) == 0);
+
+ // Align the allocation
+ Uint32 AlignedSize = (SizeInBytes + AlignmentMask) & ~AlignmentMask;
//
- // Deferred contexts must not update resources or map dynamic buffers
- // across several frames!
+ // Deferred contexts must not map dynamic buffers across several frames!
//
+ auto Offset = RingBuffer::InvalidOffset;
+ if(AlignedSize > m_PagSize)
+ {
+ // Allocate directly from the ring buffer
+ Offset = m_ParentRingBuffer.Allocate(AlignedSize);
+ }
+ else
+ {
+ if(m_CurrOffset == RingBuffer::InvalidOffset || AlignedSize > m_AvailableSize)
+ {
+ m_CurrOffset = m_ParentRingBuffer.Allocate(m_PagSize);
+ m_AvailableSize = m_PagSize;
+ }
+ if(m_CurrOffset != RingBuffer::InvalidOffset)
+ {
+ Offset = m_CurrOffset;
+ m_AvailableSize -= AlignedSize;
+ m_CurrOffset += AlignedSize;
+ }
+ }
- for (auto& RingBuff : m_RingBuffers)
+ // Every device context uses its own dynamic heap, so there is no need to lock
+ if(Offset != RingBuffer::InvalidOffset)
{
- RingBuff.RingBuff.FinishCurrentFrame(FenceValue);
- RingBuff.RingBuff.ReleaseCompletedFrames(LastCompletedFenceValue);
+ 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 };
}
+ else
+ return VulkanDynamicAllocation{};
+}
+
+VulkanDynamicHeap::~VulkanDynamicHeap()
+{
+ LOG_INFO_MESSAGE(m_HeapName, " usage stats:\n"
+ " Peak allocated size: ", SizeFormatter{ m_PeakAllocatedSize, 2, m_PeakAllocatedSize },
+ ". Peak used size: ", SizeFormatter{ m_PeakUsedSize, 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 8739852f..54d39605 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
@@ -23,7 +23,6 @@
#include "pch.h"
#include <sstream>
-#include <iomanip>
#include "VulkanUtilities/VulkanMemoryManager.h"
namespace VulkanUtilities
@@ -154,9 +153,9 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(VkDeviceSize Size, VkDevice
m_PeakAllocatedSize[stat_ind] = std::max(m_PeakAllocatedSize[stat_ind], m_CurrAllocatedSize[stat_ind]);
auto it = m_Pages.emplace(MemoryTypeIndex, VulkanMemoryPage{*this, PageSize, MemoryTypeIndex, HostVisible});
- LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "': created new ", (HostVisible ? "host-visible" : "device-local"), " page. (",
- std::fixed, std::setprecision(2), PageSize / double{1 << 20}, " MB, type idx: ", MemoryTypeIndex,
- "). Current allocated size: ", std::fixed, std::setprecision(2), m_CurrAllocatedSize[stat_ind] / double{1 << 20}, " MB");
+ LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "': created new ", (HostVisible ? "host-visible" : "device-local"),
+ " page. (", Diligent::SizeFormatter{ PageSize, 2}, ", type idx: ", MemoryTypeIndex,
+ "). Current allocated size: ", Diligent::SizeFormatter{ m_CurrAllocatedSize[stat_ind], 2});
OnNewPageCreated(it->second);
Allocation = it->second.Allocate(Size);
VERIFY(Allocation.Page != nullptr, "Failed to allocate new memory page");
@@ -186,9 +185,9 @@ void VulkanMemoryManager::ShrinkMemory()
{
auto PageSize = Page.GetPageSize();
m_CurrAllocatedSize[IsHostVisible ? 1 : 0] -= PageSize;
- LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "': destroying ", (IsHostVisible ? "host-visible" : "device-local"), " page (",
- std::fixed, std::setprecision(2), PageSize / double{1 << 20},
- " MB). Current allocated size: ", std::fixed, std::setprecision(2), m_CurrAllocatedSize[IsHostVisible ? 1 : 0] / double{1 << 20}, " MB");
+ LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "': destroying ", (IsHostVisible ? "host-visible" : "device-local"),
+ " page (", Diligent::SizeFormatter{ PageSize, 2 }, ")."
+ " Current allocated size: ", Diligent::SizeFormatter{ m_CurrAllocatedSize[IsHostVisible ? 1 : 0], 2 });
OnPageDestroy(Page);
m_Pages.erase(curr_it);
}
@@ -204,11 +203,11 @@ VulkanMemoryManager::~VulkanMemoryManager()
{
LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "' stats:\n"
" Peak used/peak allocated device-local memory size: ",
- std::fixed, std::setprecision(2), m_PeakUsedSize[0] / double{1 << 20}, "/",
- std::fixed, std::setprecision(2), m_PeakAllocatedSize[0] / double{1 << 20}, " MB. "
+ Diligent::SizeFormatter{ m_PeakUsedSize[0], 2, m_PeakAllocatedSize[0] }, "/",
+ Diligent::SizeFormatter{ m_PeakAllocatedSize[0], 2, m_PeakAllocatedSize[0] },
"\n Peak used/peak allocated host-visible memory size: ",
- std::fixed, std::setprecision(2), m_PeakUsedSize[1] / double{1 << 20}, "/",
- std::fixed, std::setprecision(2), m_PeakAllocatedSize[1] / double{1 << 20}, " MB.");
+ Diligent::SizeFormatter{ m_PeakUsedSize[1], 2, m_PeakAllocatedSize[1]}, "/",
+ Diligent::SizeFormatter{ m_PeakAllocatedSize[1], 2, m_PeakAllocatedSize[1]});
for(auto it=m_Pages.begin(); it != m_Pages.end(); ++it )
VERIFY(it->second.IsEmpty(), "The page contains outstanding allocations");