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