xref: /aosp_15_r20/external/angle/src/common/RingBufferAllocator.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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