1 /*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #ifndef skgpu_AtlasTypes_DEFINED
9 #define skgpu_AtlasTypes_DEFINED
10
11 #include <array>
12
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/base/SkTArray.h"
18 #include "include/private/base/SkTo.h"
19 #include "src/base/SkTInternalLList.h"
20 #include "src/core/SkIPoint16.h"
21 #include "src/gpu/RectanizerSkyline.h"
22
23 class GrOpFlushState;
24 class TestingUploadTarget;
25 namespace skgpu::graphite { class AtlasManager; }
26
27 /**
28 * This file includes internal types that are used by all of our gpu backends for atlases.
29 */
30
31 namespace skgpu {
32
33 struct IRect16 {
34 int16_t fLeft, fTop, fRight, fBottom;
35
MakeEmptyIRect1636 static IRect16 SK_WARN_UNUSED_RESULT MakeEmpty() {
37 IRect16 r;
38 r.setEmpty();
39 return r;
40 }
41
MakeWHIRect1642 static IRect16 SK_WARN_UNUSED_RESULT MakeWH(int16_t w, int16_t h) {
43 IRect16 r;
44 r.set(0, 0, w, h);
45 return r;
46 }
47
MakeXYWHIRect1648 static IRect16 SK_WARN_UNUSED_RESULT MakeXYWH(int16_t x, int16_t y, int16_t w, int16_t h) {
49 IRect16 r;
50 r.set(x, y, x + w, y + h);
51 return r;
52 }
53
MakeIRect1654 static IRect16 SK_WARN_UNUSED_RESULT Make(const SkIRect& ir) {
55 IRect16 r;
56 r.set(ir);
57 return r;
58 }
59
widthIRect1660 int width() const { return fRight - fLeft; }
heightIRect1661 int height() const { return fBottom - fTop; }
areaIRect1662 int area() const { return this->width() * this->height(); }
isEmptyIRect1663 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
64
setEmptyIRect1665 void setEmpty() { memset(this, 0, sizeof(*this)); }
66
setIRect1667 void set(int16_t left, int16_t top, int16_t right, int16_t bottom) {
68 fLeft = left;
69 fTop = top;
70 fRight = right;
71 fBottom = bottom;
72 }
73
setIRect1674 void set(const SkIRect& r) {
75 fLeft = SkToS16(r.fLeft);
76 fTop = SkToS16(r.fTop);
77 fRight = SkToS16(r.fRight);
78 fBottom = SkToS16(r.fBottom);
79 }
80
offsetIRect1681 void offset(int16_t dx, int16_t dy) {
82 fLeft += dx;
83 fTop += dy;
84 fRight += dx;
85 fBottom += dy;
86 }
87 };
88
89 /**
90 * Formats for masks, used by the font cache. Important that these are 0-based.
91 */
92 enum class MaskFormat : int {
93 kA8, //!< 1-byte per pixel
94 kA565, //!< 2-bytes per pixel, RGB represent 3-channel LCD coverage
95 kARGB, //!< 4-bytes per pixel, color format
96
97 kLast = kARGB
98 };
99 static const int kMaskFormatCount = static_cast<int>(MaskFormat::kLast) + 1;
100
101 /**
102 * Return the number of bytes-per-pixel for the specified mask format.
103 */
MaskFormatBytesPerPixel(MaskFormat format)104 inline constexpr int MaskFormatBytesPerPixel(MaskFormat format) {
105 SkASSERT(static_cast<int>(format) < kMaskFormatCount);
106 // kA8 (0) -> 1
107 // kA565 (1) -> 2
108 // kARGB (2) -> 4
109 static_assert(static_cast<int>(MaskFormat::kA8) == 0, "enum_order_dependency");
110 static_assert(static_cast<int>(MaskFormat::kA565) == 1, "enum_order_dependency");
111 static_assert(static_cast<int>(MaskFormat::kARGB) == 2, "enum_order_dependency");
112
113 return SkTo<int>(1u << static_cast<int>(format));
114 }
115
MaskFormatToColorType(MaskFormat format)116 static constexpr SkColorType MaskFormatToColorType(MaskFormat format) {
117 switch (format) {
118 case MaskFormat::kA8:
119 return kAlpha_8_SkColorType;
120 case MaskFormat::kA565:
121 return kRGB_565_SkColorType;
122 case MaskFormat::kARGB:
123 return kRGBA_8888_SkColorType;
124 }
125 SkUNREACHABLE;
126 }
127
128 /**
129 * Keep track of generation number for atlases and Plots.
130 */
131 class AtlasGenerationCounter {
132 public:
133 inline static constexpr uint64_t kInvalidGeneration = 0;
next()134 uint64_t next() {
135 return fGeneration++;
136 }
137
138 private:
139 uint64_t fGeneration{1};
140 };
141
142 /**
143 * AtlasToken is used to sequence uploads relative to each other and to batches of draws.
144 */
145 class AtlasToken {
146 public:
InvalidToken()147 static AtlasToken InvalidToken() { return AtlasToken(0); }
148
149 AtlasToken(const AtlasToken&) = default;
150 AtlasToken& operator=(const AtlasToken&) = default;
151
152 bool operator==(const AtlasToken& that) const {
153 return fSequenceNumber == that.fSequenceNumber;
154 }
155 bool operator!=(const AtlasToken& that) const { return !(*this == that); }
156 bool operator<(const AtlasToken that) const {
157 return fSequenceNumber < that.fSequenceNumber;
158 }
159 bool operator<=(const AtlasToken that) const {
160 return fSequenceNumber <= that.fSequenceNumber;
161 }
162 bool operator>(const AtlasToken that) const {
163 return fSequenceNumber > that.fSequenceNumber;
164 }
165 bool operator>=(const AtlasToken that) const {
166 return fSequenceNumber >= that.fSequenceNumber;
167 }
168
169 AtlasToken& operator++() {
170 ++fSequenceNumber;
171 return *this;
172 }
173 AtlasToken operator++(int) {
174 auto old = fSequenceNumber;
175 ++fSequenceNumber;
176 return AtlasToken(old);
177 }
178
next()179 AtlasToken next() const { return AtlasToken(fSequenceNumber + 1); }
180
181 /** Is this token in the [start, end] inclusive interval? */
inInterval(const AtlasToken & start,const AtlasToken & end)182 bool inInterval(const AtlasToken& start, const AtlasToken& end) {
183 return *this >= start && *this <= end;
184 }
185
186 private:
187 AtlasToken() = delete;
AtlasToken(uint64_t sequenceNumber)188 explicit AtlasToken(uint64_t sequenceNumber) : fSequenceNumber(sequenceNumber) {}
189 uint64_t fSequenceNumber;
190 };
191
192 /**
193 * The TokenTracker encapsulates the incrementing and distribution of AtlasTokens.
194 */
195 class TokenTracker {
196 public:
197 /**
198 * Gets the token one beyond the last token that has been flushed,
199 * either in GrDrawingManager::flush() or Device::flushPendingWorkToRecorder()
200 */
nextFlushToken()201 AtlasToken nextFlushToken() const { return fCurrentFlushToken.next(); }
202
203 /**
204 * Gets the next draw token. This can be used to record that the next draw
205 * issued will use a resource (e.g. texture) while preparing that draw.
206 * Not used by Graphite.
207 */
nextDrawToken()208 AtlasToken nextDrawToken() const { return fCurrentDrawToken.next(); }
209
210 private:
211 // Only these classes get to increment the token counters
212 friend class ::GrOpFlushState;
213 friend class ::TestingUploadTarget;
214 friend class skgpu::graphite::AtlasManager;
215
216 // Issues the next token for a draw.
issueDrawToken()217 AtlasToken issueDrawToken() { return ++fCurrentDrawToken; }
218
219 // Advances the next token for a flush.
issueFlushToken()220 AtlasToken issueFlushToken() { return ++fCurrentFlushToken; }
221
222 AtlasToken fCurrentDrawToken = AtlasToken::InvalidToken();
223 AtlasToken fCurrentFlushToken = AtlasToken::InvalidToken();
224 };
225
226 /**
227 * A PlotLocator specifies the plot and is analogous to a directory path:
228 * page/plot/plotGeneration
229 *
230 * In fact PlotLocator is a portion of a glyph image location in the atlas fully specified by:
231 * format/atlasGeneration/page/plot/plotGeneration/rect
232 *
233 * TODO: Remove the small path renderer's use of the PlotLocator for eviction.
234 */
235 class PlotLocator {
236 public:
237 // These are both restricted by the space they occupy in the PlotLocator.
238 // maxPages is also limited by being crammed into the glyph uvs.
239 // maxPlots is also limited by the fPlotAlreadyUpdated bitfield in
240 // GrDrawOpAtlas::BulkUseTokenUpdater.
241 inline static constexpr auto kMaxMultitexturePages = 4;
242 inline static constexpr int kMaxPlots = 32;
243
PlotLocator(uint32_t pageIdx,uint32_t plotIdx,uint64_t generation)244 PlotLocator(uint32_t pageIdx, uint32_t plotIdx, uint64_t generation)
245 : fGenID(generation)
246 , fPlotIndex(plotIdx)
247 , fPageIndex(pageIdx) {
248 SkASSERT(pageIdx < kMaxMultitexturePages);
249 SkASSERT(plotIdx < kMaxPlots);
250 SkASSERT(generation < ((uint64_t)1 << 48));
251 }
252
PlotLocator()253 PlotLocator()
254 : fGenID(AtlasGenerationCounter::kInvalidGeneration)
255 , fPlotIndex(0)
256 , fPageIndex(0) {}
257
isValid()258 bool isValid() const {
259 return fGenID != AtlasGenerationCounter::kInvalidGeneration ||
260 fPlotIndex != 0 || fPageIndex != 0;
261 }
262
makeInvalid()263 void makeInvalid() {
264 fGenID = AtlasGenerationCounter::kInvalidGeneration;
265 fPlotIndex = 0;
266 fPageIndex = 0;
267 }
268
269 bool operator==(const PlotLocator& other) const {
270 return fGenID == other.fGenID &&
271 fPlotIndex == other.fPlotIndex &&
272 fPageIndex == other.fPageIndex; }
273
pageIndex()274 uint32_t pageIndex() const { return fPageIndex; }
plotIndex()275 uint32_t plotIndex() const { return fPlotIndex; }
genID()276 uint64_t genID() const { return fGenID; }
277
278 private:
279 uint64_t fGenID:48;
280 uint64_t fPlotIndex:8;
281 uint64_t fPageIndex:8;
282 };
283
284 // AtlasLocator handles atlas position information. It keeps a left-top, right-bottom pair of
285 // encoded UV coordinates. The bits 13 & 14 of the U coordinates hold the atlas page index.
286 // This information is handed directly as is from fUVs. This encoding has the nice property
287 // that width = fUVs[2] - fUVs[0]; the page encoding in the top bits subtracts to zero.
288 class AtlasLocator {
289 public:
getUVs()290 std::array<uint16_t, 4> getUVs() const {
291 return fUVs;
292 }
293
invalidatePlotLocator()294 void invalidatePlotLocator() { fPlotLocator.makeInvalid(); }
295
296 // TODO: Remove the small path renderer's use of this for eviction
plotLocator()297 PlotLocator plotLocator() const { return fPlotLocator; }
298
pageIndex()299 uint32_t pageIndex() const { return fPlotLocator.pageIndex(); }
300
plotIndex()301 uint32_t plotIndex() const { return fPlotLocator.plotIndex(); }
302
genID()303 uint64_t genID() const { return fPlotLocator.genID(); }
304
topLeft()305 SkIPoint topLeft() const {
306 return {fUVs[0] & 0x1FFF, fUVs[1]};
307 }
308
widthHeight()309 SkPoint widthHeight() const {
310 auto width = fUVs[2] - fUVs[0],
311 height = fUVs[3] - fUVs[1];
312 return SkPoint::Make(width, height);
313 }
314
width()315 uint16_t width() const {
316 return fUVs[2] - fUVs[0];
317 }
318
height()319 uint16_t height() const {
320 return fUVs[3] - fUVs[1];
321 }
322
insetSrc(int padding)323 void insetSrc(int padding) {
324 SkASSERT(2 * padding <= this->width());
325 SkASSERT(2 * padding <= this->height());
326
327 fUVs[0] += padding;
328 fUVs[1] += padding;
329 fUVs[2] -= padding;
330 fUVs[3] -= padding;
331 }
332
updatePlotLocator(PlotLocator p)333 void updatePlotLocator(PlotLocator p) {
334 fPlotLocator = p;
335 SkASSERT(fPlotLocator.pageIndex() <= 3);
336 uint16_t page = fPlotLocator.pageIndex() << 13;
337 fUVs[0] = (fUVs[0] & 0x1FFF) | page;
338 fUVs[2] = (fUVs[2] & 0x1FFF) | page;
339 }
340
updateRect(skgpu::IRect16 rect)341 void updateRect(skgpu::IRect16 rect) {
342 SkASSERT(rect.fLeft <= rect.fRight);
343 SkASSERT(rect.fRight <= 0x1FFF);
344 fUVs[0] = (fUVs[0] & 0xE000) | rect.fLeft;
345 fUVs[1] = rect.fTop;
346 fUVs[2] = (fUVs[2] & 0xE000) | rect.fRight;
347 fUVs[3] = rect.fBottom;
348 }
349
350 private:
351 PlotLocator fPlotLocator{0, 0, 0};
352
353 // The inset padded bounds in the atlas in the lower 13 bits, and page index in bits 13 &
354 // 14 of the Us.
355 std::array<uint16_t, 4> fUVs{0, 0, 0, 0};
356 };
357
358 /**
359 * An interface for eviction callbacks. Whenever an atlas evicts a specific PlotLocator,
360 * it will call all of the registered listeners so they can process the eviction.
361 */
362 class PlotEvictionCallback {
363 public:
364 virtual ~PlotEvictionCallback() = default;
365 virtual void evict(PlotLocator) = 0;
366 };
367
368 /**
369 * A class which can be handed back to an atlas for updating plots in bulk. The
370 * current max number of plots per page an atlas can handle is 32. If in the future
371 * this is insufficient then we can move to a 64 bit int.
372 */
373 class BulkUsePlotUpdater {
374 public:
BulkUsePlotUpdater()375 BulkUsePlotUpdater() {
376 memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated));
377 }
BulkUsePlotUpdater(const BulkUsePlotUpdater & that)378 BulkUsePlotUpdater(const BulkUsePlotUpdater& that)
379 : fPlotsToUpdate(that.fPlotsToUpdate) {
380 memcpy(fPlotAlreadyUpdated, that.fPlotAlreadyUpdated, sizeof(fPlotAlreadyUpdated));
381 }
382
add(const skgpu::AtlasLocator & atlasLocator)383 bool add(const skgpu::AtlasLocator& atlasLocator) {
384 int plotIdx = atlasLocator.plotIndex();
385 int pageIdx = atlasLocator.pageIndex();
386 if (this->find(pageIdx, plotIdx)) {
387 return false;
388 }
389 this->set(pageIdx, plotIdx);
390 return true;
391 }
392
reset()393 void reset() {
394 fPlotsToUpdate.clear();
395 memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated));
396 }
397
398 struct PlotData {
PlotDataPlotData399 PlotData(int pageIdx, int plotIdx) : fPageIndex(pageIdx), fPlotIndex(plotIdx) {}
400 uint32_t fPageIndex;
401 uint32_t fPlotIndex;
402 };
403
count()404 int count() const { return fPlotsToUpdate.size(); }
405
plotData(int index)406 const PlotData& plotData(int index) const { return fPlotsToUpdate[index]; }
407
408 private:
find(int pageIdx,int index)409 bool find(int pageIdx, int index) const {
410 SkASSERT(index < skgpu::PlotLocator::kMaxPlots);
411 return (fPlotAlreadyUpdated[pageIdx] >> index) & 1;
412 }
413
set(int pageIdx,int index)414 void set(int pageIdx, int index) {
415 SkASSERT(!this->find(pageIdx, index));
416 fPlotAlreadyUpdated[pageIdx] |= (1 << index);
417 fPlotsToUpdate.push_back(PlotData(pageIdx, index));
418 }
419
420 inline static constexpr int kMinItems = 4;
421 SkSTArray<kMinItems, PlotData, true> fPlotsToUpdate;
422 // TODO: increase this to uint64_t to allow more plots per page
423 uint32_t fPlotAlreadyUpdated[skgpu::PlotLocator::kMaxMultitexturePages];
424 };
425
426 /**
427 * The backing texture for an atlas is broken into a spatial grid of Plots. The Plots
428 * keep track of subimage placement via their Rectanizer. A Plot may be subclassed if
429 * the atlas class needs to track additional information.
430 */
431 class Plot : public SkRefCnt {
432 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot);
433
434 public:
435 Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCounter,
436 int offX, int offY, int width, int height, SkColorType colorType, size_t bpp);
437
pageIndex()438 uint32_t pageIndex() const { return fPageIndex; }
439
440 /** plotIndex() is a unique id for the plot relative to the owning GrAtlas and page. */
plotIndex()441 uint32_t plotIndex() const { return fPlotIndex; }
442 /**
443 * genID() is incremented when the plot is evicted due to a atlas spill. It is used to
444 * know if a particular subimage is still present in the atlas.
445 */
genID()446 uint64_t genID() const { return fGenID; }
plotLocator()447 PlotLocator plotLocator() const {
448 SkASSERT(fPlotLocator.isValid());
449 return fPlotLocator;
450 }
451 SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
452
453 bool addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator);
454
455 /**
456 * To manage the lifetime of a plot, we use two tokens. We use the last upload token to
457 * know when we can 'piggy back' uploads, i.e. if the last upload hasn't been flushed to
458 * the gpu, we don't need to issue a new upload even if we update the cpu backing store. We
459 * use lastUse to determine when we can evict a plot from the cache, i.e. if the last use
460 * has already flushed through the gpu then we can reuse the plot.
461 */
lastUploadToken()462 skgpu::AtlasToken lastUploadToken() const { return fLastUpload; }
lastUseToken()463 skgpu::AtlasToken lastUseToken() const { return fLastUse; }
setLastUploadToken(skgpu::AtlasToken token)464 void setLastUploadToken(skgpu::AtlasToken token) { fLastUpload = token; }
setLastUseToken(skgpu::AtlasToken token)465 void setLastUseToken(skgpu::AtlasToken token) { fLastUse = token; }
466
flushesSinceLastUsed()467 int flushesSinceLastUsed() { return fFlushesSinceLastUse; }
resetFlushesSinceLastUsed()468 void resetFlushesSinceLastUsed() { fFlushesSinceLastUse = 0; }
incFlushesSinceLastUsed()469 void incFlushesSinceLastUsed() { fFlushesSinceLastUse++; }
470
needsUpload()471 bool needsUpload() { return !fDirtyRect.isEmpty(); }
472 std::pair<const void*, SkIRect> prepareForUpload(bool useCachedUploads);
473 void resetRects();
474
475 /**
476 * Create a clone of this plot. The cloned plot will take the place of the current plot in
477 * the atlas
478 */
clone()479 sk_sp<Plot> clone() const {
480 return sk_sp<Plot>(new Plot(
481 fPageIndex, fPlotIndex, fGenerationCounter, fX, fY, fWidth, fHeight, fColorType,
482 fBytesPerPixel));
483 }
484
485 #ifdef SK_DEBUG
resetListPtrs()486 void resetListPtrs() {
487 fPrev = fNext = nullptr;
488 fList = nullptr;
489 }
490 #endif
491
492 private:
493 ~Plot() override;
494
495 skgpu::AtlasToken fLastUpload;
496 skgpu::AtlasToken fLastUse;
497 int fFlushesSinceLastUse;
498
499 struct {
500 const uint32_t fPageIndex : 16;
501 const uint32_t fPlotIndex : 16;
502 };
503 AtlasGenerationCounter* const fGenerationCounter;
504 uint64_t fGenID;
505 PlotLocator fPlotLocator;
506 unsigned char* fData;
507 const int fWidth;
508 const int fHeight;
509 const int fX;
510 const int fY;
511 skgpu::RectanizerSkyline fRectanizer;
512 const SkIPoint16 fOffset; // the offset of the plot in the backing texture
513 const SkColorType fColorType;
514 const size_t fBytesPerPixel;
515 SkIRect fDirtyRect; // area in the Plot that needs to be uploaded
516 SkIRect fCachedRect; // area in the Plot that has already been uploaded
517 SkDEBUGCODE(bool fDirty);
518 };
519
520 typedef SkTInternalLList<Plot> PlotList;
521
522 } // namespace skgpu
523
524 #endif // skgpu_AtlasTypes_DEFINED
525