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