diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2018-06-05 06:07:00 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2018-06-05 06:07:00 +0000 |
| commit | 31ad9001b14c006000fa3dde1557d9a7e658316c (patch) | |
| tree | f2ae23256d73adf169580c501915c906528fbd7e /Graphics/GraphicsEngineVulkan | |
| parent | Updated info messages from memory manager (diff) | |
| download | DiligentCore-31ad9001b14c006000fa3dde1557d9a7e658316c.tar.gz DiligentCore-31ad9001b14c006000fa3dde1557d9a7e658316c.zip | |
Reworked upload heap in Vulkan
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
11 files changed, 313 insertions, 93 deletions
diff --git a/Graphics/GraphicsEngineVulkan/CMakeLists.txt b/Graphics/GraphicsEngineVulkan/CMakeLists.txt index 31502969..ab667660 100644 --- a/Graphics/GraphicsEngineVulkan/CMakeLists.txt +++ b/Graphics/GraphicsEngineVulkan/CMakeLists.txt @@ -27,8 +27,8 @@ set(INCLUDE include/SwapChainVkImpl.h include/TextureVkImpl.h include/TextureViewVkImpl.h - include/VulkanTypeConversions.h include/VulkanErrors.h + include/VulkanTypeConversions.h ) set(VULKAN_UTILS_INCLUDE @@ -42,6 +42,7 @@ set(VULKAN_UTILS_INCLUDE include/VulkanUtilities/VulkanMemoryManager.h include/VulkanUtilities/VulkanObjectWrappers.h include/VulkanUtilities/VulkanPhysicalDevice.h + include/VulkanUtilities/VulkanUploadHeap.h ) @@ -99,6 +100,7 @@ set(VULKAN_UTILS_SRC src/VulkanUtilities/VulkanLogicalDevice.cpp src/VulkanUtilities/VulkanMemoryManager.cpp src/VulkanUtilities/VulkanPhysicalDevice.cpp + src/VulkanUtilities/VulkanUploadHeap.cpp ) #set(SHADERS diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h index a748ca58..e14ef3c1 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h @@ -32,6 +32,7 @@ #include "GenerateMips.h" #include "VulkanUtilities/VulkanCommandBufferPool.h" #include "VulkanUtilities/VulkanCommandBuffer.h" +#include "VulkanUtilities/VulkanUploadHeap.h" #include "VulkanDynamicHeap.h" #ifdef _DEBUG @@ -112,8 +113,8 @@ public: ///// Number of different shader types (Vertex, Pixel, Geometry, Domain, Hull, Compute) //static constexpr int NumShaderTypes = 6; - void UpdateBufferRegion(class BufferVkImpl *pBuffVk, struct VulkanDynamicAllocation& Allocation, Uint64 DstOffset, Uint64 NumBytes); - void UpdateBufferRegion(class BufferVkImpl *pBuffVk, const void *pData, Uint64 DstOffset, Uint64 NumBytes); + void UpdateBufferRegion(class BufferVkImpl* pBuffVk, VulkanUtilities::VulkanUploadAllocation& Allocation, Uint64 DstOffset, Uint64 NumBytes); + void UpdateBufferRegion(class BufferVkImpl* pBuffVk, const void *pData, Uint64 DstOffset, Uint64 NumBytes); void CopyBufferRegion(class BufferVkImpl *pSrcBuffVk, class BufferVkImpl *pDstBuffVk, Uint64 SrcOffset, Uint64 DstOffset, Uint64 NumBytes); void CopyTextureRegion(class TextureVkImpl *pSrcTexture, class TextureVkImpl *pDstTexture, const VkImageCopy &CopyRegion); @@ -122,7 +123,7 @@ public: #endif void GenerateMips(class TextureViewVkImpl *pTexView); - void* AllocateDynamicUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes, size_t Alignment); + void* AllocateUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes); void CopyAndFreeDynamicUploadData(BufferVkImpl* pBuffer); Uint32 GetContextId()const{return m_ContextId;} @@ -178,7 +179,7 @@ private: std::vector<VkPipelineStageFlags> m_WaitDstStageMasks; std::vector<VkSemaphore> m_SignalSemaphores; - std::unordered_map<BufferVkImpl*, VulkanDynamicAllocation> m_UploadAllocations; + std::unordered_map<BufferVkImpl*, VulkanUtilities::VulkanUploadAllocation> m_UploadAllocations; }; } diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h index fb816379..7ab4df4e 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h @@ -40,6 +40,7 @@ #include "VulkanUtilities/VulkanLogicalDevice.h" #include "VulkanUtilities/VulkanObjectWrappers.h" #include "VulkanUtilities/VulkanMemoryManager.h" +#include "VulkanUtilities/VulkanUploadHeap.h" #include "FramebufferCache.h" #include "CommandPoolManager.h" @@ -102,11 +103,9 @@ public: void DisposeTransientCmdPool(VulkanUtilities::CommandPoolWrapper&& CmdPool); - template<typename VulkanObjectType> - void SafeReleaseVkObject(VulkanUtilities::VulkanObjectWrapper<VulkanObjectType>&& vkObject); - void SafeReleaseMemoryAllocation(VulkanUtilities::VulkanMemoryAllocation&& Allocation); - - + template<typename ObjectType> + void SafeReleaseVkObject(ObjectType&& Object); + void FinishFrame(bool ReleaseAllResources); virtual void FinishFrame()override final { FinishFrame(false); } @@ -121,9 +120,9 @@ public: return m_DescriptorPools[1 + CtxId].Allocate(SetLayout); } - VulkanDynamicAllocation AllocateDynamicUploadSpace(Uint32 CtxId, size_t Size, size_t Alignment) + VulkanUtilities::VulkanUploadAllocation AllocateUploadSpace(Uint32 CtxId, size_t Size) { - return m_UploadHeaps[CtxId]->Allocate(Size, Alignment); + return m_UploadHeaps[CtxId].Allocate(Size); } std::shared_ptr<const VulkanUtilities::VulkanInstance> GetVulkanInstance()const{return m_VulkanInstance;} @@ -208,8 +207,7 @@ private: // [2+] - Deferred context dynamic descriptor pool std::vector<DescriptorPoolManager, STDAllocatorRawMem<DescriptorPoolManager> > m_DescriptorPools; - typedef std::unique_ptr<VulkanDynamicHeap, STDDeleterRawMem<VulkanDynamicHeap> > UploadHeapPoolElemType; - std::vector< UploadHeapPoolElemType, STDAllocatorRawMem<UploadHeapPoolElemType> > m_UploadHeaps; + std::vector<VulkanUtilities::VulkanUploadHeap, STDAllocatorRawMem<VulkanUtilities::VulkanUploadHeap>> m_UploadHeaps; // These one-time command pools are used by buffer and texture constructors to // issue copy commands. Vulkan requires that every command pool is used by one thread diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.h index bbe6b91e..8a50da8d 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.h +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.h @@ -91,7 +91,7 @@ public: VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, VkDeviceSize PageSize, uint32_t MemoryTypeIndex, - bool MapMemory)noexcept; + bool IsHostVisible)noexcept; ~VulkanMemoryPage(); VulkanMemoryPage(VulkanMemoryPage&& rhs)noexcept : @@ -150,29 +150,57 @@ public: m_DeviceLocalReserveSize(DeviceLocalReserveSize), m_HostVisibleReserveSize(HostVisibleReserveSize) {} + + // We have to write this constructor because on msvc default + // constructor is not labeled with noexcept, which makes all + // std containers use copy instead of move + VulkanMemoryManager(VulkanMemoryManager&& rhs)noexcept : + m_MgrName (std::move(rhs.m_MgrName)), + m_LogicalDevice (rhs.m_LogicalDevice), + m_PhysicalDevice (rhs.m_PhysicalDevice), + m_Allocator (rhs.m_Allocator), + m_Pages (std::move(rhs.m_Pages)), + + m_DeviceLocalPageSize (rhs.m_DeviceLocalPageSize), + m_HostVisiblePageSize (rhs.m_HostVisiblePageSize), + m_DeviceLocalReserveSize (rhs.m_DeviceLocalReserveSize), + m_HostVisibleReserveSize (rhs.m_HostVisibleReserveSize), + //m_CurrUsedSize (rhs.m_CurrUsedSize), + m_PeakUsedSize (rhs.m_PeakUsedSize), + m_CurrAllocatedSize (rhs.m_CurrAllocatedSize), + m_PeakAllocatedSize (rhs.m_PeakAllocatedSize) + { + for(int i=0; i < m_CurrUsedSize.size(); ++i) + m_CurrUsedSize[i].store(rhs.m_CurrUsedSize[i].load()); + } + ~VulkanMemoryManager(); VulkanMemoryManager (const VulkanMemoryManager&) = delete; - VulkanMemoryManager (VulkanMemoryManager&&) = delete; VulkanMemoryManager& operator= (const VulkanMemoryManager&) = delete; VulkanMemoryManager& operator= (VulkanMemoryManager&&) = delete; + VulkanMemoryAllocation Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible); VulkanMemoryAllocation Allocate(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProps); void ShrinkMemory(); -private: +protected: friend class VulkanMemoryPage; - const std::string m_MgrName; + virtual void OnNewPageCreated(VulkanMemoryPage& NewPage){} + virtual void OnPageDestroy(VulkanMemoryPage& Page){} + + std::string m_MgrName; const VulkanLogicalDevice& m_LogicalDevice; const VulkanPhysicalDevice& m_PhysicalDevice; Diligent::IMemoryAllocator& m_Allocator; + std::mutex m_PagesMtx; std::unordered_multimap<uint32_t, VulkanMemoryPage> m_Pages; - std::mutex m_Mutex; + const VkDeviceSize m_DeviceLocalPageSize; const VkDeviceSize m_HostVisiblePageSize; const VkDeviceSize m_DeviceLocalReserveSize; @@ -185,6 +213,8 @@ private: std::array<VkDeviceSize, 2> m_PeakUsedSize = {}; std::array<VkDeviceSize, 2> m_CurrAllocatedSize = {}; std::array<VkDeviceSize, 2> m_PeakAllocatedSize = {}; + + // If adding new member, do not forget to update move ctor }; } diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanUploadHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanUploadHeap.h new file mode 100644 index 00000000..a1f07a11 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanUploadHeap.h @@ -0,0 +1,84 @@ +/* Copyright 2015-2018 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include <unordered_map> +#include "VulkanUtilities/VulkanMemoryManager.h" + +namespace VulkanUtilities +{ + +struct VulkanUploadAllocation +{ + VulkanUploadAllocation(){} + VulkanUploadAllocation(VulkanMemoryAllocation&& _MemAllocation, VkBuffer _vkBuffer) : + MemAllocation(std::move(_MemAllocation)), + vkBuffer(_vkBuffer) + {} + VulkanUploadAllocation (const VulkanUploadAllocation&) = delete; + VulkanUploadAllocation& operator = (const VulkanUploadAllocation&) = delete; + VulkanUploadAllocation (VulkanUploadAllocation&&) = default; + VulkanUploadAllocation& operator = (VulkanUploadAllocation&&) = default; + + VulkanMemoryAllocation MemAllocation; + VkBuffer vkBuffer = VK_NULL_HANDLE; // Vulkan buffer associated with this memory. +}; + +class VulkanUploadHeap : public VulkanMemoryManager +{ +public: + VulkanUploadHeap(std::string MgrName, + const VulkanLogicalDevice& LogicalDevice, + const VulkanPhysicalDevice& PhysicalDevice, + Diligent::IMemoryAllocator& Allocator, + VkDeviceSize HostVisiblePageSize, + VkDeviceSize HostVisibleReserveSize); + + VulkanUploadHeap(VulkanUploadHeap&& rhs)noexcept : + VulkanMemoryManager(std::move(rhs)), + m_StagingBufferMemoryTypeIndex(rhs.m_StagingBufferMemoryTypeIndex), + m_Buffers(std::move(rhs.m_Buffers)) + { + } + VulkanUploadHeap (const VulkanUploadHeap&) = delete; + VulkanUploadHeap& operator= (VulkanUploadHeap&) = delete; + VulkanUploadHeap& operator= (VulkanUploadHeap&& rhs) = delete; + + ~VulkanUploadHeap(); + + VulkanUploadAllocation Allocate(size_t SizeInBytes); + +private: + virtual void OnNewPageCreated(VulkanMemoryPage& NewPage); + virtual void OnPageDestroy(VulkanMemoryPage& Page); + VkBufferCreateInfo GetStagingBufferCI()const; + + uint32_t m_StagingBufferMemoryTypeIndex = 0; + std::mutex m_BuffersMtx; + std::unordered_map<VulkanMemoryPage*, VulkanUtilities::BufferWrapper> m_Buffers; + + // If adding new member, do not forget to update move ctor +}; + +} diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp index a1711629..fa5170ae 100644 --- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp @@ -182,7 +182,7 @@ BufferVkImpl :: BufferVkImpl(IReferenceCounters *pRefCounters, // until copy operation is complete. This must be done after // submitting command list for execution! pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBuffer)); - pRenderDeviceVk->SafeReleaseMemoryAllocation(std::move(StagingMemoryAllocation)); + pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingMemoryAllocation)); } else { @@ -259,7 +259,7 @@ BufferVkImpl :: ~BufferVkImpl() auto *pDeviceVkImpl = ValidatedCast<RenderDeviceVkImpl>(GetDevice()); // Vk object can only be destroyed when it is no longer used by the GPU pDeviceVkImpl->SafeReleaseVkObject(std::move(m_VulkanBuffer)); - pDeviceVkImpl->SafeReleaseMemoryAllocation(std::move(m_MemoryAllocation)); + pDeviceVkImpl->SafeReleaseVkObject(std::move(m_MemoryAllocation)); } IMPLEMENT_QUERY_INTERFACE( BufferVkImpl, IID_BufferVk, TBufferBase ) @@ -323,7 +323,7 @@ void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapF else if (m_Desc.Usage == USAGE_DYNAMIC) { VERIFY(MapFlags & MAP_FLAG_DISCARD, "Vk buffer must be mapped for writing with MAP_FLAG_DISCARD flag"); - pMappedData = pDeviceContextVk->AllocateDynamicUploadSpace(this, m_Desc.uiSizeInBytes, 0); + pMappedData = pDeviceContextVk->AllocateUploadSpace(this, m_Desc.uiSizeInBytes); } else { diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 631f5542..252142e1 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -912,17 +912,17 @@ namespace Diligent } } - void DeviceContextVkImpl::UpdateBufferRegion(BufferVkImpl *pBuffVk, VulkanDynamicAllocation& Allocation, Uint64 DstOffset, Uint64 NumBytes) + void DeviceContextVkImpl::UpdateBufferRegion(BufferVkImpl *pBuffVk, VulkanUtilities::VulkanUploadAllocation& Allocation, Uint64 DstOffset, Uint64 NumBytes) { VERIFY(DstOffset + NumBytes <= pBuffVk->GetDesc().uiSizeInBytes, "Update region is out of buffer"); - VERIFY_EXPR(NumBytes <= Allocation.Size); + VERIFY_EXPR(NumBytes <= Allocation.MemAllocation.Size); EnsureVkCmdBuffer(); if(!pBuffVk->CheckAccessFlags(VK_ACCESS_TRANSFER_WRITE_BIT)) { BufferMemoryBarrier(*pBuffVk, VK_ACCESS_TRANSFER_WRITE_BIT); } VkBufferCopy CopyRegion; - CopyRegion.srcOffset = Allocation.Offset; + CopyRegion.srcOffset = Allocation.MemAllocation.UnalignedOffset; CopyRegion.dstOffset = DstOffset; CopyRegion.size = NumBytes; m_CommandBuffer.CopyBuffer(Allocation.vkBuffer, pBuffVk->GetVkBuffer(), 1, &CopyRegion); @@ -933,9 +933,12 @@ namespace Diligent { VERIFY(pBuffVk->GetDesc().Usage != USAGE_DYNAMIC, "Dynamic buffers must be updated via Map()"); VERIFY_EXPR( static_cast<size_t>(NumBytes) == NumBytes ); - auto TmpSpace = m_pDevice.RawPtr<RenderDeviceVkImpl>()->AllocateDynamicUploadSpace(m_ContextId, NumBytes, 0); - memcpy(TmpSpace.CPUAddress, pData, static_cast<size_t>(NumBytes)); + auto *pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>(); + auto TmpSpace = pDeviceVkImpl->AllocateUploadSpace(m_ContextId, NumBytes); + auto CPUAddress = TmpSpace.MemAllocation.Page->GetCPUMemory(); + memcpy(reinterpret_cast<Uint8*>(CPUAddress) + TmpSpace.MemAllocation.UnalignedOffset, pData, static_cast<size_t>(NumBytes)); UpdateBufferRegion(pBuffVk, TmpSpace, DstOffset, NumBytes); + pDeviceVkImpl->SafeReleaseVkObject(std::move(TmpSpace)); } void DeviceContextVkImpl::CopyBufferRegion(BufferVkImpl *pSrcBuffVk, BufferVkImpl *pDstBuffVk, Uint64 SrcOffset, Uint64 DstOffset, Uint64 NumBytes) @@ -1151,11 +1154,11 @@ namespace Diligent BufferVk.SetAccessFlags(NewAccessFlags); } - void* DeviceContextVkImpl::AllocateDynamicUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes, size_t Alignment) + void* DeviceContextVkImpl::AllocateUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes) { VERIFY(m_UploadAllocations.find(pBuffer) == m_UploadAllocations.end(), "Upload space has already been allocated for this buffer"); - auto UploadAllocation = m_pDevice.RawPtr<RenderDeviceVkImpl>()->AllocateDynamicUploadSpace(m_ContextId, NumBytes, Alignment); - auto *CPUAddress = UploadAllocation.CPUAddress; + auto UploadAllocation = m_pDevice.RawPtr<RenderDeviceVkImpl>()->AllocateUploadSpace(m_ContextId, NumBytes); + auto *CPUAddress = reinterpret_cast<Uint8*>(UploadAllocation.MemAllocation.Page->GetCPUMemory()) + UploadAllocation.MemAllocation.UnalignedOffset; m_UploadAllocations.emplace(pBuffer, std::move(UploadAllocation)); return CPUAddress; } @@ -1167,10 +1170,11 @@ namespace Diligent { #ifdef _DEBUG - auto CurrentFrame = m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetCurrentFrameNumber(); - VERIFY(it->second.FrameNum == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer \"", pBuffer->GetDesc().Name, "\" must be unmapped in the same frame it is used."); + //auto CurrentFrame = m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetCurrentFrameNumber(); + //VERIFY(it->second.FrameNum == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer \"", pBuffer->GetDesc().Name, "\" must be unmapped in the same frame it is used."); #endif UpdateBufferRegion(pBuffer, it->second, 0, pBuffer->GetDesc().uiSizeInBytes); + m_pDevice.RawPtr<RenderDeviceVkImpl>()->SafeReleaseVkObject(std::move(it->second)); m_UploadAllocations.erase(it); } else diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index f69b41dc..2433d657 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -22,6 +22,7 @@ */ #include "pch.h" +#include <sstream> #include "RenderDeviceVkImpl.h" #include "PipelineStateVkImpl.h" #include "ShaderVkImpl.h" @@ -58,7 +59,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters, m_VkObjReleaseQueue(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, GetRawAllocator(), "Allocator for queue<ReleaseQueueElemType>")), m_StaleVkObjects(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, GetRawAllocator(), "Allocator for queue<ReleaseQueueElemType>")), m_DescriptorPools(STD_ALLOCATOR_RAW_MEM(DescriptorPoolManager, GetRawAllocator(), "Allocator for vector<DescriptorPoolManager>")), - m_UploadHeaps(STD_ALLOCATOR_RAW_MEM(UploadHeapPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<VulkanDynamicHeap>>")), + m_UploadHeaps(STD_ALLOCATOR_RAW_MEM(VulkanUtilities::VulkanUploadHeap, GetRawAllocator(), "Allocator for vector<VulkanUploadHeap>")), m_FramebufferCache(*this), 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) @@ -112,11 +113,15 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters, ); { - auto InitialSize = ctx == 0 ? CreationAttribs.ImmediateCtxDynamicHeapInitialSize : CreationAttribs.DeferredCtxDynamicHeapInitialSize; - auto &UploadHeapAllocator = GetRawAllocator(); - auto *pRawMem = ALLOCATE(UploadHeapAllocator, "DynamicUploadHeap instance", sizeof(VulkanDynamicHeap)); - auto *pNewHeap = new (pRawMem) VulkanDynamicHeap(GetRawAllocator(), this, InitialSize); - m_UploadHeaps.emplace_back( pNewHeap, STDDeleterRawMem<VulkanDynamicHeap>(UploadHeapAllocator) ); + auto PageSize = ctx == 0 ? CreationAttribs.ImmediateCtxUploadHeapPageSize : CreationAttribs.DeferredCtxUploadHeapPageSize; + auto ReserveSize = ctx == 0 ? CreationAttribs.ImmediateCtxUploadHeapReserveSize : CreationAttribs.DeferredCtxUploadHeapReserveSize; + std::stringstream ss; + if(ctx == 0) + ss << "Immediate context"; + else + ss << "Deferred context " << ctx-1; + ss << " upload heap"; + m_UploadHeaps.emplace_back( ss.str(), *m_LogicalVkDevice, *m_PhysicalDevice, RawMemAllocator, PageSize, ReserveSize ); } } } @@ -208,7 +213,7 @@ void RenderDeviceVkImpl::ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, bo // | was added to the delete queue | | // | with number N | | - // Move stale objects into a release queue. + // Move stale objects into the release queue. // Note that objects are moved from stale list to release queue based on the // cmd list number, not the fence value. This makes sure that basic requirement // is met even when the fence value is not incremented while executing @@ -322,8 +327,7 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) // time for every context //std::lock_guard<std::mutex> LockGuard(m_UploadHeapMutex); - // Upload heaps are used to update resource contents as well as to allocate - // space for dynamic resources. + // Upload heaps are used to update resource contents // Initial resource data is uploaded using temporary one-time upload buffers, // so can be performed in parallel across frame boundaries for (auto &UploadHeap : m_UploadHeaps) @@ -342,7 +346,7 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) // // - UploadHeap->FinishFrame(NextFenceValue, CompletedFenceValue); + UploadHeap.ShrinkMemory(); } } @@ -399,66 +403,53 @@ void RenderDeviceVkImpl::DisposeTransientCmdPool(VulkanUtilities::CommandPoolWra -template<typename VulkanObjectType> -void RenderDeviceVkImpl::SafeReleaseVkObject(VulkanUtilities::VulkanObjectWrapper<VulkanObjectType>&& vkObject) +template<typename ObjectType> +void RenderDeviceVkImpl::SafeReleaseVkObject(ObjectType&& vkObject) { class StaleVulkanObject : public RenderDeviceVkImpl::StaleVulkanObjectBase { public: - StaleVulkanObject(VulkanUtilities::VulkanObjectWrapper<VulkanObjectType>&& Object) : + StaleVulkanObject(ObjectType&& Object) : m_VkObject(std::move(Object)) {} - ~StaleVulkanObject() - { - m_VkObject.Release(); - } + StaleVulkanObject (const StaleVulkanObject&) = delete; + StaleVulkanObject (StaleVulkanObject&&) = delete; + StaleVulkanObject& operator = (const StaleVulkanObject&) = delete; + StaleVulkanObject& operator = (StaleVulkanObject&&) = delete; private: - VulkanUtilities::VulkanObjectWrapper<VulkanObjectType> m_VkObject; + ObjectType m_VkObject; }; // When Vk object is released, it is first moved into the // stale objects list. The list is moved into a release queue // after the next command list is executed. std::lock_guard<std::mutex> LockGuard(m_StaleObjectsMutex); - m_StaleVkObjects.emplace_back(m_NextCmdListNumber, new StaleVulkanObject(std::move(vkObject)) ); -} - -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkBuffer> (VulkanUtilities::BufferWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkBufferView> (VulkanUtilities::BufferViewWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkImage> (VulkanUtilities::ImageWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkImageView> (VulkanUtilities::ImageViewWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkSampler> (VulkanUtilities::SamplerWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkDeviceMemory> (VulkanUtilities::DeviceMemoryWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkRenderPass> (VulkanUtilities::RenderPassWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkPipeline> (VulkanUtilities::PipelineWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkShaderModule> (VulkanUtilities::ShaderModuleWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkPipelineLayout>(VulkanUtilities::PipelineLayoutWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkFramebuffer> (VulkanUtilities::FramebufferWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkDescriptorPool> (VulkanUtilities::DescriptorPoolWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkDescriptorSetLayout>(VulkanUtilities::DescriptorSetLayoutWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkSemaphore> (VulkanUtilities::SemaphoreWrapper &&Object); -template void RenderDeviceVkImpl::SafeReleaseVkObject<VkCommandPool> (VulkanUtilities::CommandPoolWrapper &&Object); - - -void RenderDeviceVkImpl::SafeReleaseMemoryAllocation(VulkanUtilities::VulkanMemoryAllocation&& Allocation) -{ - class StaleVulkanMemoryAllocation : public StaleVulkanObjectBase - { - public: - StaleVulkanMemoryAllocation(VulkanUtilities::VulkanMemoryAllocation &&Allocation) : - m_Allocation(std::move(Allocation)) - {} - - private: - VulkanUtilities::VulkanMemoryAllocation m_Allocation; - }; - - std::lock_guard<std::mutex> LockGuard(m_StaleObjectsMutex); - m_StaleVkObjects.emplace_back(m_NextCmdListNumber, new StaleVulkanMemoryAllocation(std::move(Allocation)) ); + m_StaleVkObjects.emplace_back(m_NextCmdListNumber, new StaleVulkanObject{std::move(vkObject)} ); } +#define INSTANTIATE_SAFE_RELEASE_VK_OBJECT(Type) template void RenderDeviceVkImpl::SafeReleaseVkObject<Type>(Type &&Object) + +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::BufferWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::BufferViewWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::ImageWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::ImageViewWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::SamplerWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::DeviceMemoryWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::RenderPassWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::PipelineWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::ShaderModuleWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::PipelineLayoutWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::FramebufferWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::DescriptorPoolWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::DescriptorSetLayoutWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::SemaphoreWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::CommandPoolWrapper); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::VulkanMemoryAllocation); +INSTANTIATE_SAFE_RELEASE_VK_OBJECT(VulkanUtilities::VulkanUploadAllocation); + +#undef INSTANTIATE_SAFE_RELEASE_VK_OBJECT void RenderDeviceVkImpl::DiscardStaleVkObjects(Uint64 CmdListNumber, Uint64 FenceValue) { diff --git a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp index 255f08fd..c43dc5c9 100644 --- a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp @@ -394,7 +394,7 @@ TextureVkImpl :: TextureVkImpl(IReferenceCounters *pRefCounters, // until copy operation is complete. This must be done after // submitting command list for execution! pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBuffer)); - pRenderDeviceVk->SafeReleaseMemoryAllocation(std::move(StagingMemoryAllocation)); + pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingMemoryAllocation)); } #if 0 @@ -495,7 +495,7 @@ TextureVkImpl :: ~TextureVkImpl() // Vk object can only be destroyed when it is no longer used by the GPU // Wrappers for external texture will not be destroyed as they are created with null device pointer pDeviceVkImpl->SafeReleaseVkObject(std::move(m_VulkanImage)); - pDeviceVkImpl->SafeReleaseMemoryAllocation(std::move(m_MemoryAllocation)); + pDeviceVkImpl->SafeReleaseVkObject(std::move(m_MemoryAllocation)); } void TextureVkImpl::UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint32 Slice, const Box &DstBox, const TextureSubResData &SubresData ) diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp index f3e8e315..8739852f 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp @@ -40,7 +40,7 @@ VulkanMemoryAllocation::~VulkanMemoryAllocation() VulkanMemoryPage::VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, VkDeviceSize PageSize, uint32_t MemoryTypeIndex, - bool MapMemory)noexcept : + bool IsHostVisible)noexcept : m_ParentMemoryMgr(ParentMemoryMgr), m_AllocationMgr(PageSize, ParentMemoryMgr.m_Allocator) { @@ -54,7 +54,7 @@ VulkanMemoryPage::VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, ss << "Device memory page. Size: " << (PageSize >> 10) << " Kb, type: " << MemoryTypeIndex; m_VkMemory = ParentMemoryMgr.m_LogicalDevice.AllocateDeviceMemory(MemAlloc, ss.str().c_str()); - if(MapMemory) + if(IsHostVisible) { auto err = ParentMemoryMgr.m_LogicalDevice.MapMemory(m_VkMemory, 0, // offset @@ -125,10 +125,16 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& LOG_ERROR_AND_THROW("Failed to find suitable device memory type for a buffer"); } - auto Size = MemReqs.size + MemReqs.alignment; + bool HostVisible = (MemoryProps & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + return Allocate(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, HostVisible); +} + +VulkanMemoryAllocation VulkanMemoryManager::Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible) +{ + Size += Alignment; VulkanMemoryAllocation Allocation; - std::lock_guard<std::mutex> Lock(m_Mutex); + std::lock_guard<std::mutex> Lock(m_PagesMtx); auto range = m_Pages.equal_range(MemoryTypeIndex); for(auto page_it = range.first; page_it != range.second; ++page_it) { @@ -137,7 +143,6 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& break; } - bool HostVisible = (MemoryProps & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; size_t stat_ind = HostVisible ? 1 : 0; if(Allocation.Page == nullptr) { @@ -152,6 +157,7 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& 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"); + OnNewPageCreated(it->second); Allocation = it->second.Allocate(Size); VERIFY(Allocation.Page != nullptr, "Failed to allocate new memory page"); } @@ -164,7 +170,7 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& void VulkanMemoryManager::ShrinkMemory() { - std::lock_guard<std::mutex> Lock(m_Mutex); + std::lock_guard<std::mutex> Lock(m_PagesMtx); if(m_CurrAllocatedSize[0] <= m_DeviceLocalReserveSize && m_CurrAllocatedSize[1] <= m_HostVisibleReserveSize) return; @@ -183,6 +189,7 @@ void VulkanMemoryManager::ShrinkMemory() 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"); + OnPageDestroy(Page); m_Pages.erase(curr_it); } } diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanUploadHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanUploadHeap.cpp new file mode 100644 index 00000000..57b4075d --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanUploadHeap.cpp @@ -0,0 +1,103 @@ +/* Copyright 2015-2018 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "VulkanUtilities/VulkanUploadHeap.h" + +namespace VulkanUtilities +{ + +VkBufferCreateInfo VulkanUploadHeap::GetStagingBufferCI()const +{ + VkBufferCreateInfo StagingBufferCI = {}; + StagingBufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + StagingBufferCI.pNext = nullptr; + StagingBufferCI.flags = 0; // VK_BUFFER_CREATE_SPARSE_BINDING_BIT, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT + StagingBufferCI.size = m_HostVisiblePageSize; + StagingBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + StagingBufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + StagingBufferCI.queueFamilyIndexCount = 0; + StagingBufferCI.pQueueFamilyIndices = nullptr; + return StagingBufferCI; +} + +VulkanUploadHeap::VulkanUploadHeap(std::string MgrName, + const VulkanLogicalDevice& LogicalDevice, + const VulkanPhysicalDevice& PhysicalDevice, + Diligent::IMemoryAllocator& Allocator, + VkDeviceSize HostVisiblePageSize, + VkDeviceSize HostVisibleReserveSize) : + VulkanMemoryManager(MgrName, LogicalDevice, PhysicalDevice, Allocator, 0, HostVisiblePageSize, 0, HostVisibleReserveSize) +{ + auto StagingBufferCI = GetStagingBufferCI(); + auto TmpStagingBuffer = LogicalDevice.CreateBuffer(StagingBufferCI); + + VkMemoryRequirements StagingBufferMemReqs = LogicalDevice.GetBufferMemoryRequirements(TmpStagingBuffer); + m_StagingBufferMemoryTypeIndex = m_PhysicalDevice.GetMemoryTypeIndex(StagingBufferMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + VERIFY(m_StagingBufferMemoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, + "Vulkan spec requires that for a VkBuffer not created with the VK_BUFFER_CREATE_SPARSE_BINDING_BIT " + "bit set, or for a VkImage that was created with a VK_IMAGE_TILING_LINEAR value in the tiling member " + "of the VkImageCreateInfo structure passed to vkCreateImage, the memoryTypeBits member always contains " + "at least one bit set corresponding to a VkMemoryType with a propertyFlags that has both the " + "VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit AND the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set. (11.6)"); +} + +VulkanUploadHeap::~VulkanUploadHeap() +{ + VERIFY_EXPR(m_Pages.size() == m_Buffers.size()); +} + +VulkanUploadAllocation VulkanUploadHeap::Allocate(size_t SizeInBytes) +{ + VulkanUploadAllocation Allocation; + Allocation.MemAllocation = VulkanMemoryManager::Allocate(SizeInBytes, 0, m_StagingBufferMemoryTypeIndex, true); + + std::lock_guard<std::mutex> Lock(m_BuffersMtx); + auto BuffIt = m_Buffers.find(Allocation.MemAllocation.Page); + VERIFY_EXPR(BuffIt != m_Buffers.end()); + Allocation.vkBuffer = BuffIt->second; + + return Allocation; +} + +void VulkanUploadHeap::OnNewPageCreated(VulkanMemoryPage& NewPage) +{ + std::lock_guard<std::mutex> Lock(m_BuffersMtx); + auto BufferCI = GetStagingBufferCI(); + auto NewBuffer = m_LogicalDevice.CreateBuffer(BufferCI, "Upload buffer"); + auto MemReqs = m_LogicalDevice.GetBufferMemoryRequirements(NewBuffer); + auto MemoryTypeIndex = m_PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + VERIFY(MemoryTypeIndex == m_StagingBufferMemoryTypeIndex, "Incosistent memory type"); + auto err = m_LogicalDevice.BindBufferMemory(NewBuffer, NewPage.GetVkMemory(), 0); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind buffer memory"); + m_Buffers.emplace(&NewPage, std::move(NewBuffer)); +} + +void VulkanUploadHeap::OnPageDestroy(VulkanMemoryPage& Page) +{ + std::lock_guard<std::mutex> Lock(m_BuffersMtx); + auto ElemsRemoved = m_Buffers.erase(&Page); + VERIFY_EXPR(ElemsRemoved == 1); +} + +} |
