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