blob: c717a1a9a1dce9c7b3bc67bd60402578df2b03bb [file] [log] [blame]
//
// 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 <atomic>
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<uint32_t>(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<uint8_t> 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<ptrdiff_t>(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<uint32_t>(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<RingBufferAllocatorBuffer> 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<uint32_t> 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_