• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
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 GrDrawOpAtlas_DEFINED
9 #define GrDrawOpAtlas_DEFINED
10 
11 #include <cmath>
12 #include <vector>
13 
14 #include "include/gpu/GrBackendSurface.h"
15 #include "include/private/SkTArray.h"
16 #include "src/core/SkIPoint16.h"
17 #include "src/core/SkTInternalLList.h"
18 #include "src/gpu/GrDeferredUpload.h"
19 #include "src/gpu/GrRectanizerSkyline.h"
20 #include "src/gpu/GrSurfaceProxyView.h"
21 #include "src/gpu/geometry/GrRect.h"
22 
23 class GrOnFlushResourceProvider;
24 class GrProxyProvider;
25 class GrResourceProvider;
26 class GrTextureProxy;
27 
28 /**
29  * This class manages one or more atlas textures on behalf of GrDrawOps. The draw ops that use the
30  * atlas perform texture uploads when preparing their draws during flush. The class provides
31  * facilities for using GrDrawOpUploadToken to detect data hazards. Op's uploads are performed in
32  * "ASAP" mode until it is impossible to add data without overwriting texels read by draws that
33  * have not yet executed on the gpu. At that point, the atlas will attempt to allocate a new
34  * atlas texture (or "page") of the same size, up to a maximum number of textures, and upload
35  * to that texture. If that's not possible, the uploads are performed "inline" between draws. If a
36  * single draw would use enough subimage space to overflow the atlas texture then the atlas will
37  * fail to add a subimage. This gives the op the chance to end the draw and begin a new one.
38  * Additional uploads will then succeed in inline mode.
39  *
40  * When the atlas has multiple pages, new uploads are prioritized to the lower index pages, i.e.,
41  * it will try to upload to page 0 before page 1 or 2. To keep the atlas from continually using
42  * excess space, periodic garbage collection is needed to shift data from the higher index pages to
43  * the lower ones, and then eventually remove any pages that are no longer in use. "In use" is
44  * determined by using the GrDrawUploadToken system: After a flush each subarea of the page
45  * is checked to see whether it was used in that flush; if it is not, a counter is incremented.
46  * Once that counter reaches a threshold that subarea is considered to be no longer in use.
47  *
48  * Garbage collection is initiated by the GrDrawOpAtlas's client via the compact() method. One
49  * solution is to make the client a subclass of GrOnFlushCallbackObject, register it with the
50  * GrContext via addOnFlushCallbackObject(), and the client's postFlush() method calls compact()
51  * and passes in the given GrDrawUploadToken.
52  */
53 class GrDrawOpAtlas {
54 public:
55     /** Is the atlas allowed to use more than one texture? */
56     enum class AllowMultitexturing : bool { kNo, kYes };
57 
58     // These are both restricted by the space they occupy in the PlotLocator.
59     // maxPages is also limited by being crammed into the glyph uvs.
60     // maxPlots is also limited by the fPlotAlreadyUpdated bitfield in BulkUseTokenUpdater
61 #ifdef SK_ENABLE_SMALL_PAGE
62     inline static constexpr auto kMaxMultitexturePages = 16;
63 #else
64     inline static constexpr auto kMaxMultitexturePages = 4;
65 #endif
66     inline static constexpr int kMaxPlots = 32;
67 
68     /**
69      * A PlotLocator specifies the plot and is analogous to a directory path:
70      *    page/plot/plotGeneration
71      *
72      * In fact PlotLocator is a portion of a glyph image location in the atlas fully specified by:
73      *    format/atlasGeneration/page/plot/plotGeneration/rect
74      *
75      * TODO: Remove the small path renderer's use of the PlotLocator for eviction.
76      */
77     class PlotLocator {
78     public:
PlotLocator(uint32_t pageIdx,uint32_t plotIdx,uint64_t generation)79         PlotLocator(uint32_t pageIdx, uint32_t plotIdx, uint64_t generation)
80                 : fGenID(generation)
81                 , fPlotIndex(plotIdx)
82                 , fPageIndex(pageIdx) {
83             SkASSERT(pageIdx < kMaxMultitexturePages);
84             SkASSERT(plotIdx < kMaxPlots);
85             SkASSERT(generation < ((uint64_t)1 << 48));
86         }
87 
PlotLocator()88         PlotLocator() : fGenID(0), fPlotIndex(0), fPageIndex(0) {}
89 
isValid()90         bool isValid() const {
91             return fGenID != 0 || fPlotIndex != 0 || fPageIndex != 0;
92         }
93 
makeInvalid()94         void makeInvalid() {
95             fGenID = 0;
96             fPlotIndex = 0;
97             fPageIndex = 0;
98         }
99 
100         bool operator==(const PlotLocator& other) const {
101             return fGenID == other.fGenID &&
102                    fPlotIndex == other.fPlotIndex &&
103                    fPageIndex == other.fPageIndex; }
104 
pageIndex()105         uint32_t pageIndex() const { return fPageIndex; }
plotIndex()106         uint32_t plotIndex() const { return fPlotIndex; }
genID()107         uint64_t genID() const { return fGenID; }
108 
109     private:
110         uint64_t fGenID:48;
111         uint64_t fPlotIndex:8;
112         uint64_t fPageIndex:8;
113     };
114 
115     static const uint64_t kInvalidAtlasGeneration = 0;
116 
117 
118     // AtlasLocator handles atlas position information. It keeps a left-top, right-bottom pair of
119     // encoded UV coordinates. The bits 13 & 14 of the U coordinates hold the atlas page index.
120     // This information is handed directly as is from fUVs. This encoding has the nice property
121     // that width = fUVs[2] - fUVs[0]; the page encoding in the top bits subtracts to zero.
122     class AtlasLocator {
123     public:
getUVs()124         std::array<uint16_t, 4> getUVs() const {
125             return fUVs;
126         }
127 
invalidatePlotLocator()128         void invalidatePlotLocator() { fPlotLocator.makeInvalid(); }
129 
130         // TODO: Remove the small path renderer's use of this for eviction
plotLocator()131         PlotLocator plotLocator() const { return fPlotLocator; }
132 
pageIndex()133         uint32_t pageIndex() const { return fPlotLocator.pageIndex(); }
134 
plotIndex()135         uint32_t plotIndex() const { return fPlotLocator.plotIndex(); }
136 
genID()137         uint64_t genID() const { return fPlotLocator.genID(); }
138 
topLeft()139         SkIPoint topLeft() const {
140 #ifdef SK_ENABLE_SMALL_PAGE
141             return {fUVs[0] & 0x3FFF, fUVs[1] & 0x3FFF};
142 #else
143             return {fUVs[0] & 0x1FFF, fUVs[1]};
144 #endif
145         }
146 
width()147         uint16_t width() const {
148             return fUVs[2] - fUVs[0];
149         }
150 
height()151         uint16_t height() const {
152             return fUVs[3] - fUVs[1];
153         }
154 
insetSrc(int padding)155         void insetSrc(int padding) {
156             SkASSERT(2 * padding <= this->width());
157             SkASSERT(2 * padding <= this->height());
158 
159             fUVs[0] += padding;
160             fUVs[1] += padding;
161             fUVs[2] -= padding;
162             fUVs[3] -= padding;
163         }
164 
updatePlotLocator(PlotLocator p)165         void updatePlotLocator(PlotLocator p) {
166             fPlotLocator = p;
167 #ifdef SK_ENABLE_SMALL_PAGE
168             SkASSERT(fPlotLocator.pageIndex() <= 15);
169             // package pageIndex(max value is 16, 4bit)
170             // high 2bit save in x-axis(fUVs[0],fUVs[2]),low 2bit save in y-axis(fUVs[1],fUVs[3])
171             uint16_t page = fPlotLocator.pageIndex() << 12;
172             fUVs[0] = (fUVs[0] & 0x3FFF) | (page & 0xC000);
173             fUVs[1] = (fUVs[1] & 0x3FFF) | ((page << 2) & 0xC000);
174             fUVs[2] = (fUVs[2] & 0x3FFF) | (page & 0xC000);
175             fUVs[3] = (fUVs[3] & 0x3FFF) | ((page << 2) & 0xC000);
176 #else
177             SkASSERT(fPlotLocator.pageIndex() <= 3);
178             uint16_t page = fPlotLocator.pageIndex() << 13;
179             fUVs[0] = (fUVs[0] & 0x1FFF) | page;
180             fUVs[2] = (fUVs[2] & 0x1FFF) | page;
181 #endif
182         }
183 
updateRect(GrIRect16 rect)184         void updateRect(GrIRect16 rect) {
185 #ifdef SK_ENABLE_SMALL_PAGE
186             SkASSERT(rect.fLeft <= rect.fRight);
187             SkASSERT(rect.fRight <= 0x3FFF);
188             SkASSERT(rect.fTop <= rect.fBottom);
189             SkASSERT(rect.fBottom <= 0x3FFF);
190             fUVs[0] = (fUVs[0] & 0xC000) | rect.fLeft;
191             fUVs[1] = (fUVs[1] & 0xC000) | rect.fTop;
192             fUVs[2] = (fUVs[2] & 0xC000) | rect.fRight;
193             fUVs[3] = (fUVs[3] & 0xC000) | rect.fBottom;
194 #else
195             SkASSERT(rect.fLeft <= rect.fRight);
196             SkASSERT(rect.fRight <= 0x1FFF);
197             fUVs[0] = (fUVs[0] & 0xE000) | rect.fLeft;
198             fUVs[1] = rect.fTop;
199             fUVs[2] = (fUVs[2] & 0xE000) | rect.fRight;
200             fUVs[3] = rect.fBottom;
201 #endif
202         }
203 
204     private:
205         PlotLocator fPlotLocator{0, 0, 0};
206 
207         // The inset padded bounds in the atlas in the lower 13 bits, and page index in bits 13 &
208         // 14 of the Us.
209         std::array<uint16_t, 4> fUVs{0, 0, 0, 0};
210     };
211 
212     /**
213      * An interface for eviction callbacks. Whenever GrDrawOpAtlas evicts a
214      * specific PlotLocator, it will call all of the registered listeners so they can process the
215      * eviction.
216      */
217     class EvictionCallback {
218     public:
219         virtual ~EvictionCallback() = default;
220         virtual void evict(PlotLocator) = 0;
221     };
222 
223     /**
224      * Keep track of generation number for Atlases and Plots.
225      */
226     class GenerationCounter {
227     public:
228         inline static constexpr uint64_t kInvalidGeneration = 0;
next()229         uint64_t next() {
230             return fGeneration++;
231         }
232 
233     private:
234         uint64_t fGeneration{1};
235     };
236 
237     /**
238      * Returns a GrDrawOpAtlas. This function can be called anywhere, but the returned atlas
239      * should only be used inside of GrMeshDrawOp::onPrepareDraws.
240      *  @param GrColorType      The colorType which this atlas will store
241      *  @param width            width in pixels of the atlas
242      *  @param height           height in pixels of the atlas
243      *  @param numPlotsX        The number of plots the atlas should be broken up into in the X
244      *                          direction
245      *  @param numPlotsY        The number of plots the atlas should be broken up into in the Y
246      *                          direction
247      *  @param atlasGeneration  a pointer to the context's generation counter.
248      *  @param allowMultitexturing Can the atlas use more than one texture.
249      *  @param evictor          A pointer to an eviction callback class.
250      *
251      *  @return                 An initialized GrDrawOpAtlas, or nullptr if creation fails
252      */
253     static std::unique_ptr<GrDrawOpAtlas> Make(GrProxyProvider*,
254                                                const GrBackendFormat& format,
255                                                GrColorType,
256                                                int width, int height,
257                                                int plotWidth, int plotHeight,
258                                                GenerationCounter* generationCounter,
259                                                AllowMultitexturing allowMultitexturing,
260 #ifdef SK_ENABLE_SMALL_PAGE
261                                                int atlasPageNum,
262 #endif
263                                                EvictionCallback* evictor);
264 
265     /**
266      * Adds a width x height subimage to the atlas. Upon success it returns 'kSucceeded' and returns
267      * the ID and the subimage's coordinates in the backing texture. 'kTryAgain' is returned if
268      * the subimage cannot fit in the atlas without overwriting texels that will be read in the
269      * current draw. This indicates that the op should end its current draw and begin another
270      * before adding more data. Upon success, an upload of the provided image data will have
271      * been added to the GrDrawOp::Target, in "asap" mode if possible, otherwise in "inline" mode.
272      * Successive uploads in either mode may be consolidated.
273      * 'kError' will be returned when some unrecoverable error was encountered while trying to
274      * add the subimage. In this case the op being created should be discarded.
275      *
276      * NOTE: When the GrDrawOp prepares a draw that reads from the atlas, it must immediately call
277      * 'setUseToken' with the currentToken from the GrDrawOp::Target, otherwise the next call to
278      * addToAtlas might cause the previous data to be overwritten before it has been read.
279      */
280 
281     enum class ErrorCode {
282         kError,
283         kSucceeded,
284         kTryAgain
285     };
286 
287     ErrorCode addToAtlas(GrResourceProvider*, GrDeferredUploadTarget*,
288                          int width, int height, const void* image, AtlasLocator*);
289 
getViews()290     const GrSurfaceProxyView* getViews() const { return fViews; }
291 
atlasGeneration()292     uint64_t atlasGeneration() const { return fAtlasGeneration; }
293 
hasID(const PlotLocator & plotLocator)294     bool hasID(const PlotLocator& plotLocator) {
295         if (!plotLocator.isValid()) {
296             return false;
297         }
298 
299         uint32_t plot = plotLocator.plotIndex();
300         uint32_t page = plotLocator.pageIndex();
301         uint64_t plotGeneration = fPages[page].fPlotArray[plot]->genID();
302         uint64_t locatorGeneration = plotLocator.genID();
303         return plot < fNumPlots && page < fNumActivePages && plotGeneration == locatorGeneration;
304     }
305 
306     /** To ensure the atlas does not evict a given entry, the client must set the last use token. */
setLastUseToken(const AtlasLocator & atlasLocator,GrDeferredUploadToken token)307     void setLastUseToken(const AtlasLocator& atlasLocator, GrDeferredUploadToken token) {
308         SkASSERT(this->hasID(atlasLocator.plotLocator()));
309         uint32_t plotIdx = atlasLocator.plotIndex();
310         SkASSERT(plotIdx < fNumPlots);
311         uint32_t pageIdx = atlasLocator.pageIndex();
312         SkASSERT(pageIdx < fNumActivePages);
313         Plot* plot = fPages[pageIdx].fPlotArray[plotIdx].get();
314         this->makeMRU(plot, pageIdx);
315         plot->setLastUseToken(token);
316     }
317 
numActivePages()318     uint32_t numActivePages() { return fNumActivePages; }
319 
320     /**
321      * A class which can be handed back to GrDrawOpAtlas for updating last use tokens in bulk.  The
322      * current max number of plots per page the GrDrawOpAtlas can handle is 32. If in the future
323      * this is insufficient then we can move to a 64 bit int.
324      */
325     class BulkUseTokenUpdater {
326     public:
BulkUseTokenUpdater()327         BulkUseTokenUpdater() {
328             memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated));
329         }
BulkUseTokenUpdater(const BulkUseTokenUpdater & that)330         BulkUseTokenUpdater(const BulkUseTokenUpdater& that)
331                 : fPlotsToUpdate(that.fPlotsToUpdate) {
332             memcpy(fPlotAlreadyUpdated, that.fPlotAlreadyUpdated, sizeof(fPlotAlreadyUpdated));
333         }
334 
add(const AtlasLocator & atlasLocator)335         bool add(const AtlasLocator& atlasLocator) {
336             int plotIdx = atlasLocator.plotIndex();
337             int pageIdx = atlasLocator.pageIndex();
338             if (this->find(pageIdx, plotIdx)) {
339                 return false;
340             }
341             this->set(pageIdx, plotIdx);
342             return true;
343         }
344 
reset()345         void reset() {
346             fPlotsToUpdate.reset();
347             memset(fPlotAlreadyUpdated, 0, sizeof(fPlotAlreadyUpdated));
348         }
349 
350         struct PlotData {
PlotDataPlotData351             PlotData(int pageIdx, int plotIdx) : fPageIndex(pageIdx), fPlotIndex(plotIdx) {}
352             uint32_t fPageIndex;
353             uint32_t fPlotIndex;
354         };
355 
356     private:
find(int pageIdx,int index)357         bool find(int pageIdx, int index) const {
358             SkASSERT(index < kMaxPlots);
359             return (fPlotAlreadyUpdated[pageIdx] >> index) & 1;
360         }
361 
set(int pageIdx,int index)362         void set(int pageIdx, int index) {
363             SkASSERT(!this->find(pageIdx, index));
364             fPlotAlreadyUpdated[pageIdx] |= (1 << index);
365             fPlotsToUpdate.push_back(PlotData(pageIdx, index));
366         }
367 
368         inline static constexpr int kMinItems = 4;
369         SkSTArray<kMinItems, PlotData, true> fPlotsToUpdate;
370         uint32_t fPlotAlreadyUpdated[kMaxMultitexturePages]; // TODO: increase this to uint64_t
371                                                              //       to allow more plots per page
372 
373         friend class GrDrawOpAtlas;
374     };
375 
setLastUseTokenBulk(const BulkUseTokenUpdater & updater,GrDeferredUploadToken token)376     void setLastUseTokenBulk(const BulkUseTokenUpdater& updater, GrDeferredUploadToken token) {
377         int count = updater.fPlotsToUpdate.count();
378         for (int i = 0; i < count; i++) {
379             const BulkUseTokenUpdater::PlotData& pd = updater.fPlotsToUpdate[i];
380             // it's possible we've added a plot to the updater and subsequently the plot's page
381             // was deleted -- so we check to prevent a crash
382             if (pd.fPageIndex < fNumActivePages) {
383                 Plot* plot = fPages[pd.fPageIndex].fPlotArray[pd.fPlotIndex].get();
384                 this->makeMRU(plot, pd.fPageIndex);
385                 plot->setLastUseToken(token);
386             }
387         }
388     }
389 
390 #ifdef SK_ENABLE_SMALL_PAGE
setRadicalsCompactFlag(bool isRadicals)391     void setRadicalsCompactFlag(bool isRadicals) { fUseRadicalsCompact = isRadicals; }
392 #endif
393     void compact(GrDeferredUploadToken startTokenForNextFlush);
394 
395     void instantiate(GrOnFlushResourceProvider*);
396 
maxPages()397     uint32_t maxPages() const {
398         return fMaxPages;
399     }
400 
401     int numAllocated_TestingOnly() const;
402     void setMaxPages_TestingOnly(uint32_t maxPages);
403 
404 private:
405     GrDrawOpAtlas(GrProxyProvider*, const GrBackendFormat& format, GrColorType, int width,
406                   int height, int plotWidth, int plotHeight, GenerationCounter* generationCounter,
407 #ifdef SK_ENABLE_SMALL_PAGE
408                   AllowMultitexturing allowMultitexturing, int atlasPageNum);
409 #else
410                   AllowMultitexturing allowMultitexturing);
411 #endif
412 
413     /**
414      * The backing GrTexture for a GrDrawOpAtlas is broken into a spatial grid of Plots. The Plots
415      * keep track of subimage placement via their GrRectanizer. A Plot manages the lifetime of its
416      * data using two tokens, a last use token and a last upload token. Once a Plot is "full" (i.e.
417      * there is no room for the new subimage according to the GrRectanizer), it can no longer be
418      * used unless the last use of the Plot has already been flushed through to the gpu.
419      */
420     class Plot : public SkRefCnt {
421         SK_DECLARE_INTERNAL_LLIST_INTERFACE(Plot);
422 
423     public:
pageIndex()424         uint32_t pageIndex() const { return fPageIndex; }
425 
426         /** plotIndex() is a unique id for the plot relative to the owning GrAtlas and page. */
plotIndex()427         uint32_t plotIndex() const { return fPlotIndex; }
428         /**
429          * genID() is incremented when the plot is evicted due to a atlas spill. It is used to know
430          * if a particular subimage is still present in the atlas.
431          */
genID()432         uint64_t genID() const { return fGenID; }
plotLocator()433         PlotLocator plotLocator() const {
434             SkASSERT(fPlotLocator.isValid());
435             return fPlotLocator;
436         }
437         SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
438 
439         bool addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator);
440 
441         /**
442          * To manage the lifetime of a plot, we use two tokens. We use the last upload token to
443          * know when we can 'piggy back' uploads, i.e. if the last upload hasn't been flushed to
444          * the gpu, we don't need to issue a new upload even if we update the cpu backing store. We
445          * use lastUse to determine when we can evict a plot from the cache, i.e. if the last use
446          * has already flushed through the gpu then we can reuse the plot.
447          */
lastUploadToken()448         GrDeferredUploadToken lastUploadToken() const { return fLastUpload; }
lastUseToken()449         GrDeferredUploadToken lastUseToken() const { return fLastUse; }
setLastUploadToken(GrDeferredUploadToken token)450         void setLastUploadToken(GrDeferredUploadToken token) { fLastUpload = token; }
setLastUseToken(GrDeferredUploadToken token)451         void setLastUseToken(GrDeferredUploadToken token) { fLastUse = token; }
452 
453         void uploadToTexture(GrDeferredTextureUploadWritePixelsFn&, GrTextureProxy*);
454         void resetRects();
455 
flushesSinceLastUsed()456         int flushesSinceLastUsed() { return fFlushesSinceLastUse; }
resetFlushesSinceLastUsed()457         void resetFlushesSinceLastUsed() { fFlushesSinceLastUse = 0; }
incFlushesSinceLastUsed()458         void incFlushesSinceLastUsed() { fFlushesSinceLastUse++; }
459 
460     private:
461         Plot(int pageIndex, int plotIndex, GenerationCounter* generationCounter,
462              int offX, int offY, int width, int height, GrColorType colorType);
463 
464         ~Plot() override;
465 
466         /**
467          * Create a clone of this plot. The cloned plot will take the place of the current plot in
468          * the atlas
469          */
clone()470         Plot* clone() const {
471             return new Plot(
472                 fPageIndex, fPlotIndex, fGenerationCounter, fX, fY, fWidth, fHeight, fColorType);
473         }
474 
475         GrDeferredUploadToken fLastUpload;
476         GrDeferredUploadToken fLastUse;
477         // the number of flushes since this plot has been last used
478         int                   fFlushesSinceLastUse;
479 
480         struct {
481             const uint32_t fPageIndex : 16;
482             const uint32_t fPlotIndex : 16;
483         };
484         GenerationCounter* const fGenerationCounter;
485         uint64_t fGenID;
486         PlotLocator fPlotLocator;
487         unsigned char* fData;
488         const int fWidth;
489         const int fHeight;
490         const int fX;
491         const int fY;
492         GrRectanizerSkyline fRectanizer;
493         const SkIPoint16 fOffset;  // the offset of the plot in the backing texture
494         const GrColorType fColorType;
495         const size_t fBytesPerPixel;
496         SkIRect fDirtyRect;
497         SkDEBUGCODE(bool fDirty);
498 
499         friend class GrDrawOpAtlas;
500 
501         using INHERITED = SkRefCnt;
502     };
503 
504     typedef SkTInternalLList<Plot> PlotList;
505 
506     inline bool updatePlot(GrDeferredUploadTarget*, AtlasLocator*, Plot*);
507 
makeMRU(Plot * plot,int pageIdx)508     inline void makeMRU(Plot* plot, int pageIdx) {
509         if (fPages[pageIdx].fPlotList.head() == plot) {
510             return;
511         }
512 
513         fPages[pageIdx].fPlotList.remove(plot);
514         fPages[pageIdx].fPlotList.addToHead(plot);
515 
516         // No MRU update for pages -- since we will always try to add from
517         // the front and remove from the back there is no need for MRU.
518     }
519 
520     bool uploadToPage(unsigned int pageIdx, GrDeferredUploadTarget*, int width, int height,
521                       const void* image, AtlasLocator*);
522 
523     bool createPages(GrProxyProvider*, GenerationCounter*);
524     bool activateNewPage(GrResourceProvider*);
525     void deactivateLastPage();
526 #ifdef SK_ENABLE_SMALL_PAGE
527     void compactRadicals(GrDeferredUploadToken startTokenForNextFlush);
528 #endif
529 
530     void processEviction(PlotLocator);
processEvictionAndResetRects(Plot * plot)531     inline void processEvictionAndResetRects(Plot* plot) {
532         this->processEviction(plot->plotLocator());
533         plot->resetRects();
534     }
535 
536     GrBackendFormat       fFormat;
537     GrColorType           fColorType;
538     int                   fTextureWidth;
539     int                   fTextureHeight;
540     int                   fPlotWidth;
541     int                   fPlotHeight;
542     unsigned int          fNumPlots;
543 
544     GenerationCounter* const fGenerationCounter;
545     uint64_t                 fAtlasGeneration;
546 
547     // nextTokenToFlush() value at the end of the previous flush
548     GrDeferredUploadToken fPrevFlushToken;
549 
550     // the number of flushes since this atlas has been last used
551     int                   fFlushesSinceLastUse;
552 
553     std::vector<EvictionCallback*> fEvictionCallbacks;
554 
555     struct Page {
556         // allocated array of Plots
557         std::unique_ptr<sk_sp<Plot>[]> fPlotArray;
558         // LRU list of Plots (MRU at head - LRU at tail)
559         PlotList fPlotList;
560     };
561     // proxies kept separate to make it easier to pass them up to client
562     GrSurfaceProxyView fViews[kMaxMultitexturePages];
563     Page fPages[kMaxMultitexturePages];
564     uint32_t fMaxPages;
565 
566     uint32_t fNumActivePages;
567 
568 #ifdef SK_ENABLE_SMALL_PAGE
569     bool fUseRadicalsCompact = false;
570 #endif
571 
572     SkDEBUGCODE(void validate(const AtlasLocator& atlasLocator) const;)
573 };
574 
575 // There are three atlases (A8, 565, ARGB) that are kept in relation with one another. In
576 // general, the A8 dimensions are 2x the 565 and ARGB dimensions with the constraint that an atlas
577 // size will always contain at least one plot. Since the ARGB atlas takes the most space, its
578 // dimensions are used to size the other two atlases.
579 class GrDrawOpAtlasConfig {
580 public:
581     // The capabilities of the GPU define maxTextureSize. The client provides maxBytes, and this
582     // represents the largest they want a single atlas texture to be. Due to multitexturing, we
583     // may expand temporarily to use more space as needed.
584     GrDrawOpAtlasConfig(int maxTextureSize, size_t maxBytes);
585 
586     // For testing only - make minimum sized atlases -- a single plot for ARGB, four for A8
GrDrawOpAtlasConfig()587     GrDrawOpAtlasConfig() : GrDrawOpAtlasConfig(kMaxAtlasDim, 0) {}
588 
589     SkISize atlasDimensions(GrMaskFormat type) const;
590     SkISize plotDimensions(GrMaskFormat type) const;
591 #ifdef SK_ENABLE_SMALL_PAGE
getARGBDimensions()592     SkISize getARGBDimensions(){ return fARGBDimensions; }
593     int resetAsSmallPage();
594 #endif
595 
596 private:
597     // On some systems texture coordinates are represented using half-precision floating point,
598     // which limits the largest atlas dimensions to 2048x2048.
599     // For simplicity we'll use this constraint for all of our atlas textures.
600     // This can be revisited later if we need larger atlases.
601     inline static constexpr int kMaxAtlasDim = 2048;
602 
603     SkISize fARGBDimensions;
604     int     fMaxTextureSize;
605 };
606 
607 #endif
608