Diligent Engine  v.2.4.g
RenderDeviceNextGenBase.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2021 Diligent Graphics LLC
3  * Copyright 2015-2019 Egor Yusov
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * In no event and under no legal theory, whether in tort (including negligence),
18  * contract, or otherwise, unless required by applicable law (such as deliberate
19  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
20  * liable for any damages, including any direct, indirect, special, incidental,
21  * or consequential damages of any character arising as a result of this License or
22  * out of the use or inability to use the software (including but not limited to damages
23  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
24  * all other commercial damages or losses), even if such Contributor has been advised
25  * of the possibility of such damages.
26  */
27 
28 #pragma once
29 
30 #include <vector>
31 #include <mutex>
32 #include <atomic>
33 
34 #include "EngineFactory.h"
35 #include "BasicTypes.h"
36 #include "ReferenceCounters.h"
37 #include "MemoryAllocator.h"
38 #include "RefCntAutoPtr.hpp"
39 #include "PlatformMisc.hpp"
40 #include "ResourceReleaseQueue.hpp"
41 #include "EngineMemory.h"
42 
43 namespace Diligent
44 {
45 
47 
48 template <class TBase, typename CommandQueueType>
49 class RenderDeviceNextGenBase : public TBase
50 {
51 public:
53  IMemoryAllocator& RawMemAllocator,
54  IEngineFactory* pEngineFactory,
55  size_t CmdQueueCount,
56  CommandQueueType** Queues,
57  Uint32 NumDeferredContexts) :
58  TBase{pRefCounters, RawMemAllocator, pEngineFactory, NumDeferredContexts},
59  m_CmdQueueCount{CmdQueueCount}
60  {
61  m_CommandQueues = ALLOCATE(this->m_RawMemAllocator, "Raw memory for the device command/release queues", CommandQueue, m_CmdQueueCount);
62  for (size_t q = 0; q < m_CmdQueueCount; ++q)
63  new (m_CommandQueues + q) CommandQueue(RefCntAutoPtr<CommandQueueType>(Queues[q]), this->m_RawMemAllocator);
64  }
65 
67  {
69  }
70 
71  // The following basic requirement guarantees correctness of resource deallocation:
72  //
73  // A resource is never released before the last draw command referencing it is submitted to the command queue
74  //
75 
76  //
77  // CPU
78  // Last Reference
79  // of resource X
80  // |
81  // | Submit Cmd Submit Cmd Submit Cmd
82  // | List N List N+1 List N+2
83  // V | | |
84  // NextFenceValue | * N | N+1 | N+2 |
85  //
86  //
87  // CompletedFenceValue | N-3 | N-2 | N-1 | N |
88  // . . . . .
89  // -----------------------------.--------------.---------------.-------------------.----------------.-------------
90  // . . . . .
91  //
92  // GPU | Cmd List N-2 | Cmd List N-1 | Cmd List N | Cmd List N+1 |
93  // |
94  // |
95  // Resource X can
96  // be released
97  template <typename ObjectType, typename = typename std::enable_if<std::is_object<ObjectType>::value>::type>
98  void SafeReleaseDeviceObject(ObjectType&& Object, Uint64 QueueMask)
99  {
100  VERIFY(m_CommandQueues != nullptr, "Command queues have been destroyed. Are you releasing an object from the render device destructor?");
101 
102  QueueMask &= GetCommandQueueMask();
103 
104  VERIFY(QueueMask != 0, "At least one bit should be set in the command queue mask");
105  if (QueueMask == 0)
106  return;
107 
108  Atomics::Long NumReferences = PlatformMisc::CountOneBits(QueueMask);
109  auto Wrapper = DynamicStaleResourceWrapper::Create(std::move(Object), NumReferences);
110 
111  while (QueueMask != 0)
112  {
113  auto QueueIndex = PlatformMisc::GetLSB(QueueMask);
114  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
115 
116  auto& Queue = m_CommandQueues[QueueIndex];
117  // Do not use std::move on wrapper!!!
118  Queue.ReleaseQueue.SafeReleaseResource(Wrapper, Queue.NextCmdBufferNumber.load());
119  QueueMask &= ~(Uint64{1} << Uint64{QueueIndex});
120  --NumReferences;
121  }
122  VERIFY_EXPR(NumReferences == 0);
123 
124  Wrapper.GiveUpOwnership();
125  }
126 
127  size_t GetCommandQueueCount() const
128  {
129  return m_CmdQueueCount;
130  }
131 
133  {
134  return (m_CmdQueueCount < 64) ? ((Uint64{1} << Uint64{m_CmdQueueCount}) - 1) : ~Uint64{0};
135  }
136 
137  void PurgeReleaseQueues(bool ForceRelease = false)
138  {
139  for (Uint32 q = 0; q < m_CmdQueueCount; ++q)
140  PurgeReleaseQueue(q, ForceRelease);
141  }
142 
143  void PurgeReleaseQueue(Uint32 QueueIndex, bool ForceRelease = false)
144  {
145  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
146  auto& Queue = m_CommandQueues[QueueIndex];
147  auto CompletedFenceValue = ForceRelease ? std::numeric_limits<Uint64>::max() : Queue.CmdQueue->GetCompletedFenceValue();
148  Queue.ReleaseQueue.Purge(CompletedFenceValue);
149  }
150 
151  void IdleCommandQueue(size_t QueueIdx, bool ReleaseResources)
152  {
153  VERIFY_EXPR(QueueIdx < m_CmdQueueCount);
154  auto& Queue = m_CommandQueues[QueueIdx];
155 
156  Uint64 CmdBufferNumber = 0;
157  Uint64 FenceValue = 0;
158  {
159  std::lock_guard<std::mutex> Lock{Queue.Mtx};
160 
161  if (ReleaseResources)
162  {
163  // Increment the command buffer number before idling the queue.
164  // This will make sure that any resource released while this function
165  // is running will be associated with the next command buffer submission.
166  CmdBufferNumber = Queue.NextCmdBufferNumber.fetch_add(1);
167  // fetch_add returns the original value immediately preceding the addition.
168  }
169 
170  FenceValue = Queue.CmdQueue->WaitForIdle();
171  }
172 
173  if (ReleaseResources)
174  {
175  Queue.ReleaseQueue.DiscardStaleResources(CmdBufferNumber, FenceValue);
176  Queue.ReleaseQueue.Purge(Queue.CmdQueue->GetCompletedFenceValue());
177  }
178  }
179 
180  void IdleAllCommandQueues(bool ReleaseResources)
181  {
182  for (size_t q = 0; q < m_CmdQueueCount; ++q)
183  IdleCommandQueue(q, ReleaseResources);
184  }
185 
187  {
190  };
191  template <typename... SubmitDataType>
192  SubmittedCommandBufferInfo SubmitCommandBuffer(Uint32 QueueIndex, bool DiscardStaleResources, const SubmitDataType&... SubmitData)
193  {
194  SubmittedCommandBufferInfo CmdBuffInfo;
195  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
196  auto& Queue = m_CommandQueues[QueueIndex];
197 
198  {
199  std::lock_guard<std::mutex> Lock{Queue.Mtx};
200 
201  // Increment the command buffer number before submitting the cmd buffer.
202  // This will make sure that any resource released while this function
203  // is running will be associated with the next command buffer.
204  CmdBuffInfo.CmdBufferNumber = Queue.NextCmdBufferNumber.fetch_add(1);
205  // fetch_add returns the original value immediately preceding the addition.
206 
207  CmdBuffInfo.FenceValue = Queue.CmdQueue->Submit(SubmitData...);
208  }
209 
210  if (DiscardStaleResources)
211  {
212  // The following basic requirement guarantees correctness of resource deallocation:
213  //
214  // A resource is never released before the last draw command referencing it is submitted for execution
215  //
216 
217  // Move stale objects into the release queue.
218  // Note that objects are moved from stale list to release queue based on the cmd buffer number,
219  // not fence value. This makes sure that basic requirement is met even when the fence value is
220  // not incremented while executing the command buffer (as is the case with Unity command queue).
221 
222  // As long as resources used by deferred contexts are not released before the command list
223  // is executed through immediate context, this stategy always works.
224  Queue.ReleaseQueue.DiscardStaleResources(CmdBuffInfo.CmdBufferNumber, CmdBuffInfo.FenceValue);
225  }
226 
227  return CmdBuffInfo;
228  }
229 
231  {
232  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
233  return m_CommandQueues[QueueIndex].ReleaseQueue;
234  }
235 
236  const CommandQueueType& DILIGENT_CALL_TYPE GetCommandQueue(Uint32 QueueIndex) const
237  {
238  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
239  return *m_CommandQueues[QueueIndex].CmdQueue;
240  }
241 
242  virtual Uint64 DILIGENT_CALL_TYPE GetCompletedFenceValue(Uint32 QueueIndex) override final
243  {
244  return m_CommandQueues[QueueIndex].CmdQueue->GetCompletedFenceValue();
245  }
246 
247  virtual Uint64 DILIGENT_CALL_TYPE GetNextFenceValue(Uint32 QueueIndex) override final
248  {
249  return m_CommandQueues[QueueIndex].CmdQueue->GetNextFenceValue();
250  }
251 
252  virtual Bool DILIGENT_CALL_TYPE IsFenceSignaled(Uint32 QueueIndex, Uint64 FenceValue) override final
253  {
254  return FenceValue <= GetCompletedFenceValue(QueueIndex);
255  }
256 
257  template <typename TAction>
258  void LockCmdQueueAndRun(Uint32 QueueIndex, TAction Action)
259  {
260  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
261  auto& Queue = m_CommandQueues[QueueIndex];
262  std::lock_guard<std::mutex> Lock{Queue.Mtx};
263  Action(Queue.CmdQueue);
264  }
265 
266  CommandQueueType* LockCommandQueue(Uint32 QueueIndex)
267  {
268  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
269  auto& Queue = m_CommandQueues[QueueIndex];
270  Queue.Mtx.lock();
271  return Queue.CmdQueue;
272  }
273 
274  void UnlockCommandQueue(Uint32 QueueIndex)
275  {
276  VERIFY_EXPR(QueueIndex < m_CmdQueueCount);
277  auto& Queue = m_CommandQueues[QueueIndex];
278  Queue.Mtx.unlock();
279  }
280 
281 protected:
283  {
284  if (m_CommandQueues != nullptr)
285  {
286  for (size_t q = 0; q < m_CmdQueueCount; ++q)
287  {
288  auto& Queue = m_CommandQueues[q];
289  DEV_CHECK_ERR(Queue.ReleaseQueue.GetStaleResourceCount() == 0, "All stale resources must be released before destroying a command queue");
290  DEV_CHECK_ERR(Queue.ReleaseQueue.GetPendingReleaseResourceCount() == 0, "All resources must be released before destroying a command queue");
291  Queue.~CommandQueue();
292  }
293  this->m_RawMemAllocator.Free(m_CommandQueues);
294  m_CommandQueues = nullptr;
295  }
296  }
297 
299  {
301  CmdQueue{std::move(_CmdQueue)},
302  ReleaseQueue{Allocator}
303  {
304  NextCmdBufferNumber.store(0);
305  }
306 
307  // clang-format off
308  CommandQueue (const CommandQueue&) = delete;
309  CommandQueue ( CommandQueue&&) = delete;
310  CommandQueue& operator = (const CommandQueue&) = delete;
311  CommandQueue& operator = ( CommandQueue&&) = delete;
312  // clang-format on
313 
314  std::mutex Mtx;
315  std::atomic_uint64_t NextCmdBufferNumber{0};
318  };
319  const size_t m_CmdQueueCount = 0;
320  CommandQueue* m_CommandQueues = nullptr;
321 };
322 
323 } // namespace Diligent
Diligent::RenderDeviceNextGenBase::GetCommandQueueMask
Uint64 GetCommandQueueMask() const
Definition: RenderDeviceNextGenBase.hpp:132
Diligent::IReferenceCounters
Base interface for a reference counter object that stores the number of strong and weak references an...
Definition: ReferenceCounters.h:44
Diligent::RenderDeviceNextGenBase::GetReleaseQueue
ResourceReleaseQueue< DynamicStaleResourceWrapper > & GetReleaseQueue(Uint32 QueueIndex)
Definition: RenderDeviceNextGenBase.hpp:230
Diligent::RenderDeviceNextGenBase::CommandQueue::ReleaseQueue
ResourceReleaseQueue< DynamicStaleResourceWrapper > ReleaseQueue
Definition: RenderDeviceNextGenBase.hpp:317
BasicAtomics::Long
long Long
Definition: BasicAtomics.hpp:34
Diligent::RenderDeviceNextGenBase::GetCommandQueueCount
size_t GetCommandQueueCount() const
Definition: RenderDeviceNextGenBase.hpp:127
Diligent::RenderDeviceNextGenBase::IsFenceSignaled
virtual Bool IsFenceSignaled(Uint32 QueueIndex, Uint64 FenceValue) override final
Definition: RenderDeviceNextGenBase.hpp:252
Diligent::RenderDeviceNextGenBase::CommandQueue::CmdQueue
RefCntAutoPtr< CommandQueueType > CmdQueue
Definition: RenderDeviceNextGenBase.hpp:316
Diligent::Uint64
uint64_t Uint64
64-bit unsigned integer
Definition: BasicTypes.h:50
Diligent::RenderDeviceNextGenBase::LockCmdQueueAndRun
void LockCmdQueueAndRun(Uint32 QueueIndex, TAction Action)
Definition: RenderDeviceNextGenBase.hpp:258
Diligent::RenderDeviceNextGenBase::SubmittedCommandBufferInfo::CmdBufferNumber
Uint64 CmdBufferNumber
Definition: RenderDeviceNextGenBase.hpp:188
Diligent::RenderDeviceNextGenBase::CommandQueue::CommandQueue
CommandQueue(RefCntAutoPtr< CommandQueueType > _CmdQueue, IMemoryAllocator &Allocator) noexcept
Definition: RenderDeviceNextGenBase.hpp:300
Diligent::RenderDeviceNextGenBase::SubmittedCommandBufferInfo
Definition: RenderDeviceNextGenBase.hpp:186
Diligent::RenderDeviceNextGenBase::IdleAllCommandQueues
void IdleAllCommandQueues(bool ReleaseResources)
Definition: RenderDeviceNextGenBase.hpp:180
Diligent::RenderDeviceNextGenBase::IdleCommandQueue
void IdleCommandQueue(size_t QueueIdx, bool ReleaseResources)
Definition: RenderDeviceNextGenBase.hpp:151
Diligent::RenderDeviceNextGenBase::RenderDeviceNextGenBase
RenderDeviceNextGenBase(IReferenceCounters *pRefCounters, IMemoryAllocator &RawMemAllocator, IEngineFactory *pEngineFactory, size_t CmdQueueCount, CommandQueueType **Queues, Uint32 NumDeferredContexts)
Definition: RenderDeviceNextGenBase.hpp:52
DEV_CHECK_ERR
#define DEV_CHECK_ERR(...)
Definition: DebugUtilities.hpp:90
Diligent::RenderDeviceNextGenBase::SubmitCommandBuffer
SubmittedCommandBufferInfo SubmitCommandBuffer(Uint32 QueueIndex, bool DiscardStaleResources, const SubmitDataType &... SubmitData)
Definition: RenderDeviceNextGenBase.hpp:192
Diligent::max
Vector3< T > max(const Vector3< T > &a, const Vector3< T > &b)
Definition: BasicMath.hpp:1660
Diligent::ResourceReleaseQueue
Facilitates safe resource destruction in D3D12 and Vulkan.
Definition: ResourceReleaseQueue.hpp:221
Diligent::RenderDeviceNextGenBase
Base implementation of the render device for next-generation backends.
Definition: RenderDeviceNextGenBase.hpp:49
EngineMemory.h
Diligent::RenderDeviceNextGenBase::CommandQueue
Definition: RenderDeviceNextGenBase.hpp:298
ReferenceCounters.h
Diligent::RenderDeviceNextGenBase::CommandQueue::operator=
CommandQueue & operator=(const CommandQueue &)=delete
Diligent::RenderDeviceNextGenBase::GetNextFenceValue
virtual Uint64 GetNextFenceValue(Uint32 QueueIndex) override final
Definition: RenderDeviceNextGenBase.hpp:247
Diligent::RenderDeviceNextGenBase::SafeReleaseDeviceObject
void SafeReleaseDeviceObject(ObjectType &&Object, Uint64 QueueMask)
Definition: RenderDeviceNextGenBase.hpp:98
BasicTypes.h
Diligent::RenderDeviceNextGenBase::m_CmdQueueCount
const size_t m_CmdQueueCount
Definition: RenderDeviceNextGenBase.hpp:319
Diligent::RenderDeviceNextGenBase::PurgeReleaseQueues
void PurgeReleaseQueues(bool ForceRelease=false)
Definition: RenderDeviceNextGenBase.hpp:137
Diligent::RefCntAutoPtr< CommandQueueType >
DILIGENT_CALL_TYPE
#define DILIGENT_CALL_TYPE
Definition: CommonDefinitions.h:45
Diligent::RenderDeviceNextGenBase::GetCompletedFenceValue
virtual Uint64 GetCompletedFenceValue(Uint32 QueueIndex) override final
Definition: RenderDeviceNextGenBase.hpp:242
Diligent::RenderDeviceNextGenBase::LockCommandQueue
CommandQueueType * LockCommandQueue(Uint32 QueueIndex)
Definition: RenderDeviceNextGenBase.hpp:266
Diligent::Uint32
uint32_t Uint32
32-bit unsigned integer
Definition: BasicTypes.h:51
Diligent::RenderDeviceNextGenBase::CommandQueue::Mtx
std::mutex Mtx
Definition: RenderDeviceNextGenBase.hpp:314
Diligent::Bool
bool Bool
Boolean.
Definition: BasicTypes.h:59
Diligent::RenderDeviceNextGenBase::CommandQueue::NextCmdBufferNumber
std::atomic_uint64_t NextCmdBufferNumber
Definition: RenderDeviceNextGenBase.hpp:315
Diligent::RenderDeviceNextGenBase::DestroyCommandQueues
void DestroyCommandQueues()
Definition: RenderDeviceNextGenBase.hpp:282
Diligent::IMemoryAllocator
Base interface for a raw memory allocator.
Definition: MemoryAllocator.h:41
Diligent::RenderDeviceNextGenBase::UnlockCommandQueue
void UnlockCommandQueue(Uint32 QueueIndex)
Definition: RenderDeviceNextGenBase.hpp:274
MemoryAllocator.h
ResourceReleaseQueue.hpp
EngineFactory.h
Diligent::RenderDeviceNextGenBase::GetCommandQueue
const CommandQueueType & GetCommandQueue(Uint32 QueueIndex) const
Definition: RenderDeviceNextGenBase.hpp:236
VERIFY_EXPR
#define VERIFY_EXPR(...)
Definition: DebugUtilities.hpp:79
RefCntAutoPtr.hpp
VERIFY
#define VERIFY(...)
Definition: DebugUtilities.hpp:76
Diligent::RenderDeviceNextGenBase::m_CommandQueues
CommandQueue * m_CommandQueues
Definition: RenderDeviceNextGenBase.hpp:320
ALLOCATE
#define ALLOCATE(Allocator, Desc, Type, Count)
Definition: EngineMemory.h:47
Diligent::IEngineFactory
Engine factory base interface.
Definition: EngineFactory.h:60
Diligent::DynamicStaleResourceWrapper::Create
static DynamicStaleResourceWrapper Create(ResourceType &&Resource, Atomics::Long NumReferences)
Definition: ResourceReleaseQueue.hpp:68
Diligent::RenderDeviceNextGenBase::SubmittedCommandBufferInfo::FenceValue
Uint64 FenceValue
Definition: RenderDeviceNextGenBase.hpp:189
Diligent::RenderDeviceNextGenBase::PurgeReleaseQueue
void PurgeReleaseQueue(Uint32 QueueIndex, bool ForceRelease=false)
Definition: RenderDeviceNextGenBase.hpp:143
Diligent
The library uses Direct3D-style math:
Definition: AdvancedMath.hpp:37
PlatformMisc.hpp
Diligent::RenderDeviceNextGenBase::~RenderDeviceNextGenBase
~RenderDeviceNextGenBase()
Definition: RenderDeviceNextGenBase.hpp:66