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