summaryrefslogtreecommitdiffstats
path: root/Graphics
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-06-06 15:47:36 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-06-06 15:47:36 +0000
commitfe135f2472e4a598976860f3f40f85b898fd5cc0 (patch)
tree03a76d6c1a07f7485350a15ee63e2818de20d9ad /Graphics
parentImprovements to resource liftime management in Vulkan (diff)
downloadDiligentCore-fe135f2472e4a598976860f3f40f85b898fd5cc0.tar.gz
DiligentCore-fe135f2472e4a598976860f3f40f85b898fd5cc0.zip
Added ResourceReleaseQueue class
Diffstat (limited to 'Graphics')
-rw-r--r--Graphics/GraphicsAccessories/CMakeLists.txt3
-rw-r--r--Graphics/GraphicsAccessories/interface/ResourceReleaseQueue.h193
-rw-r--r--Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h22
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp96
4 files changed, 208 insertions, 106 deletions
diff --git a/Graphics/GraphicsAccessories/CMakeLists.txt b/Graphics/GraphicsAccessories/CMakeLists.txt
index 8bf7d501..01101526 100644
--- a/Graphics/GraphicsAccessories/CMakeLists.txt
+++ b/Graphics/GraphicsAccessories/CMakeLists.txt
@@ -3,10 +3,11 @@ cmake_minimum_required (VERSION 3.6)
project(GraphicsAccessories CXX)
set(INTERFACE
+ interface/GraphicsAccessories.h
+ interface/ResourceReleaseQueue.h
interface/RingBuffer.h
interface/VariableSizeAllocationsManager.h
interface/VariableSizeGPUAllocationsManager.h
- interface/GraphicsAccessories.h
)
set(SOURCE
diff --git a/Graphics/GraphicsAccessories/interface/ResourceReleaseQueue.h b/Graphics/GraphicsAccessories/interface/ResourceReleaseQueue.h
new file mode 100644
index 00000000..7f8aa22b
--- /dev/null
+++ b/Graphics/GraphicsAccessories/interface/ResourceReleaseQueue.h
@@ -0,0 +1,193 @@
+/* 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.
+ */
+
+// Helper class that handles free memory block management to accommodate variable-size allocation requests
+// See http://diligentgraphics.com/diligent-engine/architecture/d3d12/variable-size-memory-allocations-manager/
+
+#pragma once
+
+/// \file
+/// Implementation of Diligent::ResourceReleaseQueue class
+
+#include <mutex>
+#include <deque>
+#include <memory>
+
+#include "MemoryAllocator.h"
+#include "STDAllocator.h"
+#include "DebugUtilities.h"
+
+namespace Diligent
+{
+
+/// Helper class that wraps stale resources of different types
+class DynamicStaleResourceWrapper
+{
+public:
+ // ___________________________ ___________________________
+ // |DynamicStaleResourceWrapper| |DynamicStaleResourceWrapper|
+ // | | | |
+ // | m_pStaleResource | | m_pStaleResource |
+ // |__________|________________| |__________|________________|
+ // | |
+ // | |
+ // | |
+ // __________V___________________________________ __________V___________________________________
+ // |SpecificStaleResource<VulkanBufferWrapper> | |SpecificStaleResource<VulkanMemoryAllocation> |
+ // | | | |
+ // | VulkanBufferWrapper m_SpecificResource; | | VulkanMemoryAllocation m_SpecificResource; |
+ // |______________________________________________| |______________________________________________|
+ //
+
+ template<typename ResourceType>
+ static DynamicStaleResourceWrapper Create(ResourceType&& Resource)
+ {
+ class SpecificStaleResource : public StaleResourceBase
+ {
+ public:
+ SpecificStaleResource(ResourceType&& SpecificResource) :
+ m_SpecificResource(std::move(SpecificResource))
+ {}
+
+ SpecificStaleResource (const SpecificStaleResource&) = delete;
+ SpecificStaleResource (SpecificStaleResource&&) = delete;
+ SpecificStaleResource& operator = (const SpecificStaleResource&) = delete;
+ SpecificStaleResource& operator = (SpecificStaleResource&&) = delete;
+
+ private:
+ ResourceType m_SpecificResource;
+ };
+ return DynamicStaleResourceWrapper{new SpecificStaleResource{std::move(Resource)}};
+ }
+
+ DynamicStaleResourceWrapper(DynamicStaleResourceWrapper&& rhs)noexcept :
+ m_pStaleResource(std::move(rhs.m_pStaleResource))
+ {}
+
+ DynamicStaleResourceWrapper& operator = (DynamicStaleResourceWrapper&& rhs)noexcept
+ {
+ m_pStaleResource = std::move(rhs.m_pStaleResource);
+ }
+
+ DynamicStaleResourceWrapper (const DynamicStaleResourceWrapper&) = delete;
+ DynamicStaleResourceWrapper& operator = (const DynamicStaleResourceWrapper&) = delete;
+
+private:
+ class StaleResourceBase
+ {
+ public:
+ virtual ~StaleResourceBase() = 0 {}
+ };
+
+ DynamicStaleResourceWrapper(StaleResourceBase *pStaleResource) :
+ m_pStaleResource(pStaleResource)
+ {}
+
+ std::unique_ptr<StaleResourceBase> m_pStaleResource;
+};
+
+/// Facilitates safe resource destruction in D3D12 and Vulkan
+
+/// Resource destruction is a two-stage process:
+/// * When resource is released, it is moved into the stale objects queue along with the next command list number
+/// * When command list is submitted to the command queue, all stale objects associated with this
+/// and earlier command lists are moved to the release queue, along with the fence value associated with
+/// the command list
+/// * Resources are removed and actually destroyed from the queue when fence is signaled and the queue is Purged
+///
+/// \tparam ResourceWrapperType - Type of the resource wrapper used by the release queue.
+template<typename ResourceWrapperType = DynamicStaleResourceWrapper>
+class ResourceReleaseQueue
+{
+public:
+ ResourceReleaseQueue(IMemoryAllocator& Allocator) :
+ m_ReleaseQueue(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, Allocator, "Allocator for deque<ReleaseQueueElemType>")),
+ m_StaleResources(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, Allocator, "Allocator for deque<ReleaseQueueElemType>"))
+ {}
+
+ ~ResourceReleaseQueue()
+ {
+ VERIFY(m_StaleResources.empty(), "Not all stale objects were destroyed");
+ VERIFY(m_ReleaseQueue.empty(), "Release queue is not empty");
+ }
+
+ /// Moves resource to the release queue
+ /// \param [in] Resource - Resource to be released
+ /// \param [in] NextCommandListNumber - Number of the command list that will be submitted to the queue next
+ template<typename ResourceType>
+ void SafeReleaseResource(ResourceType&& Resource, Uint64 NextCommandListNumber)
+ {
+ std::lock_guard<std::mutex> LockGuard(m_StaleObjectsMutex);
+ m_StaleResources.emplace_back(NextCommandListNumber, ResourceWrapperType::Create(std::move(Resource)) );
+ }
+
+
+ void DiscardStaleResources(Uint64 CmdBuffNumber, Uint64 FenceValue)
+ {
+ // Only discard these stale objects that were released before CmdBuffNumber
+ // was executed
+ std::lock_guard<std::mutex> StaleObjectsLock(m_StaleObjectsMutex);
+ std::lock_guard<std::mutex> ReleaseQueueLock(m_ReleaseQueueMutex);
+ while (!m_StaleResources.empty() )
+ {
+ auto &FirstStaleObj = m_StaleResources.front();
+ if (FirstStaleObj.first <= CmdBuffNumber)
+ {
+ m_ReleaseQueue.emplace_back(FenceValue, std::move(FirstStaleObj.second));
+ m_StaleResources.pop_front();
+ }
+ else
+ break;
+ }
+ }
+
+
+ /// Removes all objects from the release queue whose fence value is
+ /// less than or equal to CompletedFenceValue
+ /// \param [in] CompletedFenceValue - Value of the fence that has been completed by the GPU
+ void Purge(Uint64 CompletedFenceValue)
+ {
+ std::lock_guard<std::mutex> LockGuard(m_ReleaseQueueMutex);
+
+ // Release all objects whose associated fence value is at most CompletedFenceValue
+ while (!m_ReleaseQueue.empty())
+ {
+ auto &FirstObj = m_ReleaseQueue.front();
+ if (FirstObj.first <= CompletedFenceValue)
+ m_ReleaseQueue.pop_front();
+ else
+ break;
+ }
+ }
+
+private:
+
+ std::mutex m_ReleaseQueueMutex;
+ using ReleaseQueueElemType = std::pair<Uint64, ResourceWrapperType>;
+ std::deque< ReleaseQueueElemType, STDAllocatorRawMem<ReleaseQueueElemType> > m_ReleaseQueue;
+
+ std::mutex m_StaleObjectsMutex;
+ std::deque< ReleaseQueueElemType, STDAllocatorRawMem<ReleaseQueueElemType> > m_StaleResources;
+};
+
+}
diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
index bda52b94..8998b187 100644
--- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
@@ -43,6 +43,7 @@
#include "VulkanUtilities/VulkanUploadHeap.h"
#include "FramebufferCache.h"
#include "CommandPoolManager.h"
+#include "ResourceReleaseQueue.h"
/// Namespace for the Direct3D11 implementation of the graphics engine
namespace Diligent
@@ -103,7 +104,10 @@ public:
void ExecuteAndDisposeTransientCmdBuff(VkCommandBuffer vkCmdBuff, VulkanUtilities::CommandPoolWrapper&& CmdPool);
template<typename ObjectType>
- void SafeReleaseVkObject(ObjectType&& Object);
+ void SafeReleaseVkObject(ObjectType&& Object)
+ {
+ m_ReleaseQueue.SafeReleaseResource(std::move(Object), m_NextCmdBuffNumber);
+ }
void FinishFrame(bool ReleaseAllResources);
virtual void FinishFrame()override final { FinishFrame(false); }
@@ -137,7 +141,6 @@ public:
private:
virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat )override final;
void ProcessReleaseQueue(Uint64 CompletedFenceValue);
- void DiscardStaleVkObjects(Uint64 CmdBuffNumber, Uint64 FenceValue);
// Submits command buffer for execution to the command queue
// Returns the submitted command buffer number and the fence value that has been set to signal by GPU
@@ -185,20 +188,6 @@ private:
// Resource X can
// be released
-
- std::mutex m_ReleaseQueueMutex;
-
- class StaleVulkanObjectBase
- {
- public:
- virtual ~StaleVulkanObjectBase() = 0 {}
- };
-
- using ReleaseQueueElemType = std::pair<Uint64, std::unique_ptr<StaleVulkanObjectBase> >;
- std::deque< ReleaseQueueElemType, STDAllocatorRawMem<ReleaseQueueElemType> > m_VkObjReleaseQueue;
-
- std::mutex m_StaleObjectsMutex;
- std::deque< ReleaseQueueElemType, STDAllocatorRawMem<ReleaseQueueElemType> > m_StaleVkObjects;
FramebufferCache m_FramebufferCache;
// [0] - Main descriptor pool
@@ -214,6 +203,7 @@ private:
CommandPoolManager m_TransientCmdPoolMgr;
VulkanUtilities::VulkanMemoryManager m_MemoryMgr;
+ ResourceReleaseQueue<> m_ReleaseQueue;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
index e46254ec..bde9dc2e 100644
--- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
@@ -32,8 +32,8 @@
#include "BufferVkImpl.h"
#include "ShaderResourceBindingVkImpl.h"
#include "DeviceContextVkImpl.h"
-
#include "EngineMemory.h"
+
namespace Diligent
{
@@ -56,13 +56,12 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters,
/*m_CmdListManager(this),
m_ContextPool(STD_ALLOCATOR_RAW_MEM(ContextPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<CommandContext>>")),
m_AvailableContexts(STD_ALLOCATOR_RAW_MEM(CommandContext*, GetRawAllocator(), "Allocator for vector<CommandContext*>")),*/
- 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(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)
+ m_MemoryMgr("Global resource memory manager", *m_LogicalVkDevice, *m_PhysicalDevice, GetRawAllocator(), CreationAttribs.DeviceLocalMemoryPageSize, CreationAttribs.HostVisibleMemoryPageSize, CreationAttribs.DeviceLocalMemoryReserveSize, CreationAttribs.HostVisibleMemoryReserveSize),
+ m_ReleaseQueue(GetRawAllocator())
{
m_DeviceCaps.DevType = DeviceType::Vulkan;
m_DeviceCaps.MajorVersion = 1;
@@ -142,9 +141,6 @@ RenderDeviceVkImpl::~RenderDeviceVkImpl()
m_ContextPool.clear();
#endif
- VERIFY(m_StaleVkObjects.empty(), "Not all stale objects were destroyed");
- VERIFY(m_VkObjReleaseQueue.empty(), "Release queue is not empty");
-
m_TransientCmdPoolMgr.DestroyPools(m_pCommandQueue->GetCompletedFenceValue());
//if(m_PhysicalDevice)
@@ -265,7 +261,7 @@ void RenderDeviceVkImpl::ExecuteCommandBuffer(const VkSubmitInfo &SubmitInfo, De
// As long as resources used by deferred contexts are not released until the command list
// is executed through immediate context, this stategy always works.
- DiscardStaleVkObjects(SubmittedCmdBuffNumber, SubmittedFenceValue);
+ m_ReleaseQueue.DiscardStaleResources(SubmittedCmdBuffNumber, SubmittedFenceValue);
auto CompletedFenceValue = GetCompletedFenceValue();
ProcessReleaseQueue(CompletedFenceValue);
m_MemoryMgr.ShrinkMemory();
@@ -310,7 +306,7 @@ void RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects)
// This is necessary to release outstanding references to the
// swap chain buffers when it is resized in the middle of the frame.
// Since GPU has been idled, it it is safe to do so
- DiscardStaleVkObjects(CmdBuffNumber, FenceValue);
+ m_ReleaseQueue.DiscardStaleResources(CmdBuffNumber, FenceValue);
// FenceValue has now been signaled by the GPU since we waited for it
auto CompletedFenceValue = FenceValue;
ProcessReleaseQueue(CompletedFenceValue);
@@ -408,95 +404,17 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
// Discard all remaining objects. This is important to do if there were
// no command lists submitted during the frame
- DiscardStaleVkObjects(CmdBuffNumber, NextFenceValue);
+ m_ReleaseQueue.DiscardStaleResources(CmdBuffNumber, NextFenceValue);
ProcessReleaseQueue(CompletedFenceValue);
m_MemoryMgr.ShrinkMemory();
Atomics::AtomicIncrement(m_FrameNumber);
}
-template<typename ObjectType>
-void RenderDeviceVkImpl::SafeReleaseVkObject(ObjectType&& vkObject)
-{
- class StaleVulkanObject : public RenderDeviceVkImpl::StaleVulkanObjectBase
- {
- public:
- StaleVulkanObject(ObjectType&& Object) :
- m_VkObject(std::move(Object))
- {}
-
- StaleVulkanObject (const StaleVulkanObject&) = delete;
- StaleVulkanObject (StaleVulkanObject&&) = delete;
- StaleVulkanObject& operator = (const StaleVulkanObject&) = delete;
- StaleVulkanObject& operator = (StaleVulkanObject&&) = delete;
-
- private:
- 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_NextCmdBuffNumber, 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 CmdBuffNumber, Uint64 FenceValue)
-{
- // Only discard these stale objects that were released before CmdBuffNumber
- // was executed
- std::lock_guard<std::mutex> StaleObjectsLock(m_StaleObjectsMutex);
- std::lock_guard<std::mutex> ReleaseQueueLock(m_ReleaseQueueMutex);
- while (!m_StaleVkObjects.empty() )
- {
- auto &FirstStaleObj = m_StaleVkObjects.front();
- if (FirstStaleObj.first <= CmdBuffNumber)
- {
- m_VkObjReleaseQueue.emplace_back(FenceValue, std::move(FirstStaleObj.second));
- m_StaleVkObjects.pop_front();
- }
- else
- break;
- }
-}
void RenderDeviceVkImpl::ProcessReleaseQueue(Uint64 CompletedFenceValue)
{
- {
- std::lock_guard<std::mutex> LockGuard(m_ReleaseQueueMutex);
-
- // Release all objects whose associated fence value is at most CompletedFenceValue
- while (!m_VkObjReleaseQueue.empty())
- {
- auto &FirstObj = m_VkObjReleaseQueue.front();
- if (FirstObj.first <= CompletedFenceValue)
- m_VkObjReleaseQueue.pop_front();
- else
- break;
- }
- }
+ m_ReleaseQueue.Purge(CompletedFenceValue);
{
// This is OK if other thread disposes descriptor heap allocation at this time