• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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 "GrLayerAtlas.h"
9 #include "GrContext.h"
10 #include "GrDrawContext.h"
11 #include "GrGpu.h"
12 #include "GrLayerCache.h"
13 #include "GrSurfacePriv.h"
14 
15 #ifdef SK_DEBUG
validate(const GrTexture * backingTexture) const16 void GrCachedLayer::validate(const GrTexture* backingTexture) const {
17     SkASSERT(SK_InvalidGenID != fKey.pictureID());
18 
19     if (fTexture) {
20         // If the layer is in some texture then it must occupy some rectangle
21         SkASSERT(!fRect.isEmpty());
22         if (!this->isAtlased()) {
23             // If it isn't atlased then the rectangle should start at the origin
24             SkASSERT(0.0f == fRect.fLeft && 0.0f == fRect.fTop);
25         }
26     } else {
27         SkASSERT(fRect.isEmpty());
28         SkASSERT(nullptr == fPlot);
29         SkASSERT(!fLocked);     // layers without a texture cannot be locked
30         SkASSERT(!fAtlased);    // can't be atlased if it doesn't have a texture
31     }
32 
33     if (fPlot) {
34         SkASSERT(fAtlased);
35         // If a layer has a plot (i.e., is atlased) then it must point to
36         // the backing texture. Additionally, its rect should be non-empty.
37         SkASSERT(fTexture && backingTexture == fTexture);
38         SkASSERT(!fRect.isEmpty());
39     }
40 
41     if (fLocked) {
42         // If a layer is locked it must have a texture (though it need not be
43         // the atlas-backing texture) and occupy some space.
44         SkASSERT(fTexture);
45         SkASSERT(!fRect.isEmpty());
46     }
47 
48     // Unfortunately there is a brief time where a layer can be locked
49     // but not used, so we can only check the "used implies locked"
50     // invariant.
51     if (fUses > 0) {
52         SkASSERT(fLocked);
53     } else {
54         SkASSERT(0 == fUses);
55     }
56 }
57 
58 class GrAutoValidateLayer : ::SkNoncopyable {
59 public:
GrAutoValidateLayer(GrTexture * backingTexture,const GrCachedLayer * layer)60     GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer)
61         : fBackingTexture(backingTexture)
62         , fLayer(layer) {
63         if (fLayer) {
64             fLayer->validate(backingTexture);
65         }
66     }
~GrAutoValidateLayer()67     ~GrAutoValidateLayer() {
68         if (fLayer) {
69             fLayer->validate(fBackingTexture);
70         }
71     }
setBackingTexture(GrTexture * backingTexture)72     void setBackingTexture(GrTexture* backingTexture) {
73         SkASSERT(nullptr == fBackingTexture || fBackingTexture == backingTexture);
74         fBackingTexture = backingTexture;
75     }
76 
77 private:
78     const GrTexture* fBackingTexture;
79     const GrCachedLayer* fLayer;
80 };
81 #endif
82 
GrLayerCache(GrContext * context)83 GrLayerCache::GrLayerCache(GrContext* context)
84     : fContext(context) {
85     memset(fPlotLocks, 0, sizeof(fPlotLocks));
86 }
87 
~GrLayerCache()88 GrLayerCache::~GrLayerCache() {
89 
90     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
91     for (; !iter.done(); ++iter) {
92         GrCachedLayer* layer = &(*iter);
93         SkASSERT(0 == layer->uses());
94         this->unlock(layer);
95         delete layer;
96     }
97 
98     SkASSERT(0 == fPictureHash.count());
99 
100     // The atlas only lets go of its texture when the atlas is deleted.
101     fAtlas.free();
102 }
103 
initAtlas()104 void GrLayerCache::initAtlas() {
105     SkASSERT(nullptr == fAtlas.get());
106     GR_STATIC_ASSERT(kNumPlotsX*kNumPlotsX == GrPictureInfo::kNumPlots);
107 
108     SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
109     fAtlas.reset(new GrLayerAtlas(fContext->textureProvider(), kSkia8888_GrPixelConfig,
110                                   kRenderTarget_GrSurfaceFlag, textureSize,
111                                   kNumPlotsX, kNumPlotsY));
112 }
113 
freeAll()114 void GrLayerCache::freeAll() {
115 
116     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
117     for (; !iter.done(); ++iter) {
118         GrCachedLayer* layer = &(*iter);
119         this->unlock(layer);
120         delete layer;
121     }
122     fLayerHash.rewind();
123 
124     if (fAtlas) {
125         fAtlas->resetPlots();
126         fAtlas->detachBackingTexture();
127     }
128 }
129 
createLayer(uint32_t pictureID,int start,int stop,const SkIRect & srcIR,const SkIRect & dstIR,const SkMatrix & initialMat,const int * key,int keySize,const SkPaint * paint)130 GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID,
131                                          int start, int stop,
132                                          const SkIRect& srcIR,
133                                          const SkIRect& dstIR,
134                                          const SkMatrix& initialMat,
135                                          const int* key,
136                                          int keySize,
137                                          const SkPaint* paint) {
138     SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0);
139 
140     GrCachedLayer* layer = new GrCachedLayer(pictureID, start, stop, srcIR, dstIR, initialMat, key,
141                                              keySize, paint);
142     fLayerHash.add(layer);
143     return layer;
144 }
145 
findLayer(uint32_t pictureID,const SkMatrix & initialMat,const int * key,int keySize)146 GrCachedLayer* GrLayerCache::findLayer(uint32_t pictureID, const SkMatrix& initialMat,
147                                        const int* key, int keySize) {
148     SkASSERT(pictureID != SK_InvalidGenID);
149     return fLayerHash.find(GrCachedLayer::Key(pictureID, initialMat, key, keySize));
150 }
151 
findLayerOrCreate(uint32_t pictureID,int start,int stop,const SkIRect & srcIR,const SkIRect & dstIR,const SkMatrix & initialMat,const int * key,int keySize,const SkPaint * paint)152 GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
153                                                int start, int stop,
154                                                const SkIRect& srcIR,
155                                                const SkIRect& dstIR,
156                                                const SkMatrix& initialMat,
157                                                const int* key,
158                                                int keySize,
159                                                const SkPaint* paint) {
160     SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0);
161     GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, initialMat, key, keySize));
162     if (nullptr == layer) {
163         layer = this->createLayer(pictureID, start, stop,
164                                   srcIR, dstIR, initialMat,
165                                   key, keySize, paint);
166     }
167 
168     return layer;
169 }
170 
tryToAtlas(GrCachedLayer * layer,const GrSurfaceDesc & desc,bool * needsRendering)171 bool GrLayerCache::tryToAtlas(GrCachedLayer* layer,
172                               const GrSurfaceDesc& desc,
173                               bool* needsRendering) {
174     SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTextureOrNull() : nullptr, layer);)
175 
176     SkASSERT(PlausiblyAtlasable(desc.fWidth, desc.fHeight));
177     SkASSERT(0 == desc.fSampleCnt);
178 
179     if (layer->locked()) {
180         // This layer is already locked
181         SkASSERT(fAtlas);
182         SkASSERT(layer->isAtlased());
183         SkASSERT(layer->rect().width() == desc.fWidth);
184         SkASSERT(layer->rect().height() == desc.fHeight);
185         *needsRendering = false;
186         return true;
187     }
188 
189     if (layer->isAtlased()) {
190         SkASSERT(fAtlas);
191         // Hooray it is still in the atlas - make sure it stays there
192         layer->setLocked(true);
193         this->incPlotLock(layer->plot()->id());
194         *needsRendering = false;
195         return true;
196     } else {
197         if (!fAtlas) {
198             this->initAtlas();
199             if (!fAtlas) {
200                 return false;
201             }
202         }
203         // Not in the atlas - will it fit?
204         GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
205         if (nullptr == pictInfo) {
206             pictInfo = new GrPictureInfo(layer->pictureID());
207             fPictureHash.add(pictInfo);
208         }
209 
210         SkIPoint16 loc;
211         for (int i = 0; i < 2; ++i) { // extra pass in case we fail to add but are able to purge
212             GrLayerAtlas::Plot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage,
213                                                           desc.fWidth, desc.fHeight,
214                                                           &loc);
215             // addToAtlas can allocate the backing texture
216             SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture()));
217             if (plot) {
218 #if !GR_CACHE_HOISTED_LAYERS
219                 pictInfo->incPlotUsage(plot->id());
220 #endif
221                 // The layer was successfully added to the atlas
222                 const SkIRect bounds = SkIRect::MakeXYWH(loc.fX, loc.fY,
223                                                          desc.fWidth, desc.fHeight);
224                 layer->setTexture(fAtlas->getTexture(), bounds, true);
225                 layer->setPlot(plot);
226                 layer->setLocked(true);
227                 this->incPlotLock(layer->plot()->id());
228                 *needsRendering = true;
229                 return true;
230             }
231 
232             // The layer was rejected by the atlas (even though we know it is
233             // plausibly atlas-able). See if a plot can be purged and try again.
234             if (!this->purgePlots(true)) {
235                 break;  // We weren't able to purge any plots
236             }
237         }
238 
239         if (pictInfo->fPlotUsage.isEmpty()) {
240             fPictureHash.remove(pictInfo->fPictureID);
241             delete pictInfo;
242         }
243     }
244 
245     return false;
246 }
247 
lock(GrCachedLayer * layer,const GrSurfaceDesc & desc,bool * needsRendering)248 bool GrLayerCache::lock(GrCachedLayer* layer, const GrSurfaceDesc& desc, bool* needsRendering) {
249     if (layer->locked()) {
250         // This layer is already locked
251         *needsRendering = false;
252         return true;
253     }
254 
255     // TODO: make the test for exact match depend on the image filters themselves
256     SkAutoTUnref<GrTexture> tex;
257     if (layer->fFilter) {
258         tex.reset(fContext->textureProvider()->createTexture(desc, SkBudgeted::kYes));
259     } else {
260         tex.reset(fContext->textureProvider()->createApproxTexture(desc));
261     }
262 
263     if (!tex) {
264         return false;
265     }
266 
267     layer->setTexture(tex, SkIRect::MakeWH(desc.fWidth, desc.fHeight), false);
268     layer->setLocked(true);
269     *needsRendering = true;
270     return true;
271 }
272 
unlock(GrCachedLayer * layer)273 void GrLayerCache::unlock(GrCachedLayer* layer) {
274     SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTextureOrNull() : nullptr, layer);)
275 
276     if (nullptr == layer || !layer->locked()) {
277         // invalid or not locked
278         return;
279     }
280 
281     if (layer->isAtlased()) {
282         const int plotID = layer->plot()->id();
283 
284         this->decPlotLock(plotID);
285         // At this point we could aggressively clear out un-locked plots but
286         // by delaying we may be able to reuse some of the atlased layers later.
287 #if !GR_CACHE_HOISTED_LAYERS
288         // This testing code aggressively removes the atlased layers. This
289         // can be used to separate the performance contribution of less
290         // render target pingponging from that due to the re-use of cached layers
291         GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
292         SkASSERT(pictInfo);
293 
294         pictInfo->decPlotUsage(plotID);
295 
296         if (0 == pictInfo->plotUsage(plotID)) {
297             pictInfo->fPlotUsage.removePlot(layer->plot());
298 
299             if (pictInfo->fPlotUsage.isEmpty()) {
300                 fPictureHash.remove(pictInfo->fPictureID);
301                 delete pictInfo;
302             }
303         }
304 
305         layer->setPlot(nullptr);
306         layer->setTexture(nullptr, SkIRect::MakeEmpty(), false);
307 #endif
308 
309     } else {
310         layer->setTexture(nullptr, SkIRect::MakeEmpty(), false);
311     }
312 
313     layer->setLocked(false);
314 }
315 
316 #ifdef SK_DEBUG
validate() const317 void GrLayerCache::validate() const {
318     int plotLocks[kNumPlotsX * kNumPlotsY];
319     memset(plotLocks, 0, sizeof(plotLocks));
320 
321     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHash);
322     for (; !iter.done(); ++iter) {
323         const GrCachedLayer* layer = &(*iter);
324 
325         layer->validate(fAtlas.get() ? fAtlas->getTextureOrNull() : nullptr);
326 
327         const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
328         if (!pictInfo) {
329             // If there is no picture info for this picture then all of its
330             // layers should be non-atlased.
331             SkASSERT(!layer->isAtlased());
332         }
333 
334         if (layer->plot()) {
335             SkASSERT(pictInfo);
336             SkASSERT(pictInfo->fPictureID == layer->pictureID());
337 
338             SkASSERT(pictInfo->fPlotUsage.contains(layer->plot()));
339 #if !GR_CACHE_HOISTED_LAYERS
340             SkASSERT(pictInfo->plotUsage(layer->plot()->id()) > 0);
341 #endif
342 
343             if (layer->locked()) {
344                 plotLocks[layer->plot()->id()]++;
345             }
346         }
347     }
348 
349     for (int i = 0; i < kNumPlotsX*kNumPlotsY; ++i) {
350         SkASSERT(plotLocks[i] == fPlotLocks[i]);
351     }
352 }
353 
354 class GrAutoValidateCache : ::SkNoncopyable {
355 public:
GrAutoValidateCache(GrLayerCache * cache)356     explicit GrAutoValidateCache(GrLayerCache* cache)
357         : fCache(cache) {
358         fCache->validate();
359     }
~GrAutoValidateCache()360     ~GrAutoValidateCache() {
361         fCache->validate();
362     }
363 private:
364     GrLayerCache* fCache;
365 };
366 #endif
367 
purge(uint32_t pictureID)368 void GrLayerCache::purge(uint32_t pictureID) {
369 
370     SkDEBUGCODE(GrAutoValidateCache avc(this);)
371 
372     // We need to find all the layers associated with 'picture' and remove them.
373     SkTDArray<GrCachedLayer*> toBeRemoved;
374 
375     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
376     for (; !iter.done(); ++iter) {
377         if (pictureID == (*iter).pictureID()) {
378             *toBeRemoved.append() = &(*iter);
379         }
380     }
381 
382     for (int i = 0; i < toBeRemoved.count(); ++i) {
383         SkASSERT(0 == toBeRemoved[i]->uses());
384         this->unlock(toBeRemoved[i]);
385         fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
386         delete toBeRemoved[i];
387     }
388 
389     GrPictureInfo* pictInfo = fPictureHash.find(pictureID);
390     if (pictInfo) {
391         fPictureHash.remove(pictureID);
392         delete pictInfo;
393     }
394 }
395 
purgePlots(bool justOne)396 bool GrLayerCache::purgePlots(bool justOne) {
397     SkDEBUGCODE(GrAutoValidateCache avc(this);)
398     SkASSERT(fAtlas);
399 
400     bool anyPurged = false;
401     GrLayerAtlas::PlotIter iter;
402     GrLayerAtlas::Plot* plot;
403     for (plot = fAtlas->iterInit(&iter, GrLayerAtlas::kLRUFirst_IterOrder);
404          plot;
405          plot = iter.prev()) {
406         if (fPlotLocks[plot->id()] > 0) {
407             continue;
408         }
409 
410         anyPurged = true;
411         this->purgePlot(plot);
412         if (justOne) {
413             break;
414         }
415     }
416 
417     return anyPurged;
418 }
419 
purgePlot(GrLayerAtlas::Plot * plot)420 void GrLayerCache::purgePlot(GrLayerAtlas::Plot* plot) {
421     SkASSERT(0 == fPlotLocks[plot->id()]);
422 
423     // We need to find all the layers in 'plot' and remove them.
424     SkTDArray<GrCachedLayer*> toBeRemoved;
425 
426     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
427     for (; !iter.done(); ++iter) {
428         if (plot == (*iter).plot()) {
429             *toBeRemoved.append() = &(*iter);
430         }
431     }
432 
433     for (int i = 0; i < toBeRemoved.count(); ++i) {
434         SkASSERT(0 == toBeRemoved[i]->uses());
435         SkASSERT(!toBeRemoved[i]->locked());
436 
437         uint32_t pictureIDToRemove = toBeRemoved[i]->pictureID();
438 
439         // Aggressively remove layers and, if it becomes totally uncached, delete the picture info
440         fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
441         delete toBeRemoved[i];
442 
443         GrPictureInfo* pictInfo = fPictureHash.find(pictureIDToRemove);
444         if (pictInfo) {
445 #if !GR_CACHE_HOISTED_LAYERS
446             SkASSERT(0 == pictInfo->plotUsage(plot->id()));
447 #endif
448             pictInfo->fPlotUsage.removePlot(plot);
449 
450             if (pictInfo->fPlotUsage.isEmpty()) {
451                 fPictureHash.remove(pictInfo->fPictureID);
452                 delete pictInfo;
453             }
454         }
455     }
456 
457     plot->reset();
458 }
459 
460 #if !GR_CACHE_HOISTED_LAYERS
purgeAll()461 void GrLayerCache::purgeAll() {
462     if (!fAtlas) {
463         return;
464     }
465 
466     this->purgePlots(false); // clear them all out
467 
468     SkASSERT(0 == fPictureHash.count());
469 
470     if (fAtlas->getTextureOrNull()) {
471         SkAutoTUnref<GrDrawContext> drawContext(
472                                     fContext->drawContext(fAtlas->getTexture()->asRenderTarget()));
473 
474         if (drawContext) {
475             drawContext->discard();
476         }
477     }
478 }
479 #endif
480 
begin()481 void GrLayerCache::begin() {
482     if (!fAtlas) {
483         return;
484     }
485 
486     if (!fAtlas->reattachBackingTexture()) {
487         // We weren't able to re-attach. Clear out all the atlased layers.
488         this->purgePlots(false);
489         SkASSERT(0 == fPictureHash.count());
490     }
491 #ifdef SK_DEBUG
492     else {
493         // we've reattached - everything had better make sense
494         SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
495         for (; !iter.done(); ++iter) {
496             GrCachedLayer* layer = &(*iter);
497 
498             if (layer->isAtlased()) {
499                 SkASSERT(fAtlas->getTexture() == layer->texture());
500             }
501         }
502     }
503 #endif
504 }
505 
end()506 void GrLayerCache::end() {
507     if (!fAtlas) {
508         return;
509     }
510 
511     // Adding this call will clear out all the layers in the atlas
512     //this->purgePlots(false);
513 
514     fAtlas->detachBackingTexture();
515 }
516 
processDeletedPictures()517 void GrLayerCache::processDeletedPictures() {
518     SkTArray<SkPicture::DeletionMessage> deletedPictures;
519     fPictDeletionInbox.poll(&deletedPictures);
520 
521     for (int i = 0; i < deletedPictures.count(); i++) {
522         this->purge(deletedPictures[i].fUniqueID);
523     }
524 }
525 
526 #ifdef SK_DEVELOPER
writeLayersToDisk(const SkString & dirName)527 void GrLayerCache::writeLayersToDisk(const SkString& dirName) {
528 
529     if (fAtlas) {
530         GrTexture* atlasTexture = fAtlas->getTextureOrNull();
531         if (nullptr != atlasTexture) {
532             SkString fileName(dirName);
533             fileName.append("\\atlas.png");
534 
535             atlasTexture->surfacePriv().savePixels(fileName.c_str());
536         }
537     }
538 
539     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
540     for (; !iter.done(); ++iter) {
541         GrCachedLayer* layer = &(*iter);
542 
543         if (layer->isAtlased() || !layer->texture()) {
544             continue;
545         }
546 
547         SkString fileName(dirName);
548         fileName.appendf("\\%d", layer->fKey.pictureID());
549         for (int i = 0; i < layer->fKey.keySize(); ++i) {
550             fileName.appendf("-%d", layer->fKey.key()[i]);
551         }
552         fileName.appendf(".png");
553 
554         layer->texture()->surfacePriv().savePixels(fileName.c_str());
555     }
556 }
557 #endif
558