diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2018-06-16 18:57:12 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2018-06-16 18:57:12 +0000 |
| commit | 50f26d4c6c75e11ae1cc44aad6c795681de76124 (patch) | |
| tree | b32f2071103c294b8d09486fb47ddb87c42bff2f /Graphics/GraphicsEngineVulkan | |
| parent | Reworked dynamic buffer mapping using ring buffers (diff) | |
| download | DiligentCore-50f26d4c6c75e11ae1cc44aad6c795681de76124.tar.gz DiligentCore-50f26d4c6c75e11ae1cc44aad6c795681de76124.zip | |
Reworked vulkan dynamic heap implementation
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
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"); |
