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 #include "GrDrawOpAtlas.h"
9
10 #include "GrContext.h"
11 #include "GrContextPriv.h"
12 #include "GrOnFlushResourceProvider.h"
13 #include "GrOpFlushState.h"
14 #include "GrRectanizer.h"
15 #include "GrProxyProvider.h"
16 #include "GrResourceProvider.h"
17 #include "GrSurfaceProxyPriv.h"
18 #include "GrTexture.h"
19 #include "GrTracing.h"
20
21 // When proxy allocation is deferred until flush time the proxies acting as atlases require
22 // special handling. This is because the usage that can be determined from the ops themselves
23 // isn't sufficient. Independent of the ops there will be ASAP and inline uploads to the
24 // atlases. Extending the usage interval of any op that uses an atlas to the start of the
25 // flush (as is done for proxies that are used for sw-generated masks) also won't work because
26 // the atlas persists even beyond the last use in an op - for a given flush. Given this, atlases
27 // must explicitly manage the lifetime of their backing proxies via the onFlushCallback system
28 // (which calls this method).
instantiate(GrOnFlushResourceProvider * onFlushResourceProvider)29 void GrDrawOpAtlas::instantiate(GrOnFlushResourceProvider* onFlushResourceProvider) {
30 for (uint32_t i = 0; i < fNumActivePages; ++i) {
31 // All the atlas pages are now instantiated at flush time in the activeNewPage method.
32 SkASSERT(fProxies[i] && fProxies[i]->priv().isInstantiated());
33 }
34 }
35
Make(GrProxyProvider * proxyProvider,GrPixelConfig config,int width,int height,int numPlotsX,int numPlotsY,AllowMultitexturing allowMultitexturing,GrDrawOpAtlas::EvictionFunc func,void * data)36 std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvider,
37 GrPixelConfig config, int width,
38 int height, int numPlotsX, int numPlotsY,
39 AllowMultitexturing allowMultitexturing,
40 GrDrawOpAtlas::EvictionFunc func, void* data) {
41 std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, config, width, height,
42 numPlotsX, numPlotsY,
43 allowMultitexturing));
44 if (!atlas->getProxies()[0]) {
45 return nullptr;
46 }
47
48 atlas->registerEvictionCallback(func, data);
49 return atlas;
50 }
51
52 #ifdef DUMP_ATLAS_DATA
53 static bool gDumpAtlasData = false;
54 #endif
55
56 ////////////////////////////////////////////////////////////////////////////////
Plot(int pageIndex,int plotIndex,uint64_t genID,int offX,int offY,int width,int height,GrPixelConfig config)57 GrDrawOpAtlas::Plot::Plot(int pageIndex, int plotIndex, uint64_t genID, int offX, int offY,
58 int width, int height, GrPixelConfig config)
59 : fLastUpload(GrDeferredUploadToken::AlreadyFlushedToken())
60 , fLastUse(GrDeferredUploadToken::AlreadyFlushedToken())
61 , fFlushesSinceLastUse(0)
62 , fPageIndex(pageIndex)
63 , fPlotIndex(plotIndex)
64 , fGenID(genID)
65 , fID(CreateId(fPageIndex, fPlotIndex, fGenID))
66 , fData(nullptr)
67 , fWidth(width)
68 , fHeight(height)
69 , fX(offX)
70 , fY(offY)
71 , fRects(nullptr)
72 , fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
73 , fConfig(config)
74 , fBytesPerPixel(GrBytesPerPixel(config))
75 #ifdef SK_DEBUG
76 , fDirty(false)
77 #endif
78 {
79 // We expect the allocated dimensions to be a multiple of 4 bytes
80 SkASSERT(((width*fBytesPerPixel) & 0x3) == 0);
81 // The padding for faster uploads only works for 1, 2 and 4 byte texels
82 SkASSERT(fBytesPerPixel != 3 && fBytesPerPixel <= 4);
83 fDirtyRect.setEmpty();
84 }
85
~Plot()86 GrDrawOpAtlas::Plot::~Plot() {
87 sk_free(fData);
88 delete fRects;
89 }
90
addSubImage(int width,int height,const void * image,SkIPoint16 * loc)91 bool GrDrawOpAtlas::Plot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
92 SkASSERT(width <= fWidth && height <= fHeight);
93
94 if (!fRects) {
95 fRects = GrRectanizer::Factory(fWidth, fHeight);
96 }
97
98 if (!fRects->addRect(width, height, loc)) {
99 return false;
100 }
101
102 if (!fData) {
103 fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
104 fHeight));
105 }
106 size_t rowBytes = width * fBytesPerPixel;
107 const unsigned char* imagePtr = (const unsigned char*)image;
108 // point ourselves at the right starting spot
109 unsigned char* dataPtr = fData;
110 dataPtr += fBytesPerPixel * fWidth * loc->fY;
111 dataPtr += fBytesPerPixel * loc->fX;
112 // copy into the data buffer, swizzling as we go if this is ARGB data
113 if (4 == fBytesPerPixel && kSkia8888_GrPixelConfig == kBGRA_8888_GrPixelConfig) {
114 for (int i = 0; i < height; ++i) {
115 SkOpts::RGBA_to_BGRA(reinterpret_cast<uint32_t*>(dataPtr), imagePtr, width);
116 dataPtr += fBytesPerPixel * fWidth;
117 imagePtr += rowBytes;
118 }
119 } else {
120 for (int i = 0; i < height; ++i) {
121 memcpy(dataPtr, imagePtr, rowBytes);
122 dataPtr += fBytesPerPixel * fWidth;
123 imagePtr += rowBytes;
124 }
125 }
126
127 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
128
129 loc->fX += fOffset.fX;
130 loc->fY += fOffset.fY;
131 SkDEBUGCODE(fDirty = true;)
132
133 return true;
134 }
135
uploadToTexture(GrDeferredTextureUploadWritePixelsFn & writePixels,GrTextureProxy * proxy)136 void GrDrawOpAtlas::Plot::uploadToTexture(GrDeferredTextureUploadWritePixelsFn& writePixels,
137 GrTextureProxy* proxy) {
138 // We should only be issuing uploads if we are in fact dirty
139 SkASSERT(fDirty && fData && proxy && proxy->priv().peekTexture());
140 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
141 size_t rowBytes = fBytesPerPixel * fWidth;
142 const unsigned char* dataPtr = fData;
143 // Clamp to 4-byte aligned boundaries
144 unsigned int clearBits = 0x3 / fBytesPerPixel;
145 fDirtyRect.fLeft &= ~clearBits;
146 fDirtyRect.fRight += clearBits;
147 fDirtyRect.fRight &= ~clearBits;
148 SkASSERT(fDirtyRect.fRight <= fWidth);
149 // Set up dataPtr
150 dataPtr += rowBytes * fDirtyRect.fTop;
151 dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
152 // TODO: Make GrDrawOpAtlas store a GrColorType rather than GrPixelConfig.
153 auto colorType = GrPixelConfigToColorType(fConfig);
154 writePixels(proxy, fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
155 fDirtyRect.width(), fDirtyRect.height(), colorType, dataPtr, rowBytes);
156 fDirtyRect.setEmpty();
157 SkDEBUGCODE(fDirty = false;)
158 }
159
resetRects()160 void GrDrawOpAtlas::Plot::resetRects() {
161 if (fRects) {
162 fRects->reset();
163 }
164
165 fGenID++;
166 fID = CreateId(fPageIndex, fPlotIndex, fGenID);
167 fLastUpload = GrDeferredUploadToken::AlreadyFlushedToken();
168 fLastUse = GrDeferredUploadToken::AlreadyFlushedToken();
169
170 // zero out the plot
171 if (fData) {
172 sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
173 }
174
175 fDirtyRect.setEmpty();
176 SkDEBUGCODE(fDirty = false;)
177 }
178
179 ///////////////////////////////////////////////////////////////////////////////
180
GrDrawOpAtlas(GrProxyProvider * proxyProvider,GrPixelConfig config,int width,int height,int numPlotsX,int numPlotsY,AllowMultitexturing allowMultitexturing)181 GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider,
182 GrPixelConfig config, int width, int height,
183 int numPlotsX, int numPlotsY, AllowMultitexturing allowMultitexturing)
184 : fPixelConfig(config)
185 , fTextureWidth(width)
186 , fTextureHeight(height)
187 , fAtlasGeneration(kInvalidAtlasGeneration + 1)
188 , fPrevFlushToken(GrDeferredUploadToken::AlreadyFlushedToken())
189 , fAllowMultitexturing(allowMultitexturing)
190 , fNumActivePages(0) {
191 fPlotWidth = fTextureWidth / numPlotsX;
192 fPlotHeight = fTextureHeight / numPlotsY;
193 SkASSERT(numPlotsX * numPlotsY <= BulkUseTokenUpdater::kMaxPlots);
194 SkASSERT(fPlotWidth * numPlotsX == fTextureWidth);
195 SkASSERT(fPlotHeight * numPlotsY == fTextureHeight);
196
197 fNumPlots = numPlotsX * numPlotsY;
198
199 this->createPages(proxyProvider);
200 }
201
processEviction(AtlasID id)202 inline void GrDrawOpAtlas::processEviction(AtlasID id) {
203 for (int i = 0; i < fEvictionCallbacks.count(); i++) {
204 (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
205 }
206 ++fAtlasGeneration;
207 }
208
updatePlot(GrDeferredUploadTarget * target,AtlasID * id,Plot * plot)209 inline bool GrDrawOpAtlas::updatePlot(GrDeferredUploadTarget* target, AtlasID* id, Plot* plot) {
210 int pageIdx = GetPageIndexFromID(plot->id());
211 this->makeMRU(plot, pageIdx);
212
213 // If our most recent upload has already occurred then we have to insert a new
214 // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
215 // This new update will piggy back on that previously scheduled update.
216 if (plot->lastUploadToken() < target->tokenTracker()->nextTokenToFlush()) {
217 // With c+14 we could move sk_sp into lamba to only ref once.
218 sk_sp<Plot> plotsp(SkRef(plot));
219
220 GrTextureProxy* proxy = fProxies[pageIdx].get();
221 SkASSERT(proxy->priv().isInstantiated()); // This is occurring at flush time
222
223 GrDeferredUploadToken lastUploadToken = target->addASAPUpload(
224 [plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
225 plotsp->uploadToTexture(writePixels, proxy);
226 });
227 plot->setLastUploadToken(lastUploadToken);
228 }
229 *id = plot->id();
230 return true;
231 }
232
233 // Number of atlas-related flushes beyond which we consider a plot to no longer be in use.
234 //
235 // This value is somewhat arbitrary -- the idea is to keep it low enough that
236 // a page with unused plots will get removed reasonably quickly, but allow it
237 // to hang around for a bit in case it's needed. The assumption is that flushes
238 // are rare; i.e., we are not continually refreshing the frame.
239 static constexpr auto kRecentlyUsedCount = 256;
240
addToAtlas(GrResourceProvider * resourceProvider,AtlasID * id,GrDeferredUploadTarget * target,int width,int height,const void * image,SkIPoint16 * loc)241 bool GrDrawOpAtlas::addToAtlas(GrResourceProvider* resourceProvider,
242 AtlasID* id, GrDeferredUploadTarget* target,
243 int width, int height, const void* image, SkIPoint16* loc) {
244 if (width > fPlotWidth || height > fPlotHeight) {
245 return false;
246 }
247
248 // Look through each page to see if we can upload without having to flush
249 // We prioritize this upload to the first pages, not the most recently used, to make it easier
250 // to remove unused pages in reverse page order.
251 for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
252 SkASSERT(fProxies[pageIdx]);
253 // look through all allocated plots for one we can share, in Most Recently Refed order
254 PlotList::Iter plotIter;
255 plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart);
256 Plot* plot;
257 while ((plot = plotIter.get())) {
258 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
259 if (plot->addSubImage(width, height, image, loc)) {
260 return this->updatePlot(target, id, plot);
261 }
262 plotIter.next();
263 }
264 }
265
266 // If the above fails, then see if the least recently used plot per page has already been
267 // flushed to the gpu if we're at max page allocation, or if the plot has aged out otherwise.
268 // We wait until we've grown to the full number of pages to begin evicting already flushed
269 // plots so that we can maximize the opportunity for reuse.
270 // As before we prioritize this upload to the first pages, not the most recently used.
271 for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
272 Plot* plot = fPages[pageIdx].fPlotList.tail();
273 SkASSERT(plot);
274 if ((fNumActivePages == this->maxPages() &&
275 plot->lastUseToken() < target->tokenTracker()->nextTokenToFlush()) ||
276 plot->flushesSinceLastUsed() >= kRecentlyUsedCount) {
277 this->processEvictionAndResetRects(plot);
278 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
279 SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc);
280 SkASSERT(verify);
281 if (!this->updatePlot(target, id, plot)) {
282 return false;
283 }
284 return true;
285 }
286 }
287
288 // If the simple cases fail, try to create a new page and add to it
289 if (this->activateNewPage(resourceProvider)) {
290 unsigned int pageIdx = fNumActivePages-1;
291 SkASSERT(fProxies[pageIdx] && fProxies[pageIdx]->priv().isInstantiated());
292
293 Plot* plot = fPages[pageIdx].fPlotList.head();
294 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
295 if (plot->addSubImage(width, height, image, loc)) {
296 return this->updatePlot(target, id, plot);
297 }
298
299 // we shouldn't get here -- if so, something has gone terribly wrong
300 SkASSERT(false);
301 return false;
302 }
303
304 // Try to find a plot that we can perform an inline upload to.
305 // We prioritize this upload in reverse order of pages to counterbalance the order above.
306 Plot* plot = nullptr;
307 for (int pageIdx = ((int)fNumActivePages)-1; pageIdx >= 0; --pageIdx) {
308 Plot* currentPlot = fPages[pageIdx].fPlotList.tail();
309 if (currentPlot->lastUseToken() != target->tokenTracker()->nextDrawToken()) {
310 plot = currentPlot;
311 break;
312 }
313 }
314
315 // If we can't find a plot that is not used in a draw currently being prepared by an op, then
316 // we have to fail. This gives the op a chance to enqueue the draw, and call back into this
317 // function. When that draw is enqueued, the draw token advances, and the subsequent call will
318 // continue past this branch and prepare an inline upload that will occur after the enqueued
319 //draw which references the plot's pre-upload content.
320 if (!plot) {
321 return false;
322 }
323
324 this->processEviction(plot->id());
325 int pageIdx = GetPageIndexFromID(plot->id());
326 fPages[pageIdx].fPlotList.remove(plot);
327 sk_sp<Plot>& newPlot = fPages[pageIdx].fPlotArray[plot->index()];
328 newPlot.reset(plot->clone());
329
330 fPages[pageIdx].fPlotList.addToHead(newPlot.get());
331 SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == newPlot->bpp());
332 SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc);
333 SkASSERT(verify);
334
335 // Note that this plot will be uploaded inline with the draws whereas the
336 // one it displaced most likely was uploaded ASAP.
337 // With c+14 we could move sk_sp into lambda to only ref once.
338 sk_sp<Plot> plotsp(SkRef(newPlot.get()));
339
340 GrTextureProxy* proxy = fProxies[pageIdx].get();
341 SkASSERT(proxy->priv().isInstantiated());
342
343 GrDeferredUploadToken lastUploadToken = target->addInlineUpload(
344 [plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
345 plotsp->uploadToTexture(writePixels, proxy);
346 });
347 newPlot->setLastUploadToken(lastUploadToken);
348
349 *id = newPlot->id();
350
351 return true;
352 }
353
compact(GrDeferredUploadToken startTokenForNextFlush)354 void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
355 if (fNumActivePages <= 1) {
356 fPrevFlushToken = startTokenForNextFlush;
357 return;
358 }
359
360 // For all plots, reset number of flushes since used if used this frame.
361 PlotList::Iter plotIter;
362 bool atlasUsedThisFlush = false;
363 for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
364 plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
365 while (Plot* plot = plotIter.get()) {
366 // Reset number of flushes since used
367 if (plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
368 plot->resetFlushesSinceLastUsed();
369 atlasUsedThisFlush = true;
370 }
371
372 plotIter.next();
373 }
374 }
375
376 // We only try to compact if the atlas was used in the recently completed flush.
377 // This is to handle the case where a lot of text or path rendering has occurred but then just
378 // a blinking cursor is drawn.
379 // TODO: consider if we should also do this if it's been a long time since the last atlas use
380 if (atlasUsedThisFlush) {
381 SkTArray<Plot*> availablePlots;
382 uint32_t lastPageIndex = fNumActivePages - 1;
383
384 // For all plots but the last one, update number of flushes since used, and check to see
385 // if there are any in the first pages that the last page can safely upload to.
386 for (uint32_t pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) {
387 #ifdef DUMP_ATLAS_DATA
388 if (gDumpAtlasData) {
389 SkDebugf("page %d: ", pageIndex);
390 }
391 #endif
392 plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
393 while (Plot* plot = plotIter.get()) {
394 // Update number of flushes since plot was last used
395 // We only increment the 'sinceLastUsed' count for flushes where the atlas was used
396 // to avoid deleting everything when we return to text drawing in the blinking
397 // cursor case
398 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
399 plot->incFlushesSinceLastUsed();
400 }
401
402 #ifdef DUMP_ATLAS_DATA
403 if (gDumpAtlasData) {
404 SkDebugf("%d ", plot->flushesSinceLastUsed());
405 }
406 #endif
407 // Count plots we can potentially upload to in all pages except the last one
408 // (the potential compactee).
409 if (plot->flushesSinceLastUsed() > kRecentlyUsedCount) {
410 availablePlots.push_back() = plot;
411 }
412
413 plotIter.next();
414 }
415 #ifdef DUMP_ATLAS_DATA
416 if (gDumpAtlasData) {
417 SkDebugf("\n");
418 }
419 #endif
420 }
421
422 // Count recently used plots in the last page and evict any that are no longer in use.
423 // Since we prioritize uploading to the first pages, this will eventually
424 // clear out usage of this page unless we have a large need.
425 plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
426 unsigned int usedPlots = 0;
427 #ifdef DUMP_ATLAS_DATA
428 if (gDumpAtlasData) {
429 SkDebugf("page %d: ", lastPageIndex);
430 }
431 #endif
432 while (Plot* plot = plotIter.get()) {
433 // Update number of flushes since plot was last used
434 if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
435 plot->incFlushesSinceLastUsed();
436 }
437
438 #ifdef DUMP_ATLAS_DATA
439 if (gDumpAtlasData) {
440 SkDebugf("%d ", plot->flushesSinceLastUsed());
441 }
442 #endif
443 // If this plot was used recently
444 if (plot->flushesSinceLastUsed() <= kRecentlyUsedCount) {
445 usedPlots++;
446 } else if (plot->lastUseToken() != GrDeferredUploadToken::AlreadyFlushedToken()) {
447 // otherwise if aged out just evict it.
448 this->processEvictionAndResetRects(plot);
449 }
450 plotIter.next();
451 }
452 #ifdef DUMP_ATLAS_DATA
453 if (gDumpAtlasData) {
454 SkDebugf("\n");
455 }
456 #endif
457
458 // If recently used plots in the last page are using less than a quarter of the page, try
459 // to evict them if there's available space in earlier pages. Since we prioritize uploading
460 // to the first pages, this will eventually clear out usage of this page unless we have a
461 // large need.
462 if (availablePlots.count() && usedPlots && usedPlots <= fNumPlots / 4) {
463 plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
464 while (Plot* plot = plotIter.get()) {
465 // If this plot was used recently
466 if (plot->flushesSinceLastUsed() <= kRecentlyUsedCount) {
467 // See if there's room in an earlier page and if so evict.
468 // We need to be somewhat harsh here so that a handful of plots that are
469 // consistently in use don't end up locking the page in memory.
470 if (availablePlots.count() > 0) {
471 this->processEvictionAndResetRects(plot);
472 this->processEvictionAndResetRects(availablePlots.back());
473 availablePlots.pop_back();
474 --usedPlots;
475 }
476 if (!usedPlots || !availablePlots.count()) {
477 break;
478 }
479 }
480 plotIter.next();
481 }
482 }
483
484 // If none of the plots in the last page have been used recently, delete it.
485 if (!usedPlots) {
486 #ifdef DUMP_ATLAS_DATA
487 if (gDumpAtlasData) {
488 SkDebugf("delete %d\n", fNumPages-1);
489 }
490 #endif
491 this->deactivateLastPage();
492 }
493 }
494
495 fPrevFlushToken = startTokenForNextFlush;
496 }
497
createPages(GrProxyProvider * proxyProvider)498 bool GrDrawOpAtlas::createPages(GrProxyProvider* proxyProvider) {
499 SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
500
501 GrSurfaceDesc desc;
502 desc.fFlags = kNone_GrSurfaceFlags;
503 desc.fOrigin = kTopLeft_GrSurfaceOrigin;
504 desc.fWidth = fTextureWidth;
505 desc.fHeight = fTextureHeight;
506 desc.fConfig = fPixelConfig;
507
508 int numPlotsX = fTextureWidth/fPlotWidth;
509 int numPlotsY = fTextureHeight/fPlotHeight;
510
511 for (uint32_t i = 0; i < this->maxPages(); ++i) {
512 fProxies[i] = proxyProvider->createProxy(desc, SkBackingFit::kExact, SkBudgeted::kYes,
513 GrResourceProvider::kNoPendingIO_Flag);
514 if (!fProxies[i]) {
515 return false;
516 }
517
518 // set up allocated plots
519 fPages[i].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
520
521 sk_sp<Plot>* currPlot = fPages[i].fPlotArray.get();
522 for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
523 for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
524 uint32_t plotIndex = r * numPlotsX + c;
525 currPlot->reset(new Plot(i, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
526 fPixelConfig));
527
528 // build LRU list
529 fPages[i].fPlotList.addToHead(currPlot->get());
530 ++currPlot;
531 }
532 }
533
534 }
535
536 return true;
537 }
538
539
activateNewPage(GrResourceProvider * resourceProvider)540 bool GrDrawOpAtlas::activateNewPage(GrResourceProvider* resourceProvider) {
541 if (fNumActivePages >= this->maxPages()) {
542 return false;
543 }
544
545 if (!fProxies[fNumActivePages]->instantiate(resourceProvider)) {
546 return false;
547 }
548
549 #ifdef DUMP_ATLAS_DATA
550 if (gDumpAtlasData) {
551 SkDebugf("activated page#: %d\n", fNumActivePages);
552 }
553 #endif
554
555 ++fNumActivePages;
556 return true;
557 }
558
559
deactivateLastPage()560 inline void GrDrawOpAtlas::deactivateLastPage() {
561 SkASSERT(fNumActivePages);
562
563 uint32_t lastPageIndex = fNumActivePages - 1;
564
565 int numPlotsX = fTextureWidth/fPlotWidth;
566 int numPlotsY = fTextureHeight/fPlotHeight;
567
568 fPages[lastPageIndex].fPlotList.reset();
569 for (int r = 0; r < numPlotsY; ++r) {
570 for (int c = 0; c < numPlotsX; ++c) {
571 uint32_t plotIndex = r * numPlotsX + c;
572
573 Plot* currPlot = fPages[lastPageIndex].fPlotArray[plotIndex].get();
574 currPlot->resetRects();
575 currPlot->resetFlushesSinceLastUsed();
576
577 // rebuild the LRU list
578 SkDEBUGCODE(currPlot->fPrev = currPlot->fNext = nullptr);
579 SkDEBUGCODE(currPlot->fList = nullptr);
580 fPages[lastPageIndex].fPlotList.addToHead(currPlot);
581 }
582 }
583
584 // remove ref to the backing texture
585 fProxies[lastPageIndex]->deInstantiate();
586 --fNumActivePages;
587 }
588