Diligent Engine  v.2.4.g
RingBuffer.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 
32 
33 
34 #include <deque>
35 #include "../../../Primitives/interface/MemoryAllocator.h"
36 #include "../../../Platforms/Basic/interface/DebugUtilities.hpp"
37 #include "../../../Common/interface/Align.hpp"
38 #include "../../../Common/interface/STDAllocator.hpp"
39 
40 namespace Diligent
41 {
44 {
45 public:
46  using OffsetType = size_t;
48  {
49  // clang-format off
50  FrameHeadAttribs(Uint64 fv, OffsetType off, OffsetType sz) noexcept :
51  FenceValue{fv },
52  Offset {off},
53  Size {sz }
54  {}
55  // clang-format on
56 
57  // Fence value associated with the command list in which
58  // the allocation could have been referenced last time
62  };
63  static constexpr const OffsetType InvalidOffset = static_cast<OffsetType>(-1);
64 
65  RingBuffer(OffsetType MaxSize, IMemoryAllocator& Allocator) noexcept :
66  m_CompletedFrameHeads(STD_ALLOCATOR_RAW_MEM(FrameHeadAttribs, Allocator, "Allocator for deque<FrameHeadAttribs>")),
67  m_MaxSize{MaxSize}
68  {}
69 
70  // clang-format off
71  RingBuffer(RingBuffer&& rhs) noexcept :
72  m_CompletedFrameHeads{std::move(rhs.m_CompletedFrameHeads)},
73  m_Tail {rhs.m_Tail },
74  m_Head {rhs.m_Head },
75  m_MaxSize {rhs.m_MaxSize },
76  m_UsedSize {rhs.m_UsedSize },
77  m_CurrFrameSize {rhs.m_CurrFrameSize}
78  // clang-format on
79  {
80  rhs.m_Tail = 0;
81  rhs.m_Head = 0;
82  rhs.m_MaxSize = 0;
83  rhs.m_UsedSize = 0;
84  rhs.m_CurrFrameSize = 0;
85  }
86 
87  RingBuffer& operator=(RingBuffer&& rhs) noexcept
88  {
89  m_CompletedFrameHeads = std::move(rhs.m_CompletedFrameHeads);
90  m_Tail = rhs.m_Tail;
91  m_Head = rhs.m_Head;
92  m_MaxSize = rhs.m_MaxSize;
93  m_UsedSize = rhs.m_UsedSize;
94  m_CurrFrameSize = rhs.m_CurrFrameSize;
95 
96  rhs.m_MaxSize = 0;
97  rhs.m_Tail = 0;
98  rhs.m_Head = 0;
99  rhs.m_UsedSize = 0;
100  rhs.m_CurrFrameSize = 0;
101 
102  return *this;
103  }
104 
105  // clang-format off
106  RingBuffer (const RingBuffer&) = delete;
107  RingBuffer& operator = (const RingBuffer&) = delete;
108  // clang-format on
109 
111  {
112  VERIFY(m_UsedSize == 0, "All space in the ring buffer must be released");
113  }
114 
116  {
117  VERIFY_EXPR(Size > 0);
118  VERIFY(IsPowerOfTwo(Alignment), "Alignment (", Alignment, ") must be power of 2");
119  Size = AlignUp(Size, Alignment);
120 
121  if (m_UsedSize + Size > m_MaxSize)
122  {
123  return InvalidOffset;
124  }
125 
126  auto AlignedHead = AlignUp(m_Head, Alignment);
127  if (m_Head >= m_Tail)
128  {
129  // AlignedHead
130  // Tail Head | MaxSize
131  // | | | |
132  // [ xxxxxxxxxxxxxxxxx... ]
133  //
134  //
135  if (AlignedHead + Size <= m_MaxSize)
136  {
137  auto Offset = AlignedHead;
138  auto AdjustedSize = Size + (AlignedHead - m_Head);
139  m_Head += AdjustedSize;
140  m_UsedSize += AdjustedSize;
141  m_CurrFrameSize += AdjustedSize;
142  return Offset;
143  }
144  else if (Size <= m_Tail)
145  {
146  // Allocate from the beginning of the buffer
147  //
148  //
149  // Offset Tail Head MaxSize
150  // | | |<---AddSize--->|
151  // [ xxxxxxxxxxxxxxxxx++++++++++++++++]
152  //
153  OffsetType AddSize = (m_MaxSize - m_Head) + Size;
154  m_UsedSize += AddSize;
155  m_CurrFrameSize += AddSize;
156  m_Head = Size;
157  return 0;
158  }
159  }
160  else if (AlignedHead + Size <= m_Tail)
161  {
162  // AlignedHead
163  // Head | Tail
164  // | | |
165  // [xxxx... xxxxxxxxxxxxxxxxxxxxxxxxxx]
166  //
167  auto Offset = AlignedHead;
168  auto AdjustedSize = Size + (AlignedHead - m_Head);
169  m_Head += AdjustedSize;
170  m_UsedSize += AdjustedSize;
171  m_CurrFrameSize += AdjustedSize;
172  return Offset;
173  }
174 
175  return InvalidOffset;
176  }
177 
178  // FenceValue is the fence value associated with the command list in which the head
179  // could have been referenced last time
180  // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/
181  void FinishCurrentFrame(Uint64 FenceValue)
182  {
183 #ifdef DILIGENT_DEBUG
184  if (!m_CompletedFrameHeads.empty())
185  VERIFY(FenceValue >= m_CompletedFrameHeads.back().FenceValue, "Current frame fence value (", FenceValue, ") is lower than the fence value of the previous frame (", m_CompletedFrameHeads.back().FenceValue, ")");
186 #endif
187  // Ignore zero-size frames
188  if (m_CurrFrameSize != 0)
189  {
190  m_CompletedFrameHeads.emplace_back(FenceValue, m_Head, m_CurrFrameSize);
191  m_CurrFrameSize = 0;
192  }
193  }
194 
195  // CompletedFenceValue indicates GPU progress
196  // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/
197  void ReleaseCompletedFrames(Uint64 CompletedFenceValue)
198  {
199  // We can release all heads whose associated fence value is less than or equal to CompletedFenceValue
200  while (!m_CompletedFrameHeads.empty() && m_CompletedFrameHeads.front().FenceValue <= CompletedFenceValue)
201  {
202  const auto& OldestFrameHead = m_CompletedFrameHeads.front();
203  VERIFY_EXPR(OldestFrameHead.Size <= m_UsedSize);
204  m_UsedSize -= OldestFrameHead.Size;
205  m_Tail = OldestFrameHead.Offset;
206  m_CompletedFrameHeads.pop_front();
207  }
208 
209  if (IsEmpty())
210  {
211 #ifdef DILIGENT_DEBUG
212  VERIFY(m_CompletedFrameHeads.empty(), "Zero-size heads are not added to the list, and since the buffer is empty, there must be no heads in the list");
213  for (const auto& head : m_CompletedFrameHeads)
214  VERIFY(head.Size == 0, "Non zero-size head found");
215 #endif
216  m_CompletedFrameHeads.clear();
217 
218  // t,h t,h
219  // | | | ====> | |
220  m_Tail = m_Head = 0;
221  }
222  }
223 
224  // clang-format off
225  OffsetType GetMaxSize() const { return m_MaxSize; }
226  bool IsFull() const { return m_UsedSize==m_MaxSize; };
227  bool IsEmpty() const { return m_UsedSize==0; };
228  OffsetType GetUsedSize()const { return m_UsedSize; }
229  // clang-format on
230 
231 private:
232  std::deque<FrameHeadAttribs, STDAllocatorRawMem<FrameHeadAttribs>> m_CompletedFrameHeads;
233 
234  OffsetType m_Tail = 0;
235  OffsetType m_Head = 0;
236  OffsetType m_MaxSize = 0;
237  OffsetType m_UsedSize = 0;
238  OffsetType m_CurrFrameSize = 0;
239 };
240 } // namespace Diligent
Diligent::RingBuffer
Implementation of a ring buffer. The class is not thread-safe.
Definition: RingBuffer.hpp:43
Diligent::RingBuffer::FrameHeadAttribs::FenceValue
Uint64 FenceValue
Definition: RingBuffer.hpp:59
Diligent::RingBuffer::GetMaxSize
OffsetType GetMaxSize() const
Definition: RingBuffer.hpp:225
Diligent::RingBuffer::FinishCurrentFrame
void FinishCurrentFrame(Uint64 FenceValue)
Definition: RingBuffer.hpp:181
Diligent::RingBuffer::ReleaseCompletedFrames
void ReleaseCompletedFrames(Uint64 CompletedFenceValue)
Definition: RingBuffer.hpp:197
Diligent::RingBuffer::FrameHeadAttribs::Size
OffsetType Size
Definition: RingBuffer.hpp:61
Diligent::RingBuffer::operator=
RingBuffer & operator=(RingBuffer &&rhs) noexcept
Definition: RingBuffer.hpp:87
Diligent::Uint64
uint64_t Uint64
64-bit unsigned integer
Definition: BasicTypes.h:50
Diligent::RingBuffer::RingBuffer
RingBuffer(RingBuffer &&rhs) noexcept
Definition: RingBuffer.hpp:71
Diligent::RingBuffer::FrameHeadAttribs::FrameHeadAttribs
FrameHeadAttribs(Uint64 fv, OffsetType off, OffsetType sz) noexcept
Definition: RingBuffer.hpp:50
Diligent::RingBuffer::FrameHeadAttribs
Definition: RingBuffer.hpp:47
Diligent::RingBuffer::Allocate
OffsetType Allocate(OffsetType Size, OffsetType Alignment)
Definition: RingBuffer.hpp:115
Diligent::RingBuffer::InvalidOffset
static constexpr const OffsetType InvalidOffset
Definition: RingBuffer.hpp:63
Diligent::RingBuffer::GetUsedSize
OffsetType GetUsedSize() const
Definition: RingBuffer.hpp:228
Diligent::RingBuffer::IsEmpty
bool IsEmpty() const
Definition: RingBuffer.hpp:227
Diligent::RingBuffer::~RingBuffer
~RingBuffer()
Definition: RingBuffer.hpp:110
Diligent::RingBuffer::OffsetType
size_t OffsetType
Definition: RingBuffer.hpp:46
Diligent::IsPowerOfTwo
bool IsPowerOfTwo(T val)
Definition: Align.hpp:41
Diligent::IMemoryAllocator
Base interface for a raw memory allocator.
Definition: MemoryAllocator.h:41
Diligent::RingBuffer::IsFull
bool IsFull() const
Definition: RingBuffer.hpp:226
STD_ALLOCATOR_RAW_MEM
#define STD_ALLOCATOR_RAW_MEM(Type, Allocator, Description)
Definition: STDAllocator.hpp:179
VERIFY_EXPR
#define VERIFY_EXPR(...)
Definition: DebugUtilities.hpp:79
VERIFY
#define VERIFY(...)
Definition: DebugUtilities.hpp:76
Diligent::AlignUp
T2 ::type AlignUp(T1 val, T2 alignment)
Definition: Align.hpp:47
Diligent::RingBuffer::RingBuffer
RingBuffer(OffsetType MaxSize, IMemoryAllocator &Allocator) noexcept
Definition: RingBuffer.hpp:65
Diligent
The library uses Direct3D-style math:
Definition: AdvancedMath.hpp:37
Diligent::RingBuffer::FrameHeadAttribs::Offset
OffsetType Offset
Definition: RingBuffer.hpp:60