• 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 #include "src/gpu/graphite/DrawAtlas.h"
9 
10 #include <memory>
11 
12 #include "include/core/SkColorSpace.h"
13 #include "include/gpu/graphite/Recorder.h"
14 #include "include/private/SkColorData.h"
15 #include "include/private/base/SkTPin.h"
16 
17 #include "src/base/SkMathPriv.h"
18 #include "src/core/SkOpts.h"
19 #include "src/core/SkTraceEvent.h"
20 #include "src/gpu/AtlasTypes.h"
21 #include "src/gpu/graphite/Caps.h"
22 #include "src/gpu/graphite/CommandTypes.h"
23 #include "src/gpu/graphite/ContextPriv.h"
24 #include "src/gpu/graphite/RecorderPriv.h"
25 #include "src/gpu/graphite/TextureProxy.h"
26 #include "src/gpu/graphite/UploadTask.h"
27 
28 namespace skgpu::graphite {
29 
30 #if defined(DUMP_ATLAS_DATA)
31 static const constexpr bool kDumpAtlasData = true;
32 #else
33 static const constexpr bool kDumpAtlasData = false;
34 #endif
35 
36 class PlotUploadContext : public ConditionalUploadContext {
37 public:
Make(PlotLocator plotLocator,AtlasToken uploadToken,uint32_t atlasID)38     static std::unique_ptr<ConditionalUploadContext> Make(PlotLocator plotLocator,
39                                                           AtlasToken uploadToken,
40                                                           uint32_t atlasID) {
41         return std::unique_ptr<PlotUploadContext>(new PlotUploadContext(plotLocator,
42                                                                         uploadToken,
43                                                                         atlasID));
44     }
~PlotUploadContext()45     ~PlotUploadContext() override {}
46 
needsUpload(Context * context) const47     bool needsUpload(Context* context) const override {
48         return context->priv().plotUploadTracker()->needsUpload(fPlotLocator,
49                                                                 fUploadToken,
50                                                                 fAtlasID);
51     }
52 
53 private:
PlotUploadContext(PlotLocator plotLocator,AtlasToken uploadToken,uint32_t atlasID)54     PlotUploadContext(PlotLocator plotLocator,
55                       AtlasToken uploadToken,
56                       uint32_t atlasID)
57         : ConditionalUploadContext()
58         , fPlotLocator(plotLocator)
59         , fUploadToken(uploadToken)
60         , fAtlasID(atlasID) {}
61 
62     // identifiers
63     PlotLocator fPlotLocator; // has plot index, page index, and eviction gen ID
64     AtlasToken fUploadToken;
65     uint32_t fAtlasID;
66 };
67 
68 #ifdef SK_DEBUG
validate(const AtlasLocator & atlasLocator) const69 void DrawAtlas::validate(const AtlasLocator& atlasLocator) const {
70     // Verify that the plotIndex stored in the PlotLocator is consistent with the glyph rectangle
71     int numPlotsX = fTextureWidth / fPlotWidth;
72     int numPlotsY = fTextureHeight / fPlotHeight;
73 
74     int plotIndex = atlasLocator.plotIndex();
75     auto topLeft = atlasLocator.topLeft();
76     int plotX = topLeft.x() / fPlotWidth;
77     int plotY = topLeft.y() / fPlotHeight;
78     SkASSERT(plotIndex == (numPlotsY - plotY - 1) * numPlotsX + (numPlotsX - plotX - 1));
79 }
80 #endif
81 
Make(SkColorType colorType,size_t bpp,int width,int height,int plotWidth,int plotHeight,AtlasGenerationCounter * generationCounter,AllowMultitexturing allowMultitexturing,PlotEvictionCallback * evictor,std::string_view label)82 std::unique_ptr<DrawAtlas> DrawAtlas::Make(SkColorType colorType, size_t bpp, int width,
83                                            int height, int plotWidth, int plotHeight,
84                                            AtlasGenerationCounter* generationCounter,
85                                            AllowMultitexturing allowMultitexturing,
86                                            PlotEvictionCallback* evictor,
87                                            std::string_view label) {
88     std::unique_ptr<DrawAtlas> atlas(new DrawAtlas(colorType, bpp, width, height,
89                                                    plotWidth, plotHeight, generationCounter,
90                                                    allowMultitexturing, label));
91 
92     if (evictor != nullptr) {
93         atlas->fEvictionCallbacks.emplace_back(evictor);
94     }
95     return atlas;
96 }
97 
98 ///////////////////////////////////////////////////////////////////////////////
next_id()99 static int32_t next_id() {
100     static std::atomic<int32_t> nextID{1};
101     int32_t id;
102     do {
103         id = nextID.fetch_add(1, std::memory_order_relaxed);
104     } while (id == SK_InvalidGenID);
105     return id;
106 }
DrawAtlas(SkColorType colorType,size_t bpp,int width,int height,int plotWidth,int plotHeight,AtlasGenerationCounter * generationCounter,AllowMultitexturing allowMultitexturing,std::string_view label)107 DrawAtlas::DrawAtlas(SkColorType colorType, size_t bpp, int width, int height,
108                      int plotWidth, int plotHeight, AtlasGenerationCounter* generationCounter,
109                      AllowMultitexturing allowMultitexturing, std::string_view label)
110         : fColorType(colorType)
111         , fBytesPerPixel(bpp)
112         , fTextureWidth(width)
113         , fTextureHeight(height)
114         , fPlotWidth(plotWidth)
115         , fPlotHeight(plotHeight)
116         , fLabel(label)
117         , fAtlasID(next_id())
118         , fGenerationCounter(generationCounter)
119         , fAtlasGeneration(fGenerationCounter->next())
120         , fPrevFlushToken(AtlasToken::InvalidToken())
121         , fFlushesSinceLastUse(0)
122         , fMaxPages(AllowMultitexturing::kYes == allowMultitexturing ?
123                             PlotLocator::kMaxMultitexturePages : 1)
124         , fNumActivePages(0) {
125     int numPlotsX = width/plotWidth;
126     int numPlotsY = height/plotHeight;
127     SkASSERT(numPlotsX * numPlotsY <= PlotLocator::kMaxPlots);
128     SkASSERT(fPlotWidth * numPlotsX == fTextureWidth);
129     SkASSERT(fPlotHeight * numPlotsY == fTextureHeight);
130 
131     fNumPlots = numPlotsX * numPlotsY;
132 
133     this->createPages(generationCounter);
134 }
135 
processEviction(PlotLocator plotLocator)136 inline void DrawAtlas::processEviction(PlotLocator plotLocator) {
137     for (PlotEvictionCallback* evictor : fEvictionCallbacks) {
138         evictor->evict(plotLocator);
139     }
140 
141     fAtlasGeneration = fGenerationCounter->next();
142 }
143 
updatePlot(AtlasLocator * atlasLocator,Plot * plot)144 inline bool DrawAtlas::updatePlot(AtlasLocator* atlasLocator, Plot* plot) {
145     int pageIdx = plot->pageIndex();
146     this->makeMRU(plot, pageIdx);
147 
148     // The actual upload will be created in recordUploads().
149 
150     atlasLocator->updatePlotLocator(plot->plotLocator());
151     SkDEBUGCODE(this->validate(*atlasLocator);)
152     return true;
153 }
154 
addToPage(unsigned int pageIdx,int width,int height,const void * image,AtlasLocator * atlasLocator)155 bool DrawAtlas::addToPage(unsigned int pageIdx, int width, int height, const void* image,
156                           AtlasLocator* atlasLocator) {
157     SkASSERT(fProxies[pageIdx]);
158 
159     // look through all allocated plots for one we can share, in Most Recently Refed order
160     PlotList::Iter plotIter;
161     plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart);
162 
163     for (Plot* plot = plotIter.get(); plot; plot = plotIter.next()) {
164         if (plot->addSubImage(width, height, image, atlasLocator)) {
165             return this->updatePlot(atlasLocator, plot);
166         }
167     }
168 
169     return false;
170 }
171 
recordUploads(UploadList * ul,Recorder * recorder,bool useCachedUploads)172 bool DrawAtlas::recordUploads(UploadList* ul, Recorder* recorder, bool useCachedUploads) {
173     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
174     for (uint32_t pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
175         PlotList::Iter plotIter;
176         plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart);
177         for (Plot* plot = plotIter.get(); plot; plot = plotIter.next()) {
178             if (useCachedUploads || plot->needsUpload()) {
179                 TextureProxy* proxy = fProxies[pageIdx].get();
180                 SkASSERT(proxy);
181 
182                 const void* dataPtr;
183                 SkIRect dstRect;
184                 std::tie(dataPtr, dstRect) = plot->prepareForUpload(useCachedUploads);
185                 if (dstRect.isEmpty()) {
186                     continue;
187                 }
188 
189                 std::vector<MipLevel> levels;
190                 levels.push_back({dataPtr, fBytesPerPixel*fPlotWidth});
191 
192                 plot->setLastUploadToken(recorder->priv().tokenTracker()->nextFlushToken());
193 
194                 auto uploadContext = PlotUploadContext::Make(plot->plotLocator(),
195                                                              plot->lastUploadToken(),
196                                                              fAtlasID);
197 
198                 // Src and dst colorInfo are the same
199                 SkColorInfo colorInfo(fColorType, kUnknown_SkAlphaType, nullptr);
200                 if (!ul->recordUpload(recorder, sk_ref_sp(proxy), colorInfo, colorInfo, levels,
201                                       dstRect, std::move(uploadContext))) {
202                     return false;
203                 }
204             }
205         }
206     }
207     return true;
208 }
209 
210 // Number of atlas-related flushes beyond which we consider a plot to no longer be in use.
211 //
212 // This value is somewhat arbitrary -- the idea is to keep it low enough that
213 // a page with unused plots will get removed reasonably quickly, but allow it
214 // to hang around for a bit in case it's needed. The assumption is that flushes
215 // are rare; i.e., we are not continually refreshing the frame.
216 static constexpr auto kPlotRecentlyUsedCount = 32;
217 static constexpr auto kAtlasRecentlyUsedCount = 128;
218 
addToAtlas(Recorder * recorder,int width,int height,const void * image,AtlasLocator * atlasLocator)219 DrawAtlas::ErrorCode DrawAtlas::addToAtlas(Recorder* recorder,
220                                            int width, int height, const void* image,
221                                            AtlasLocator* atlasLocator) {
222     if (width > fPlotWidth || height > fPlotHeight) {
223         return ErrorCode::kError;
224     }
225 
226     // Look through each page to see if we can upload without having to flush
227     // We prioritize this upload to the first pages, not the most recently used, to make it easier
228     // to remove unused pages in reverse page order.
229     for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
230         if (this->addToPage(pageIdx, width, height, image, atlasLocator)) {
231             return ErrorCode::kSucceeded;
232         }
233     }
234 
235     // If the above fails, then see if the least recently used plot per page has already been
236     // queued for upload if we're at max page allocation, or if the plot has aged out otherwise.
237     // We wait until we've grown to the full number of pages to begin evicting already queued
238     // plots so that we can maximize the opportunity for reuse.
239     // As before we prioritize this upload to the first pages, not the most recently used.
240     if (fNumActivePages == this->maxPages()) {
241         for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
242             Plot* plot = fPages[pageIdx].fPlotList.tail();
243             SkASSERT(plot);
244             if (plot->lastUseToken() < recorder->priv().tokenTracker()->nextFlushToken()) {
245                 this->processEvictionAndResetRects(plot);
246                 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, atlasLocator);
247                 SkASSERT(verify);
248                 if (!this->updatePlot(atlasLocator, plot)) {
249                     return ErrorCode::kError;
250                 }
251                 return ErrorCode::kSucceeded;
252             }
253         }
254     } else {
255         // If we haven't activated all the available pages, try to create a new one and add to it
256         if (!this->activateNewPage(recorder)) {
257             return ErrorCode::kError;
258         }
259 
260         if (this->addToPage(fNumActivePages-1, width, height, image, atlasLocator)) {
261             return ErrorCode::kSucceeded;
262         } else {
263             // If we fail to upload to a newly activated page then something has gone terribly
264             // wrong - return an error
265             return ErrorCode::kError;
266         }
267     }
268 
269     if (!fNumActivePages) {
270         return ErrorCode::kError;
271     }
272 
273     // All plots are currently in use by the current set of draws, so we need to fail. This
274     // gives the Device a chance to snap the current set of uploads and draws, advance the draw
275     // token, and call back into this function. The subsequent call will have plots available
276     // for fresh uploads.
277     return ErrorCode::kTryAgain;
278 }
279 
compact(AtlasToken startTokenForNextFlush)280 void DrawAtlas::compact(AtlasToken startTokenForNextFlush) {
281     if (fNumActivePages < 1) {
282         fPrevFlushToken = startTokenForNextFlush;
283         return;
284     }
285 
286     // For all plots, reset number of flushes since used if used this frame.
287     PlotList::Iter plotIter;
288     bool atlasUsedThisFlush = false;
289     for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
290         plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
291         while (Plot* plot = plotIter.get()) {
292             // Reset number of flushes since used
293             if (plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
294                 plot->resetFlushesSinceLastUsed();
295                 atlasUsedThisFlush = true;
296             }
297 
298             plotIter.next();
299         }
300     }
301 
302     if (atlasUsedThisFlush) {
303         fFlushesSinceLastUse = 0;
304     } else {
305         ++fFlushesSinceLastUse;
306     }
307 
308     // We only try to compact if the atlas was used in the recently completed flush or
309     // hasn't been used in a long time.
310     // This is to handle the case where a lot of text or path rendering has occurred but then just
311     // a blinking cursor is drawn.
312     if (atlasUsedThisFlush || fFlushesSinceLastUse > kAtlasRecentlyUsedCount) {
313         SkTArray<Plot*> availablePlots;
314         uint32_t lastPageIndex = fNumActivePages - 1;
315 
316         // For all plots but the last one, update number of flushes since used, and check to see
317         // if there are any in the first pages that the last page can safely upload to.
318         for (uint32_t pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) {
319             if constexpr (kDumpAtlasData) {
320                 SkDebugf("page %d: ", pageIndex);
321             }
322 
323             plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
324             while (Plot* plot = plotIter.get()) {
325                 // Update number of flushes since plot was last used
326                 // We only increment the 'sinceLastUsed' count for flushes where the atlas was used
327                 // to avoid deleting everything when we return to text drawing in the blinking
328                 // cursor case
329                 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
330                     plot->incFlushesSinceLastUsed();
331                 }
332 
333                 if constexpr (kDumpAtlasData) {
334                     SkDebugf("%d ", plot->flushesSinceLastUsed());
335                 }
336 
337                 // Count plots we can potentially upload to in all pages except the last one
338                 // (the potential compactee).
339                 if (plot->flushesSinceLastUsed() > kPlotRecentlyUsedCount) {
340                     availablePlots.push_back() = plot;
341                 }
342 
343                 plotIter.next();
344             }
345 
346             if constexpr (kDumpAtlasData) {
347                 SkDebugf("\n");
348             }
349         }
350 
351         // Count recently used plots in the last page and evict any that are no longer in use.
352         // Since we prioritize uploading to the first pages, this will eventually
353         // clear out usage of this page unless we have a large need.
354         plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
355         unsigned int usedPlots = 0;
356         if constexpr (kDumpAtlasData) {
357             SkDebugf("page %d: ", lastPageIndex);
358         }
359         while (Plot* plot = plotIter.get()) {
360             // Update number of flushes since plot was last used
361             if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
362                 plot->incFlushesSinceLastUsed();
363             }
364 
365             if constexpr (kDumpAtlasData) {
366                 SkDebugf("%d ", plot->flushesSinceLastUsed());
367             }
368 
369             // If this plot was used recently
370             if (plot->flushesSinceLastUsed() <= kPlotRecentlyUsedCount) {
371                 usedPlots++;
372             } else if (plot->lastUseToken() != AtlasToken::InvalidToken()) {
373                 // otherwise if aged out just evict it.
374                 this->processEvictionAndResetRects(plot);
375             }
376             plotIter.next();
377         }
378 
379         if constexpr (kDumpAtlasData) {
380             SkDebugf("\n");
381         }
382 
383         // If recently used plots in the last page are using less than a quarter of the page, try
384         // to evict them if there's available space in lower index pages. Since we prioritize
385         // uploading to the first pages, this will eventually clear out usage of this page unless
386         // we have a large need.
387         if (availablePlots.size() && usedPlots && usedPlots <= fNumPlots / 4) {
388             plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
389             while (Plot* plot = plotIter.get()) {
390                 // If this plot was used recently
391                 if (plot->flushesSinceLastUsed() <= kPlotRecentlyUsedCount) {
392                     // See if there's room in an lower index page and if so evict.
393                     // We need to be somewhat harsh here so that a handful of plots that are
394                     // consistently in use don't end up locking the page in memory.
395                     if (availablePlots.size() > 0) {
396                         this->processEvictionAndResetRects(plot);
397                         this->processEvictionAndResetRects(availablePlots.back());
398                         availablePlots.pop_back();
399                         --usedPlots;
400                     }
401                     if (!usedPlots || !availablePlots.size()) {
402                         break;
403                     }
404                 }
405                 plotIter.next();
406             }
407         }
408 
409         // If none of the plots in the last page have been used recently, delete it.
410         if (!usedPlots) {
411             if constexpr (kDumpAtlasData) {
412                 SkDebugf("delete %d\n", fNumActivePages-1);
413             }
414 
415             this->deactivateLastPage();
416             fFlushesSinceLastUse = 0;
417         }
418     }
419 
420     fPrevFlushToken = startTokenForNextFlush;
421 }
422 
createPages(AtlasGenerationCounter * generationCounter)423 bool DrawAtlas::createPages(AtlasGenerationCounter* generationCounter) {
424     SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
425 
426     int numPlotsX = fTextureWidth/fPlotWidth;
427     int numPlotsY = fTextureHeight/fPlotHeight;
428 
429     for (uint32_t i = 0; i < this->maxPages(); ++i) {
430         // Proxies are uncreated at first
431         fProxies[i] = nullptr;
432 
433         // set up allocated plots
434         fPages[i].fPlotArray = std::make_unique<sk_sp<Plot>[]>(numPlotsX * numPlotsY);
435 
436         sk_sp<Plot>* currPlot = fPages[i].fPlotArray.get();
437         for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
438             for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
439                 uint32_t plotIndex = r * numPlotsX + c;
440                 currPlot->reset(new Plot(
441                     i, plotIndex, generationCounter, x, y, fPlotWidth, fPlotHeight, fColorType,
442                     fBytesPerPixel));
443 
444                 // build LRU list
445                 fPages[i].fPlotList.addToHead(currPlot->get());
446                 ++currPlot;
447             }
448         }
449 
450     }
451 
452     return true;
453 }
454 
activateNewPage(Recorder * recorder)455 bool DrawAtlas::activateNewPage(Recorder* recorder) {
456     SkASSERT(fNumActivePages < this->maxPages());
457     SkASSERT(!fProxies[fNumActivePages]);
458 
459     auto textureInfo = recorder->priv().caps()->getDefaultSampledTextureInfo(
460             fColorType,
461             /*mipmapped=*/Mipmapped::kNo,
462             Protected::kNo,
463             Renderable::kNo);
464     fProxies[fNumActivePages].reset(
465             new TextureProxy({fTextureWidth, fTextureHeight}, textureInfo, skgpu::Budgeted::kYes));
466     if (!fProxies[fNumActivePages]) {
467         return false;
468     }
469 
470     if constexpr (kDumpAtlasData) {
471         SkDebugf("activated page#: %d\n", fNumActivePages);
472     }
473 
474     ++fNumActivePages;
475     return true;
476 }
477 
deactivateLastPage()478 inline void DrawAtlas::deactivateLastPage() {
479     SkASSERT(fNumActivePages);
480 
481     uint32_t lastPageIndex = fNumActivePages - 1;
482 
483     int numPlotsX = fTextureWidth/fPlotWidth;
484     int numPlotsY = fTextureHeight/fPlotHeight;
485 
486     fPages[lastPageIndex].fPlotList.reset();
487     for (int r = 0; r < numPlotsY; ++r) {
488         for (int c = 0; c < numPlotsX; ++c) {
489             uint32_t plotIndex = r * numPlotsX + c;
490 
491             Plot* currPlot = fPages[lastPageIndex].fPlotArray[plotIndex].get();
492             currPlot->resetRects();
493             currPlot->resetFlushesSinceLastUsed();
494 
495             // rebuild the LRU list
496             SkDEBUGCODE(currPlot->resetListPtrs());
497             fPages[lastPageIndex].fPlotList.addToHead(currPlot);
498         }
499     }
500 
501     // remove ref to the texture proxy
502     fProxies[lastPageIndex].reset();
503     --fNumActivePages;
504 }
505 
evictAllPlots()506 void DrawAtlas::evictAllPlots() {
507     PlotList::Iter plotIter;
508     for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
509         plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
510         while (Plot* plot = plotIter.get()) {
511             this->processEvictionAndResetRects(plot);
512             plotIter.next();
513         }
514     }
515 }
516 
DrawAtlasConfig(int maxTextureSize,size_t maxBytes)517 DrawAtlasConfig::DrawAtlasConfig(int maxTextureSize, size_t maxBytes) {
518     static const SkISize kARGBDimensions[] = {
519         {256, 256},   // maxBytes < 2^19
520         {512, 256},   // 2^19 <= maxBytes < 2^20
521         {512, 512},   // 2^20 <= maxBytes < 2^21
522         {1024, 512},  // 2^21 <= maxBytes < 2^22
523         {1024, 1024}, // 2^22 <= maxBytes < 2^23
524         {2048, 1024}, // 2^23 <= maxBytes
525     };
526 
527     // Index 0 corresponds to maxBytes of 2^18, so start by dividing it by that
528     maxBytes >>= 18;
529     // Take the floor of the log to get the index
530     int index = maxBytes > 0
531         ? SkTPin<int>(SkPrevLog2(maxBytes), 0, std::size(kARGBDimensions) - 1)
532         : 0;
533 
534     SkASSERT(kARGBDimensions[index].width() <= kMaxAtlasDim);
535     SkASSERT(kARGBDimensions[index].height() <= kMaxAtlasDim);
536     fARGBDimensions.set(std::min<int>(kARGBDimensions[index].width(), maxTextureSize),
537                         std::min<int>(kARGBDimensions[index].height(), maxTextureSize));
538     fMaxTextureSize = std::min<int>(maxTextureSize, kMaxAtlasDim);
539 }
540 
atlasDimensions(MaskFormat type) const541 SkISize DrawAtlasConfig::atlasDimensions(MaskFormat type) const {
542     if (MaskFormat::kA8 == type) {
543         // A8 is always 2x the ARGB dimensions, clamped to the max allowed texture size
544         return { std::min<int>(2 * fARGBDimensions.width(), fMaxTextureSize),
545                  std::min<int>(2 * fARGBDimensions.height(), fMaxTextureSize) };
546     } else {
547         return fARGBDimensions;
548     }
549 }
550 
plotDimensions(MaskFormat type) const551 SkISize DrawAtlasConfig::plotDimensions(MaskFormat type) const {
552     if (MaskFormat::kA8 == type) {
553         SkISize atlasDimensions = this->atlasDimensions(type);
554         // For A8 we want to grow the plots at larger texture sizes to accept more of the
555         // larger SDF glyphs. Since the largest SDF glyph can be 170x170 with padding, this
556         // allows us to pack 3 in a 512x256 plot, or 9 in a 512x512 plot.
557 
558         // This will give us 512x256 plots for 2048x1024, 512x512 plots for 2048x2048,
559         // and 256x256 plots otherwise.
560         int plotWidth = atlasDimensions.width() >= 2048 ? 512 : 256;
561         int plotHeight = atlasDimensions.height() >= 2048 ? 512 : 256;
562 
563         return { plotWidth, plotHeight };
564     } else {
565         // ARGB and LCD always use 256x256 plots -- this has been shown to be faster
566         return { 256, 256 };
567     }
568 }
569 
570 ////////////////////////////////////////////////////////////////////////////////////////////
571 
needsUpload(PlotLocator plotLocator,AtlasToken uploadToken,uint32_t atlasID)572 bool PlotUploadTracker::needsUpload(PlotLocator plotLocator,
573                                     AtlasToken uploadToken,
574                                     uint32_t atlasID) {
575     uint32_t key = plotLocator.pageIndex() << 8 | plotLocator.plotIndex();
576 
577     PlotAgeData* ageData = fAtlasData[atlasID].find(key);
578     if (!ageData || ageData->genID != plotLocator.genID() || ageData->uploadToken < uploadToken) {
579         PlotAgeData data{plotLocator.genID(), uploadToken};
580         fAtlasData[atlasID].set(key, data);
581         return true;
582     }
583 
584     return false;
585 }
586 
587 }  // namespace skgpu::graphite
588