• 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 <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