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