xref: /aosp_15_r20/external/angle/src/common/RingBufferAllocator.h (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.h:
7 //    Classes used to implement ring buffer allocators.
8 //
9 
10 #ifndef COMMON_RING_BUFFER_ALLOCATOR_H_
11 #define COMMON_RING_BUFFER_ALLOCATOR_H_
12 
13 #include "angleutils.h"
14 #include "common/SimpleMutex.h"
15 #include "common/debug.h"
16 
17 #include <atomic>
18 
19 namespace angle
20 {
21 
22 static constexpr uint32_t kMinRingBufferAllocationCapacity = 1024;
23 static constexpr uint32_t kDefaultDecaySpeedFactor         = 10;
24 
25 // Only called from RingBufferAllocator::allocate(). Other function may also change the fragment.
26 class RingBufferAllocateListener
27 {
28   public:
29     virtual void onRingBufferNewFragment() = 0;
30     virtual void onRingBufferFragmentEnd() = 0;
31 
32   protected:
33     ~RingBufferAllocateListener() = default;
34 };
35 
36 class RingBufferAllocatorCheckPoint final
37 {
38   public:
reset()39     void reset() { *this = {}; }
valid()40     bool valid() const { return (mReleasePtr != nullptr); }
41 
42   private:
43     friend class RingBufferAllocator;
44     uint64_t mBufferId   = 0;
45     uint8_t *mReleasePtr = nullptr;
46 };
47 
48 class RingBufferAllocatorBuffer final
49 {
50   public:
51     static constexpr uint32_t kBaseOffset = alignof(std::max_align_t);
52 
resize(uint32_t size)53     void resize(uint32_t size) { mStorage.resize(size + kBaseOffset); }
data()54     uint8_t *data() { return mStorage.data() + kBaseOffset; }
55 
decClamped(uint8_t * ptr,uint32_t offset)56     uint8_t *decClamped(uint8_t *ptr, uint32_t offset) const
57     {
58         ASSERT(ptr >= mStorage.data() + kBaseOffset && ptr <= mStorage.data() + mStorage.size());
59         return ptr - std::min(offset, static_cast<uint32_t>(ptr - mStorage.data()));
60     }
61 
getId()62     uint64_t getId() const { return mId; }
resetId()63     void resetId() { mId = 0; }
incrementId()64     void incrementId() { ++mId; }
65 
isEmpty()66     bool isEmpty() const { return mStorage.empty(); }
67 
68   private:
69     uint64_t mId = 0;
70     std::vector<uint8_t> mStorage;
71 };
72 
73 class RingBufferAllocator final : angle::NonCopyable
74 {
75   public:
76     RingBufferAllocator() = default;
77     RingBufferAllocator(RingBufferAllocator &&other);
78     RingBufferAllocator &operator=(RingBufferAllocator &&other);
79 
80     void reset();
valid()81     bool valid() const { return (getPointer() != nullptr); }
82 
83     void setListener(RingBufferAllocateListener *listener);
84 
85     void setDecaySpeedFactor(uint32_t decaySpeedFactor);
86 
87     void setFragmentReserve(uint32_t reserve);
88 
allocate(uint32_t size)89     uint8_t *allocate(uint32_t size)
90     {
91         ASSERT(valid());
92         if (ANGLE_LIKELY(mFragmentEndR - mDataEnd >= static_cast<ptrdiff_t>(size)))
93         {
94             uint8_t *const result = mDataEnd;
95             mDataEnd              = result + size;
96             return result;
97         }
98         return allocateInNewFragment(size);
99     }
100 
getPointer()101     uint8_t *getPointer() const { return mDataEnd; }
getFragmentSize()102     uint32_t getFragmentSize() const
103     {
104         ASSERT(mFragmentEnd >= mDataEnd);
105         return static_cast<uint32_t>(mFragmentEnd - mDataEnd);
106     }
107 
108     RingBufferAllocatorCheckPoint getReleaseCheckPoint() const;
109     void release(const RingBufferAllocatorCheckPoint &checkPoint);
110 
111   private:
112     void release(uint8_t *releasePtr);
113     uint32_t getNumAllocatedInBuffer() const;
114     void resetPointers();
115 
116     uint8_t *allocateInNewFragment(uint32_t size);
117     void resize(uint32_t newCapacity);
118 
119     RingBufferAllocateListener *mListener = nullptr;
120 
121     std::vector<RingBufferAllocatorBuffer> mOldBuffers;
122     RingBufferAllocatorBuffer mBuffer;
123     uint8_t *mDataBegin       = nullptr;
124     uint8_t *mDataEnd         = nullptr;
125     uint8_t *mFragmentEnd     = nullptr;
126     uint8_t *mFragmentEndR    = nullptr;
127     uint32_t mFragmentReserve = 0;
128 
129     uint32_t mMinCapacity      = 0;
130     uint32_t mCurrentCapacity  = 0;
131     uint32_t mAllocationMargin = 0;
132 
133     // 1 - fastest decay speed.
134     // 2 - 2x slower than fastest, and so on.
135     uint32_t mDecaySpeedFactor = 0;
136 };
137 
138 class SharedRingBufferAllocatorCheckPoint final : angle::NonCopyable
139 {
140   public:
141     void releaseAndUpdate(RingBufferAllocatorCheckPoint *newValue);
142 
143   private:
144     friend class SharedRingBufferAllocator;
145     RingBufferAllocatorCheckPoint pop();
146     angle::SimpleMutex mMutex;
147     RingBufferAllocatorCheckPoint mValue;
148 
149 #if defined(ANGLE_ENABLE_ASSERTS)
150     std::atomic<uint32_t> mRefCount{1};
151 #endif
152 };
153 
154 class SharedRingBufferAllocator final : angle::NonCopyable
155 {
156   public:
157     SharedRingBufferAllocator();
158     ~SharedRingBufferAllocator();
159 
get()160     RingBufferAllocator &get() { return mAllocator; }
161 
162     // Once shared - always shared
isShared()163     bool isShared() const { return mSharedCP != nullptr; }
164 
165     // Once acquired must be released with releaseAndUpdate().
166     SharedRingBufferAllocatorCheckPoint *acquireSharedCP();
167     void releaseToSharedCP();
168 
169   private:
170     RingBufferAllocator mAllocator;
171     SharedRingBufferAllocatorCheckPoint *mSharedCP = nullptr;
172 };
173 
174 }  // namespace angle
175 
176 #endif  // COMMON_RING_BUFFER_ALLOCATOR_H_
177