// // Copyright 2022 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // RingBufferAllocator.h: // Classes used to implement ring buffer allocators. // #ifndef COMMON_RING_BUFFER_ALLOCATOR_H_ #define COMMON_RING_BUFFER_ALLOCATOR_H_ #include "angleutils.h" #include "common/SimpleMutex.h" #include "common/debug.h" #include namespace angle { static constexpr uint32_t kMinRingBufferAllocationCapacity = 1024; static constexpr uint32_t kDefaultDecaySpeedFactor = 10; // Only called from RingBufferAllocator::allocate(). Other function may also change the fragment. class RingBufferAllocateListener { public: virtual void onRingBufferNewFragment() = 0; virtual void onRingBufferFragmentEnd() = 0; protected: ~RingBufferAllocateListener() = default; }; class RingBufferAllocatorCheckPoint final { public: void reset() { *this = {}; } bool valid() const { return (mReleasePtr != nullptr); } private: friend class RingBufferAllocator; uint64_t mBufferId = 0; uint8_t *mReleasePtr = nullptr; }; class RingBufferAllocatorBuffer final { public: static constexpr uint32_t kBaseOffset = alignof(std::max_align_t); void resize(uint32_t size) { mStorage.resize(size + kBaseOffset); } uint8_t *data() { return mStorage.data() + kBaseOffset; } uint8_t *decClamped(uint8_t *ptr, uint32_t offset) const { ASSERT(ptr >= mStorage.data() + kBaseOffset && ptr <= mStorage.data() + mStorage.size()); return ptr - std::min(offset, static_cast(ptr - mStorage.data())); } uint64_t getId() const { return mId; } void resetId() { mId = 0; } void incrementId() { ++mId; } bool isEmpty() const { return mStorage.empty(); } private: uint64_t mId = 0; std::vector mStorage; }; class RingBufferAllocator final : angle::NonCopyable { public: RingBufferAllocator() = default; RingBufferAllocator(RingBufferAllocator &&other); RingBufferAllocator &operator=(RingBufferAllocator &&other); void reset(); bool valid() const { return (getPointer() != nullptr); } void setListener(RingBufferAllocateListener *listener); void setDecaySpeedFactor(uint32_t decaySpeedFactor); void setFragmentReserve(uint32_t reserve); uint8_t *allocate(uint32_t size) { ASSERT(valid()); if (ANGLE_LIKELY(mFragmentEndR - mDataEnd >= static_cast(size))) { uint8_t *const result = mDataEnd; mDataEnd = result + size; return result; } return allocateInNewFragment(size); } uint8_t *getPointer() const { return mDataEnd; } uint32_t getFragmentSize() const { ASSERT(mFragmentEnd >= mDataEnd); return static_cast(mFragmentEnd - mDataEnd); } RingBufferAllocatorCheckPoint getReleaseCheckPoint() const; void release(const RingBufferAllocatorCheckPoint &checkPoint); private: void release(uint8_t *releasePtr); uint32_t getNumAllocatedInBuffer() const; void resetPointers(); uint8_t *allocateInNewFragment(uint32_t size); void resize(uint32_t newCapacity); RingBufferAllocateListener *mListener = nullptr; std::vector mOldBuffers; RingBufferAllocatorBuffer mBuffer; uint8_t *mDataBegin = nullptr; uint8_t *mDataEnd = nullptr; uint8_t *mFragmentEnd = nullptr; uint8_t *mFragmentEndR = nullptr; uint32_t mFragmentReserve = 0; uint32_t mMinCapacity = 0; uint32_t mCurrentCapacity = 0; uint32_t mAllocationMargin = 0; // 1 - fastest decay speed. // 2 - 2x slower than fastest, and so on. uint32_t mDecaySpeedFactor = 0; }; class SharedRingBufferAllocatorCheckPoint final : angle::NonCopyable { public: void releaseAndUpdate(RingBufferAllocatorCheckPoint *newValue); private: friend class SharedRingBufferAllocator; RingBufferAllocatorCheckPoint pop(); angle::SimpleMutex mMutex; RingBufferAllocatorCheckPoint mValue; #if defined(ANGLE_ENABLE_ASSERTS) std::atomic mRefCount{1}; #endif }; class SharedRingBufferAllocator final : angle::NonCopyable { public: SharedRingBufferAllocator(); ~SharedRingBufferAllocator(); RingBufferAllocator &get() { return mAllocator; } // Once shared - always shared bool isShared() const { return mSharedCP != nullptr; } // Once acquired must be released with releaseAndUpdate(). SharedRingBufferAllocatorCheckPoint *acquireSharedCP(); void releaseToSharedCP(); private: RingBufferAllocator mAllocator; SharedRingBufferAllocatorCheckPoint *mSharedCP = nullptr; }; } // namespace angle #endif // COMMON_RING_BUFFER_ALLOCATOR_H_