// // Copyright 2019 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. // // serial_utils: // Utilities for generating unique IDs for resources in ANGLE. // #ifndef LIBANGLE_RENDERER_SERIAL_UTILS_H_ #define LIBANGLE_RENDERER_SERIAL_UTILS_H_ #include #include #include #include "common/angleutils.h" #include "common/debug.h" namespace rx { class ResourceSerial { public: constexpr ResourceSerial() : mValue(kDirty) {} explicit constexpr ResourceSerial(uintptr_t value) : mValue(value) {} constexpr bool operator==(ResourceSerial other) const { return mValue == other.mValue; } constexpr bool operator!=(ResourceSerial other) const { return mValue != other.mValue; } void dirty() { mValue = kDirty; } void clear() { mValue = kEmpty; } constexpr bool valid() const { return mValue != kEmpty && mValue != kDirty; } constexpr bool empty() const { return mValue == kEmpty; } private: constexpr static uintptr_t kDirty = std::numeric_limits::max(); constexpr static uintptr_t kEmpty = 0; uintptr_t mValue; }; // Class UniqueSerial defines unique serial number for object identification. It has only // equal/unequal comparison but no greater/smaller comparison. The default constructor creates an // invalid value. class UniqueSerial final { public: constexpr UniqueSerial() : mValue(kInvalid) {} constexpr UniqueSerial(const UniqueSerial &other) = default; UniqueSerial &operator=(const UniqueSerial &other) = default; constexpr bool operator==(const UniqueSerial &other) const { return mValue != kInvalid && mValue == other.mValue; } constexpr bool operator!=(const UniqueSerial &other) const { return mValue == kInvalid || mValue != other.mValue; } // Useful for serialization. constexpr uint64_t getValue() const { return mValue; } constexpr bool valid() const { return mValue != kInvalid; } private: friend class UniqueSerialFactory; constexpr explicit UniqueSerial(uint64_t value) : mValue(value) {} uint64_t mValue; static constexpr uint64_t kInvalid = 0; }; class UniqueSerialFactory final : angle::NonCopyable { public: UniqueSerialFactory() : mSerial(1) {} UniqueSerial generate() { uint64_t current = mSerial++; ASSERT(mSerial > current); // Integer overflow return UniqueSerial(current); } private: uint64_t mSerial; }; // Class Serial defines a monotonically increasing serial number that indicates the timeline of // execution. class Serial final { public: constexpr Serial() : mValue(0) {} constexpr Serial(const Serial &other) = default; Serial &operator=(const Serial &other) = default; static constexpr Serial Infinite() { return Serial(std::numeric_limits::max()); } constexpr bool operator==(const Serial &other) const { return mValue == other.mValue; } constexpr bool operator!=(const Serial &other) const { return mValue != other.mValue; } constexpr bool operator>(const Serial &other) const { return mValue > other.mValue; } constexpr bool operator>=(const Serial &other) const { return mValue >= other.mValue; } constexpr bool operator<(const Serial &other) const { return mValue < other.mValue; } constexpr bool operator<=(const Serial &other) const { return mValue <= other.mValue; } // Useful for serialization. constexpr uint64_t getValue() const { return mValue; } private: friend class AtomicSerialFactory; friend class RangedSerialFactory; friend class AtomicQueueSerial; constexpr explicit Serial(uint64_t value) : mValue(value) {} uint64_t mValue; }; // Defines class to track the queue serial that can be load/store from multiple threads atomically. class alignas(8) AtomicQueueSerial final { public: AtomicQueueSerial &operator=(const Serial &other) { mValue.store(other.mValue, std::memory_order_release); return *this; } Serial getSerial() const { return Serial(mValue.load(std::memory_order_consume)); } private: static constexpr uint64_t kInvalid = 0; std::atomic mValue = kInvalid; }; // Used as default/initial serial static constexpr Serial kZeroSerial = Serial(); // The factory to generate a serial number within the range [mSerial, mSerial+mCount} class RangedSerialFactory final : angle::NonCopyable { public: RangedSerialFactory() : mSerial(0), mCount(0) {} void reset() { mCount = 0; } bool empty() const { return mCount == 0; } bool generate(Serial *serialOut) { if (mCount > 0) { uint64_t current = mSerial++; ASSERT(mSerial > current); // Integer overflow *serialOut = Serial(current); mCount--; return true; } return false; } private: friend class AtomicSerialFactory; void initialize(uint64_t initialSerial, size_t count) { mSerial = initialSerial; mCount = count; } uint64_t mSerial; size_t mCount; }; class AtomicSerialFactory final : angle::NonCopyable { public: AtomicSerialFactory() : mSerial(1) {} Serial generate() { uint64_t current = mSerial++; ASSERT(mSerial > current); // Integer overflow return Serial(current); } void reserve(RangedSerialFactory *rangeFactory, size_t count) { uint64_t current = mSerial; mSerial += count; ASSERT(mSerial > current); // Integer overflow rangeFactory->initialize(current, count); } private: std::atomic mSerial; }; // For backend that supports multiple queue serials, QueueSerial includes a Serial and an index. using SerialIndex = uint32_t; static constexpr SerialIndex kInvalidQueueSerialIndex = SerialIndex(-1); class QueueSerial; // Because we release queue index when context becomes non-current, in order to use up all index // count, you will need to have 256 threads each has a context current. This is not a reasonable // usage case. constexpr size_t kMaxQueueSerialIndexCount = 256; // Fixed array of queue serials class AtomicQueueSerialFixedArray final { public: AtomicQueueSerialFixedArray() = default; ~AtomicQueueSerialFixedArray() = default; void setQueueSerial(SerialIndex index, Serial serial); void setQueueSerial(const QueueSerial &queueSerial); void fill(Serial serial) { std::fill(mSerials.begin(), mSerials.end(), serial); } Serial operator[](SerialIndex index) const { return mSerials[index].getSerial(); } size_t size() const { return mSerials.size(); } private: std::array mSerials; }; std::ostream &operator<<(std::ostream &os, const AtomicQueueSerialFixedArray &serials); class QueueSerial final { public: QueueSerial() : mIndex(kInvalidQueueSerialIndex) {} QueueSerial(SerialIndex index, Serial serial) : mIndex(index), mSerial(serial) { ASSERT(index != kInvalidQueueSerialIndex); } constexpr QueueSerial(const QueueSerial &other) = default; QueueSerial &operator=(const QueueSerial &other) = default; constexpr bool operator==(const QueueSerial &other) const { return mIndex == other.mIndex && mSerial == other.mSerial; } constexpr bool operator!=(const QueueSerial &other) const { return mIndex != other.mIndex || mSerial != other.mSerial; } constexpr bool operator<(const QueueSerial &other) const { ASSERT(mIndex != kInvalidQueueSerialIndex); ASSERT(mIndex == other.mIndex); return mSerial < other.mSerial; } constexpr bool operator<=(const QueueSerial &other) const { ASSERT(mIndex != kInvalidQueueSerialIndex); ASSERT(mIndex == other.mIndex); return mSerial <= other.mSerial; } constexpr bool operator>(const QueueSerial &other) const { ASSERT(mIndex != kInvalidQueueSerialIndex); ASSERT(mIndex == other.mIndex); return mSerial > other.mSerial; } constexpr bool operator>=(const QueueSerial &other) const { ASSERT(mIndex != kInvalidQueueSerialIndex); ASSERT(mIndex == other.mIndex); return mSerial >= other.mSerial; } bool operator>(const AtomicQueueSerialFixedArray &serials) const { ASSERT(mIndex != kInvalidQueueSerialIndex); return mSerial > serials[mIndex]; } bool operator<=(const AtomicQueueSerialFixedArray &serials) const { ASSERT(mIndex != kInvalidQueueSerialIndex); return mSerial <= serials[mIndex]; } constexpr bool valid() const { return mIndex != kInvalidQueueSerialIndex; } SerialIndex getIndex() const { return mIndex; } Serial getSerial() const { return mSerial; } private: SerialIndex mIndex; Serial mSerial; }; std::ostream &operator<<(std::ostream &os, const QueueSerial &queueSerial); ANGLE_INLINE void AtomicQueueSerialFixedArray::setQueueSerial(SerialIndex index, Serial serial) { ASSERT(index != kInvalidQueueSerialIndex); ASSERT(index < mSerials.size()); // Serial can only increase ASSERT(serial > mSerials[index].getSerial()); mSerials[index] = serial; } ANGLE_INLINE void AtomicQueueSerialFixedArray::setQueueSerial(const QueueSerial &queueSerial) { setQueueSerial(queueSerial.getIndex(), queueSerial.getSerial()); } ANGLE_INLINE std::ostream &operator<<(std::ostream &os, const AtomicQueueSerialFixedArray &serials) { // Search for last non-zero index (or 0 if all zeros). SerialIndex lastIndex = serials.size() == 0 ? 0 : static_cast(serials.size() - 1); while (lastIndex > 0 && serials[lastIndex].getValue() == 0) { lastIndex--; } os << '{'; for (SerialIndex i = 0; i < lastIndex; i++) { os << serials[i].getValue() << ','; } os << serials[lastIndex].getValue() << '}'; return os; } ANGLE_INLINE std::ostream &operator<<(std::ostream &os, const QueueSerial &queueSerial) { os << '{' << queueSerial.getIndex() << ':' << queueSerial.getSerial().getValue() << '}'; return os; } } // namespace rx #endif // LIBANGLE_RENDERER_SERIAL_UTILS_H_