• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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