1 //
2 // Copyright 2019 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 // serial_utils:
7 // Utilities for generating unique IDs for resources in ANGLE.
8 //
9
10 #ifndef LIBANGLE_RENDERER_SERIAL_UTILS_H_
11 #define LIBANGLE_RENDERER_SERIAL_UTILS_H_
12
13 #include <array>
14 #include <atomic>
15 #include <limits>
16
17 #include "common/angleutils.h"
18 #include "common/debug.h"
19
20 namespace rx
21 {
22 class ResourceSerial
23 {
24 public:
ResourceSerial()25 constexpr ResourceSerial() : mValue(kDirty) {}
ResourceSerial(uintptr_t value)26 explicit constexpr ResourceSerial(uintptr_t value) : mValue(value) {}
27 constexpr bool operator==(ResourceSerial other) const { return mValue == other.mValue; }
28 constexpr bool operator!=(ResourceSerial other) const { return mValue != other.mValue; }
29
dirty()30 void dirty() { mValue = kDirty; }
clear()31 void clear() { mValue = kEmpty; }
32
valid()33 constexpr bool valid() const { return mValue != kEmpty && mValue != kDirty; }
empty()34 constexpr bool empty() const { return mValue == kEmpty; }
35
36 private:
37 constexpr static uintptr_t kDirty = std::numeric_limits<uintptr_t>::max();
38 constexpr static uintptr_t kEmpty = 0;
39
40 uintptr_t mValue;
41 };
42
43 // Class UniqueSerial defines unique serial number for object identification. It has only
44 // equal/unequal comparison but no greater/smaller comparison. The default constructor creates an
45 // invalid value.
46 class UniqueSerial final
47 {
48 public:
UniqueSerial()49 constexpr UniqueSerial() : mValue(kInvalid) {}
50 constexpr UniqueSerial(const UniqueSerial &other) = default;
51 UniqueSerial &operator=(const UniqueSerial &other) = default;
52
53 constexpr bool operator==(const UniqueSerial &other) const
54 {
55 return mValue != kInvalid && mValue == other.mValue;
56 }
57 constexpr bool operator!=(const UniqueSerial &other) const
58 {
59 return mValue == kInvalid || mValue != other.mValue;
60 }
61
62 // Useful for serialization.
getValue()63 constexpr uint64_t getValue() const { return mValue; }
valid()64 constexpr bool valid() const { return mValue != kInvalid; }
65
66 private:
67 friend class UniqueSerialFactory;
UniqueSerial(uint64_t value)68 constexpr explicit UniqueSerial(uint64_t value) : mValue(value) {}
69 uint64_t mValue;
70 static constexpr uint64_t kInvalid = 0;
71 };
72
73 class UniqueSerialFactory final : angle::NonCopyable
74 {
75 public:
UniqueSerialFactory()76 UniqueSerialFactory() : mSerial(1) {}
77
generate()78 UniqueSerial generate()
79 {
80 uint64_t current = mSerial++;
81 ASSERT(mSerial > current); // Integer overflow
82 return UniqueSerial(current);
83 }
84
85 private:
86 uint64_t mSerial;
87 };
88
89 // Class Serial defines a monotonically increasing serial number that indicates the timeline of
90 // execution.
91 class Serial final
92 {
93 public:
Serial()94 constexpr Serial() : mValue(0) {}
95 constexpr Serial(const Serial &other) = default;
96 Serial &operator=(const Serial &other) = default;
97
Infinite()98 static constexpr Serial Infinite() { return Serial(std::numeric_limits<uint64_t>::max()); }
99
100 constexpr bool operator==(const Serial &other) const { return mValue == other.mValue; }
101 constexpr bool operator!=(const Serial &other) const { return mValue != other.mValue; }
102 constexpr bool operator>(const Serial &other) const { return mValue > other.mValue; }
103 constexpr bool operator>=(const Serial &other) const { return mValue >= other.mValue; }
104 constexpr bool operator<(const Serial &other) const { return mValue < other.mValue; }
105 constexpr bool operator<=(const Serial &other) const { return mValue <= other.mValue; }
106
107 // Useful for serialization.
getValue()108 constexpr uint64_t getValue() const { return mValue; }
109
110 private:
111 friend class AtomicSerialFactory;
112 friend class RangedSerialFactory;
113 friend class AtomicQueueSerial;
Serial(uint64_t value)114 constexpr explicit Serial(uint64_t value) : mValue(value) {}
115 uint64_t mValue;
116 };
117
118 // Defines class to track the queue serial that can be load/store from multiple threads atomically.
119 class alignas(8) AtomicQueueSerial final
120 {
121 public:
122 AtomicQueueSerial &operator=(const Serial &other)
123 {
124 mValue.store(other.mValue, std::memory_order_release);
125 return *this;
126 }
getSerial()127 Serial getSerial() const { return Serial(mValue.load(std::memory_order_consume)); }
128
129 private:
130 static constexpr uint64_t kInvalid = 0;
131 std::atomic<uint64_t> mValue = kInvalid;
132 };
133
134 // Used as default/initial serial
135 static constexpr Serial kZeroSerial = Serial();
136
137 // The factory to generate a serial number within the range [mSerial, mSerial+mCount}
138 class RangedSerialFactory final : angle::NonCopyable
139 {
140 public:
RangedSerialFactory()141 RangedSerialFactory() : mSerial(0), mCount(0) {}
142
reset()143 void reset() { mCount = 0; }
empty()144 bool empty() const { return mCount == 0; }
generate(Serial * serialOut)145 bool generate(Serial *serialOut)
146 {
147 if (mCount > 0)
148 {
149 uint64_t current = mSerial++;
150 ASSERT(mSerial > current); // Integer overflow
151 *serialOut = Serial(current);
152 mCount--;
153 return true;
154 }
155 return false;
156 }
157
158 private:
159 friend class AtomicSerialFactory;
initialize(uint64_t initialSerial,size_t count)160 void initialize(uint64_t initialSerial, size_t count)
161 {
162 mSerial = initialSerial;
163 mCount = count;
164 }
165 uint64_t mSerial;
166 size_t mCount;
167 };
168
169 class AtomicSerialFactory final : angle::NonCopyable
170 {
171 public:
AtomicSerialFactory()172 AtomicSerialFactory() : mSerial(1) {}
173
generate()174 Serial generate()
175 {
176 uint64_t current = mSerial++;
177 ASSERT(mSerial > current); // Integer overflow
178 return Serial(current);
179 }
180
reserve(RangedSerialFactory * rangeFactory,size_t count)181 void reserve(RangedSerialFactory *rangeFactory, size_t count)
182 {
183 uint64_t current = mSerial;
184 mSerial += count;
185 ASSERT(mSerial > current); // Integer overflow
186 rangeFactory->initialize(current, count);
187 }
188
189 private:
190 std::atomic<uint64_t> mSerial;
191 };
192
193 // For backend that supports multiple queue serials, QueueSerial includes a Serial and an index.
194 using SerialIndex = uint32_t;
195 static constexpr SerialIndex kInvalidQueueSerialIndex = SerialIndex(-1);
196
197 class QueueSerial;
198 // Because we release queue index when context becomes non-current, in order to use up all index
199 // count, you will need to have 256 threads each has a context current. This is not a reasonable
200 // usage case.
201 constexpr size_t kMaxQueueSerialIndexCount = 256;
202 // Fixed array of queue serials
203 class AtomicQueueSerialFixedArray final
204 {
205 public:
206 AtomicQueueSerialFixedArray() = default;
207 ~AtomicQueueSerialFixedArray() = default;
208
209 void setQueueSerial(SerialIndex index, Serial serial);
210 void setQueueSerial(const QueueSerial &queueSerial);
fill(Serial serial)211 void fill(Serial serial) { std::fill(mSerials.begin(), mSerials.end(), serial); }
212 Serial operator[](SerialIndex index) const { return mSerials[index].getSerial(); }
size()213 size_t size() const { return mSerials.size(); }
214
215 private:
216 std::array<AtomicQueueSerial, kMaxQueueSerialIndexCount> mSerials;
217 };
218 std::ostream &operator<<(std::ostream &os, const AtomicQueueSerialFixedArray &serials);
219
220 class QueueSerial final
221 {
222 public:
QueueSerial()223 QueueSerial() : mIndex(kInvalidQueueSerialIndex) {}
QueueSerial(SerialIndex index,Serial serial)224 QueueSerial(SerialIndex index, Serial serial) : mIndex(index), mSerial(serial)
225 {
226 ASSERT(index != kInvalidQueueSerialIndex);
227 }
228 constexpr QueueSerial(const QueueSerial &other) = default;
229 QueueSerial &operator=(const QueueSerial &other) = default;
230
231 constexpr bool operator==(const QueueSerial &other) const
232 {
233 return mIndex == other.mIndex && mSerial == other.mSerial;
234 }
235 constexpr bool operator!=(const QueueSerial &other) const
236 {
237 return mIndex != other.mIndex || mSerial != other.mSerial;
238 }
239 constexpr bool operator<(const QueueSerial &other) const
240 {
241 ASSERT(mIndex != kInvalidQueueSerialIndex);
242 ASSERT(mIndex == other.mIndex);
243 return mSerial < other.mSerial;
244 }
245 constexpr bool operator<=(const QueueSerial &other) const
246 {
247 ASSERT(mIndex != kInvalidQueueSerialIndex);
248 ASSERT(mIndex == other.mIndex);
249 return mSerial <= other.mSerial;
250 }
251 constexpr bool operator>(const QueueSerial &other) const
252 {
253 ASSERT(mIndex != kInvalidQueueSerialIndex);
254 ASSERT(mIndex == other.mIndex);
255 return mSerial > other.mSerial;
256 }
257 constexpr bool operator>=(const QueueSerial &other) const
258 {
259 ASSERT(mIndex != kInvalidQueueSerialIndex);
260 ASSERT(mIndex == other.mIndex);
261 return mSerial >= other.mSerial;
262 }
263
264 bool operator>(const AtomicQueueSerialFixedArray &serials) const
265 {
266 ASSERT(mIndex != kInvalidQueueSerialIndex);
267 return mSerial > serials[mIndex];
268 }
269 bool operator<=(const AtomicQueueSerialFixedArray &serials) const
270 {
271 ASSERT(mIndex != kInvalidQueueSerialIndex);
272 return mSerial <= serials[mIndex];
273 }
274
valid()275 constexpr bool valid() const { return mIndex != kInvalidQueueSerialIndex; }
276
getIndex()277 SerialIndex getIndex() const { return mIndex; }
getSerial()278 Serial getSerial() const { return mSerial; }
279
280 private:
281 SerialIndex mIndex;
282 Serial mSerial;
283 };
284 std::ostream &operator<<(std::ostream &os, const QueueSerial &queueSerial);
285
setQueueSerial(SerialIndex index,Serial serial)286 ANGLE_INLINE void AtomicQueueSerialFixedArray::setQueueSerial(SerialIndex index, Serial serial)
287 {
288 ASSERT(index != kInvalidQueueSerialIndex);
289 ASSERT(index < mSerials.size());
290 // Serial can only increase
291 ASSERT(serial > mSerials[index].getSerial());
292 mSerials[index] = serial;
293 }
294
setQueueSerial(const QueueSerial & queueSerial)295 ANGLE_INLINE void AtomicQueueSerialFixedArray::setQueueSerial(const QueueSerial &queueSerial)
296 {
297 setQueueSerial(queueSerial.getIndex(), queueSerial.getSerial());
298 }
299
300 ANGLE_INLINE std::ostream &operator<<(std::ostream &os, const AtomicQueueSerialFixedArray &serials)
301 {
302 // Search for last non-zero index (or 0 if all zeros).
303 SerialIndex lastIndex = serials.size() == 0 ? 0 : static_cast<SerialIndex>(serials.size() - 1);
304 while (lastIndex > 0 && serials[lastIndex].getValue() == 0)
305 {
306 lastIndex--;
307 }
308 os << '{';
309 for (SerialIndex i = 0; i < lastIndex; i++)
310 {
311 os << serials[i].getValue() << ',';
312 }
313 os << serials[lastIndex].getValue() << '}';
314 return os;
315 }
316
317 ANGLE_INLINE std::ostream &operator<<(std::ostream &os, const QueueSerial &queueSerial)
318 {
319 os << '{' << queueSerial.getIndex() << ':' << queueSerial.getSerial().getValue() << '}';
320 return os;
321 }
322 } // namespace rx
323
324 #endif // LIBANGLE_RENDERER_SERIAL_UTILS_H_
325