1 //
2 // Copyright 2022 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // RingBufferAllocator.cpp:
7 // Implements classes used for ring buffer allocators.
8 //
9
10 #include "common/RingBufferAllocator.h"
11
12 namespace angle
13 {
14
15 // RingBufferAllocator implementation.
RingBufferAllocator(RingBufferAllocator && other)16 RingBufferAllocator::RingBufferAllocator(RingBufferAllocator &&other)
17 {
18 *this = std::move(other);
19 }
20
operator =(RingBufferAllocator && other)21 RingBufferAllocator &RingBufferAllocator::operator=(RingBufferAllocator &&other)
22 {
23 mOldBuffers = std::move(other.mOldBuffers);
24 mBuffer = std::move(other.mBuffer);
25 mDataBegin = other.mDataBegin;
26 mDataEnd = other.mDataEnd;
27 mFragmentEnd = other.mFragmentEnd;
28 mFragmentEndR = other.mFragmentEndR;
29 mFragmentReserve = other.mFragmentReserve;
30
31 mMinCapacity = other.mMinCapacity;
32 mCurrentCapacity = other.mCurrentCapacity;
33 mAllocationMargin = other.mAllocationMargin;
34 mDecaySpeedFactor = other.mDecaySpeedFactor;
35
36 ASSERT(other.mOldBuffers.size() == 0);
37 ASSERT(other.mBuffer.isEmpty());
38 other.mBuffer.resetId();
39 other.mDataBegin = nullptr;
40 other.mDataEnd = nullptr;
41 other.mFragmentEnd = nullptr;
42 other.mFragmentEndR = nullptr;
43 other.mFragmentReserve = 0;
44
45 other.mMinCapacity = 0;
46 other.mCurrentCapacity = 0;
47 other.mAllocationMargin = 0;
48 other.mDecaySpeedFactor = 0;
49
50 return *this;
51 }
52
reset()53 void RingBufferAllocator::reset()
54 {
55 mListener = nullptr;
56 mFragmentReserve = 0;
57 mDecaySpeedFactor = kDefaultDecaySpeedFactor;
58 mMinCapacity = kMinRingBufferAllocationCapacity;
59 resize(mMinCapacity);
60 mOldBuffers.clear();
61 }
62
setListener(RingBufferAllocateListener * listener)63 void RingBufferAllocator::setListener(RingBufferAllocateListener *listener)
64 {
65 ASSERT(!mListener || !listener);
66 mListener = listener;
67 }
68
setDecaySpeedFactor(uint32_t decaySpeedFactor)69 void RingBufferAllocator::setDecaySpeedFactor(uint32_t decaySpeedFactor)
70 {
71 ASSERT(valid());
72 mDecaySpeedFactor = std::max(decaySpeedFactor, 1u);
73 }
74
getReleaseCheckPoint() const75 RingBufferAllocatorCheckPoint RingBufferAllocator::getReleaseCheckPoint() const
76 {
77 ASSERT(valid());
78 RingBufferAllocatorCheckPoint result;
79 result.mBufferId = mBuffer.getId();
80 result.mReleasePtr = mDataEnd;
81 return result;
82 }
83
release(const RingBufferAllocatorCheckPoint & checkPoint)84 void RingBufferAllocator::release(const RingBufferAllocatorCheckPoint &checkPoint)
85 {
86 ASSERT(valid());
87 ASSERT(checkPoint.valid());
88
89 if (mOldBuffers.size() > 0)
90 {
91 // mOldBuffers are sorted by id
92 int removeCount = 0;
93 for (uint32_t i = 0;
94 (i < mOldBuffers.size()) && (mOldBuffers[i].getId() < checkPoint.mBufferId); ++i)
95 {
96 ++removeCount;
97 }
98 mOldBuffers.erase(mOldBuffers.begin(), mOldBuffers.begin() + removeCount);
99 }
100
101 if (checkPoint.mBufferId == mBuffer.getId())
102 {
103 const uint32_t allocatedBefore = getNumAllocatedInBuffer();
104
105 release(checkPoint.mReleasePtr);
106
107 if (allocatedBefore >= mAllocationMargin)
108 {
109 if ((mCurrentCapacity > mMinCapacity) && (allocatedBefore * 6 <= mCurrentCapacity))
110 {
111 resize(std::max(allocatedBefore * 3, mMinCapacity));
112 }
113 else
114 {
115 mAllocationMargin = mCurrentCapacity;
116 }
117 }
118 else
119 {
120 const uint64_t numReleased = (allocatedBefore - getNumAllocatedInBuffer());
121 const uint64_t distanceToMargin = (mAllocationMargin - allocatedBefore);
122 mAllocationMargin -=
123 std::max(static_cast<uint32_t>(numReleased * distanceToMargin / mAllocationMargin /
124 mDecaySpeedFactor),
125 1u);
126 }
127 }
128 }
129
setFragmentReserve(uint32_t reserve)130 void RingBufferAllocator::setFragmentReserve(uint32_t reserve)
131 {
132 ASSERT(valid());
133 mFragmentReserve = reserve;
134 mFragmentEndR = mBuffer.decClamped(mFragmentEnd, mFragmentReserve);
135 }
136
allocateInNewFragment(uint32_t size)137 uint8_t *RingBufferAllocator::allocateInNewFragment(uint32_t size)
138 {
139 if (mListener)
140 {
141 mListener->onRingBufferFragmentEnd();
142 }
143
144 uint8_t *newFragmentBegin = nullptr;
145 if (mFragmentEnd != mDataBegin)
146 {
147 newFragmentBegin = mBuffer.data();
148 uint8_t *const newFragmentEnd = mDataBegin;
149 uint8_t *const newFragmentEndR = mBuffer.decClamped(newFragmentEnd, mFragmentReserve);
150
151 // It should wrap around only if it can allocate.
152 if (newFragmentEndR - newFragmentBegin >= static_cast<ptrdiff_t>(size))
153 {
154 mDataEnd = newFragmentBegin;
155 mFragmentEnd = newFragmentEnd;
156 mFragmentEndR = newFragmentEndR;
157
158 if (mListener)
159 {
160 mListener->onRingBufferNewFragment();
161 }
162
163 mDataEnd = newFragmentBegin + size;
164 return newFragmentBegin;
165 }
166 }
167
168 resize(std::max(mCurrentCapacity + mCurrentCapacity / 2, size + mFragmentReserve));
169
170 if (mListener)
171 {
172 mListener->onRingBufferNewFragment();
173 }
174
175 ASSERT(mFragmentEndR - mDataEnd >= static_cast<ptrdiff_t>(size));
176 newFragmentBegin = mDataEnd;
177 mDataEnd = newFragmentBegin + size;
178 return newFragmentBegin;
179 }
180
resize(uint32_t newCapacity)181 void RingBufferAllocator::resize(uint32_t newCapacity)
182 {
183 ASSERT(newCapacity >= mMinCapacity);
184
185 if (mBuffer.getId() != 0)
186 {
187 mOldBuffers.emplace_back(std::move(mBuffer));
188 }
189
190 mCurrentCapacity = newCapacity;
191 mBuffer.incrementId();
192 mBuffer.resize(mCurrentCapacity);
193 resetPointers();
194
195 mAllocationMargin = mCurrentCapacity;
196 }
197
release(uint8_t * releasePtr)198 void RingBufferAllocator::release(uint8_t *releasePtr)
199 {
200 if (releasePtr == mDataEnd)
201 {
202 // Ensures "mDataEnd == mBuffer.data()" with 0 allocations.
203 resetPointers();
204 return;
205 }
206
207 if (mDataBegin == mFragmentEnd)
208 {
209 ASSERT((releasePtr >= mBuffer.data() && releasePtr < mDataEnd) ||
210 (releasePtr >= mDataBegin && releasePtr <= mBuffer.data() + mCurrentCapacity));
211 if (releasePtr < mDataBegin)
212 {
213 mFragmentEnd = mBuffer.data() + mCurrentCapacity;
214 }
215 else
216 {
217 mFragmentEnd = releasePtr;
218 }
219 mFragmentEndR = mBuffer.decClamped(mFragmentEnd, mFragmentReserve);
220 }
221 else
222 {
223 ASSERT(releasePtr >= mDataBegin && releasePtr < mDataEnd);
224 }
225 mDataBegin = releasePtr;
226 }
227
getNumAllocatedInBuffer() const228 uint32_t RingBufferAllocator::getNumAllocatedInBuffer() const
229 {
230 // 2 free fragments: [mBuffer.begin, mDataBegin) [mDataEnd, mBuffer.end);
231 // 1 used fragment: [DataBegin, DataEnd)
232 if (mFragmentEnd != mDataBegin)
233 {
234 ASSERT(mDataEnd >= mDataBegin);
235 return static_cast<uint32_t>(mDataEnd - mDataBegin);
236 }
237
238 // 1 free fragment: [mDataEnd, mDataBegin)
239 // 2 used fragments: [mBuffer.begin, mDataEnd) [mDataBegin, mBuffer.end)
240 ASSERT(mDataBegin >= mDataEnd);
241 return (mCurrentCapacity - static_cast<uint32_t>(mDataBegin - mDataEnd));
242 }
243
resetPointers()244 void RingBufferAllocator::resetPointers()
245 {
246 mDataBegin = mBuffer.data();
247 mDataEnd = mDataBegin;
248 mFragmentEnd = mDataEnd + mCurrentCapacity;
249 mFragmentEndR = mBuffer.decClamped(mFragmentEnd, mFragmentReserve);
250 }
251
252 // SharedRingBufferAllocator implementation.
SharedRingBufferAllocator()253 SharedRingBufferAllocator::SharedRingBufferAllocator()
254 {
255 mAllocator.reset();
256 }
257
~SharedRingBufferAllocator()258 SharedRingBufferAllocator::~SharedRingBufferAllocator()
259 {
260 #if defined(ANGLE_ENABLE_ASSERTS)
261 ASSERT(!mSharedCP || mSharedCP->mRefCount == 1);
262 #endif
263 SafeDelete(mSharedCP);
264 }
265
acquireSharedCP()266 SharedRingBufferAllocatorCheckPoint *SharedRingBufferAllocator::acquireSharedCP()
267 {
268 if (!mSharedCP)
269 {
270 mSharedCP = new SharedRingBufferAllocatorCheckPoint();
271 }
272 #if defined(ANGLE_ENABLE_ASSERTS)
273 ASSERT(++mSharedCP->mRefCount > 1);
274 // Must always be 1 ref before
275 #endif
276 return mSharedCP;
277 }
278
releaseToSharedCP()279 void SharedRingBufferAllocator::releaseToSharedCP()
280 {
281 ASSERT(mSharedCP);
282 const RingBufferAllocatorCheckPoint releaseCP = mSharedCP->pop();
283 if (releaseCP.valid())
284 {
285 mAllocator.release(releaseCP);
286 }
287 }
288
releaseAndUpdate(RingBufferAllocatorCheckPoint * newValue)289 void SharedRingBufferAllocatorCheckPoint::releaseAndUpdate(RingBufferAllocatorCheckPoint *newValue)
290 {
291 ASSERT(newValue && newValue->valid());
292 #if defined(ANGLE_ENABLE_ASSERTS)
293 ASSERT(--mRefCount >= 1);
294 // Must always remain 1 ref
295 #endif
296 {
297 std::lock_guard<angle::SimpleMutex> lock(mMutex);
298 mValue = *newValue;
299 }
300 newValue->reset();
301 }
302
pop()303 RingBufferAllocatorCheckPoint SharedRingBufferAllocatorCheckPoint::pop()
304 {
305 std::lock_guard<angle::SimpleMutex> lock(mMutex);
306 RingBufferAllocatorCheckPoint value = mValue;
307 mValue.reset();
308 return value;
309 }
310
311 } // namespace angle
312