Diligent Engine API Reference
RingBuffer.h
1 /* Copyright 2015-2018 Egor Yusov
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12  *
13  * In no event and under no legal theory, whether in tort (including negligence),
14  * contract, or otherwise, unless required by applicable law (such as deliberate
15  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
16  * liable for any damages, including any direct, indirect, special, incidental,
17  * or consequential damages of any character arising as a result of this License or
18  * out of the use or inability to use the software (including but not limited to damages
19  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20  * all other commercial damages or losses), even if such Contributor has been advised
21  * of the possibility of such damages.
22  */
23 
24 #pragma once
25 
28 
29 
30 #include <deque>
31 #include "MemoryAllocator.h"
32 #include "STDAllocator.h"
33 #include "DebugUtilities.h"
34 
35 namespace Diligent
36 {
38  class RingBuffer
39  {
40  public:
41  typedef size_t OffsetType;
42  struct FrameTailAttribs
43  {
44  FrameTailAttribs(Uint64 fv, OffsetType off, OffsetType sz) :
45  FenceValue(fv),
46  Offset(off),
47  Size(sz)
48  {}
49 
50  // Fence value associated with the command list in which
51  // the allocation could have been referenced last time
52  Uint64 FenceValue;
53  OffsetType Offset;
54  OffsetType Size;
55  };
56  static const OffsetType InvalidOffset = static_cast<OffsetType>(-1);
57 
58  RingBuffer(OffsetType MaxSize, IMemoryAllocator &Allocator)noexcept :
59  m_CompletedFrameTails(0, FrameTailAttribs(0,0,0), STD_ALLOCATOR_RAW_MEM(FrameTailAttribs, Allocator, "Allocator for vector<FrameNumOffsetPair>" )),
60  m_MaxSize(MaxSize)
61  {}
62 
63  RingBuffer(RingBuffer&& rhs)noexcept :
64  m_CompletedFrameTails(std::move(rhs.m_CompletedFrameTails)),
65  m_Head(rhs.m_Head),
66  m_Tail(rhs.m_Tail),
67  m_MaxSize(rhs.m_MaxSize),
68  m_UsedSize(rhs.m_UsedSize),
69  m_CurrFrameSize(rhs.m_CurrFrameSize)
70  {
71  rhs.m_Head = 0;
72  rhs.m_Tail = 0;
73  rhs.m_MaxSize = 0;
74  rhs.m_UsedSize = 0;
75  rhs.m_CurrFrameSize = 0;
76  }
77 
78  RingBuffer& operator = (RingBuffer&& rhs)noexcept
79  {
80  m_CompletedFrameTails = std::move(rhs.m_CompletedFrameTails);
81  m_Head = rhs.m_Head;
82  m_Tail = rhs.m_Tail;
83  m_MaxSize = rhs.m_MaxSize;
84  m_UsedSize = rhs.m_UsedSize;
85  m_CurrFrameSize = rhs.m_CurrFrameSize;
86 
87  rhs.m_MaxSize = 0;
88  rhs.m_Head = 0;
89  rhs.m_Tail = 0;
90  rhs.m_UsedSize = 0;
91  rhs.m_CurrFrameSize = 0;
92 
93  return *this;
94  }
95 
96  RingBuffer(const RingBuffer&) = delete;
97  RingBuffer& operator = (const RingBuffer&) = delete;
98 
99  ~RingBuffer()
100  {
101  VERIFY(m_UsedSize==0, "All space in the ring buffer must be released");
102  }
103 
104  OffsetType Allocate(OffsetType Size)
105  {
106  if(IsFull())
107  {
108  return InvalidOffset;
109  }
110 
111  if (m_Tail >= m_Head )
112  {
113  // Head Tail MaxSize
114  // | | |
115  // [ xxxxxxxxxxxxxxxxx ]
116  //
117  //
118  if (m_Tail + Size <= m_MaxSize)
119  {
120  auto Offset = m_Tail;
121  m_Tail += Size;
122  m_UsedSize += Size;
123  m_CurrFrameSize += Size;
124  return Offset;
125  }
126  else if(Size <= m_Head)
127  {
128  // Allocate from the beginning of the buffer
129  OffsetType AddSize = (m_MaxSize - m_Tail) + Size;
130  m_UsedSize += AddSize;
131  m_CurrFrameSize += AddSize;
132  m_Tail = Size;
133  return 0;
134  }
135  }
136  else if (m_Tail + Size <= m_Head )
137  {
138  //
139  // Tail Head
140  // | |
141  // [xxxx xxxxxxxxxxxxxxxxxxxxxxxxxx]
142  //
143  auto Offset = m_Tail;
144  m_Tail += Size;
145  m_UsedSize += Size;
146  m_CurrFrameSize += Size;
147  return Offset;
148  }
149 
150  return InvalidOffset;
151  }
152 
153  // FenceValue is the fence value associated with the command list in which the tail
154  // could have been referenced last time
155  // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/
156  void FinishCurrentFrame(Uint64 FenceValue)
157  {
158  m_CompletedFrameTails.emplace_back(FenceValue, m_Tail, m_CurrFrameSize);
159  m_CurrFrameSize = 0;
160  }
161 
162  // CompletedFenceValue indicates GPU progress
163  // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/
164  void ReleaseCompletedFrames(Uint64 CompletedFenceValue)
165  {
166  // We can release all tails whose associated fence value is less than or equal to CompletedFenceValue
167  while(!m_CompletedFrameTails.empty() && m_CompletedFrameTails.front().FenceValue <= CompletedFenceValue)
168  {
169  const auto &OldestFrameTail = m_CompletedFrameTails.front();
170  VERIFY_EXPR(OldestFrameTail.Size <= m_UsedSize);
171  m_UsedSize -= OldestFrameTail.Size;
172  m_Head = OldestFrameTail.Offset;
173  m_CompletedFrameTails.pop_front();
174  }
175  }
176 
177  OffsetType GetMaxSize()const{return m_MaxSize;}
178  bool IsFull()const{ return m_UsedSize==m_MaxSize; };
179  bool IsEmpty()const{ return m_UsedSize==0; };
180  OffsetType GetUsedSize()const{return m_UsedSize;}
181 
182  private:
183  // Consider the following scenario for a 1024-byte buffer:
184  // Allocate(512)
185  //
186  // h t m
187  // |xxxxx| |
188 
189  // FinishCurrentFrame(0)
190  //
191  // t0
192  // h t m
193  // |xxxxx| |
194 
195  // ReleaseCompletedFrames(1)
196  //
197  // h
198  // t m
199  // | | |
200 
201  // FinishCurrentFrame(1)
202  //
203  // t1
204  // h
205  // t m
206  // | | |
207 
208  // Allocate(512)
209  //
210  // t1 t
211  // h m
212  // | |xxxxx|
213 
214  // Allocate(512)
215  //
216  // t
217  // t1
218  // h m
219  // |xxxxx|xxxxx|
220 
221  // FinishCurrentFrame(2)
222  //
223  // t
224  // t1
225  // t2
226  // h m
227  // |xxxxx|xxxxx|
228 
229  // At this point there will be two tails in the queue, both at 512. m_UsedSize will be 0. When
230  // ReleaseCompletedFrames(2) is called, there wil be no way to find out if the current frame is 0
231  // or the entire buffer if we don't store the frame size
232 
233  std::deque< FrameTailAttribs, STDAllocatorRawMem<FrameTailAttribs> > m_CompletedFrameTails;
234  OffsetType m_Head = 0;
235  OffsetType m_Tail = 0;
236  OffsetType m_MaxSize = 0;
237  OffsetType m_UsedSize = 0;
238  OffsetType m_CurrFrameSize = 0;
239  };
240 }
Graphics engine namespace.
Definition: AdaptiveFixedBlockAllocator.h:30
uint64_t Uint64
64-bit unsigned integer
Definition: BasicTypes.h:38
Base interface for a raw memory allocator.
Definition: MemoryAllocator.h:35
Implementation of a ring buffer. The class is not thread-safe.
Definition: RingBuffer.h:38