Home | History | Annotate | Download | only in gpu
      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
     16 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:
     60     GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer)
     61         : fBackingTexture(backingTexture)
     62         , fLayer(layer) {
     63         if (fLayer) {
     64             fLayer->validate(backingTexture);
     65         }
     66     }
     67     ~GrAutoValidateLayer() {
     68         if (fLayer) {
     69             fLayer->validate(fBackingTexture);
     70         }
     71     }
     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 
     83 GrLayerCache::GrLayerCache(GrContext* context)
     84     : fContext(context) {
     85     memset(fPlotLocks, 0, sizeof(fPlotLocks));
     86 }
     87 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    317 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:
    356     explicit GrAutoValidateCache(GrLayerCache* cache)
    357         : fCache(cache) {
    358         fCache->validate();
    359     }
    360     ~GrAutoValidateCache() {
    361         fCache->validate();
    362     }
    363 private:
    364     GrLayerCache* fCache;
    365 };
    366 #endif
    367 
    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 
    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 
    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
    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 
    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 
    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 
    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
    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