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