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 
     12 DECLARE_SKMESSAGEBUS_MESSAGE(GrPictureDeletedMessage);
     13 
     14 #ifdef SK_DEBUG
     15 void GrCachedLayer::validate(const GrTexture* backingTexture) const {
     16     SkASSERT(SK_InvalidGenID != fKey.pictureID());
     17     SkASSERT(fKey.start() > 0 && fKey.stop() > 0);
     18 
     19 
     20     if (fTexture) {
     21         // If the layer is in some texture then it must occupy some rectangle
     22         SkASSERT(!fRect.isEmpty());
     23         if (!this->isAtlased()) {
     24             // If it isn't atlased then the rectangle should start at the origin
     25             SkASSERT(0.0f == fRect.fLeft && 0.0f == fRect.fTop);
     26         }
     27     } else {
     28         SkASSERT(fRect.isEmpty());
     29         SkASSERT(NULL == fPlot);
     30         SkASSERT(!fLocked);     // layers without a texture cannot be locked
     31     }
     32 
     33     if (fPlot) {
     34         // If a layer has a plot (i.e., is atlased) then it must point to
     35         // the backing texture. Additionally, its rect should be non-empty.
     36         SkASSERT(fTexture && backingTexture == fTexture);
     37         SkASSERT(!fRect.isEmpty());
     38     }
     39 
     40     if (fLocked) {
     41         // If a layer is locked it must have a texture (though it need not be
     42         // the atlas-backing texture) and occupy some space.
     43         SkASSERT(fTexture);
     44         SkASSERT(!fRect.isEmpty());
     45     }
     46 }
     47 
     48 class GrAutoValidateLayer : ::SkNoncopyable {
     49 public:
     50     GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer)
     51         : fBackingTexture(backingTexture)
     52         , fLayer(layer) {
     53         if (fLayer) {
     54             fLayer->validate(backingTexture);
     55         }
     56     }
     57     ~GrAutoValidateLayer() {
     58         if (fLayer) {
     59             fLayer->validate(fBackingTexture);
     60         }
     61     }
     62     void setBackingTexture(GrTexture* backingTexture) {
     63         SkASSERT(NULL == fBackingTexture || fBackingTexture == backingTexture);
     64         fBackingTexture = backingTexture;
     65     }
     66 
     67 private:
     68     const GrTexture* fBackingTexture;
     69     const GrCachedLayer* fLayer;
     70 };
     71 #endif
     72 
     73 GrLayerCache::GrLayerCache(GrContext* context)
     74     : fContext(context) {
     75     this->initAtlas();
     76     memset(fPlotLocks, 0, sizeof(fPlotLocks));
     77 }
     78 
     79 GrLayerCache::~GrLayerCache() {
     80 
     81     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
     82     for (; !iter.done(); ++iter) {
     83         GrCachedLayer* layer = &(*iter);
     84         this->unlock(layer);
     85         SkDELETE(layer);
     86     }
     87 
     88     // The atlas only lets go of its texture when the atlas is deleted.
     89     fAtlas.free();
     90 }
     91 
     92 void GrLayerCache::initAtlas() {
     93     SkASSERT(NULL == fAtlas.get());
     94 
     95     SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
     96     fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfig,
     97                                       kRenderTarget_GrTextureFlagBit,
     98                                       textureSize, kNumPlotsX, kNumPlotsY, false)));
     99 }
    100 
    101 void GrLayerCache::freeAll() {
    102 
    103     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
    104     for (; !iter.done(); ++iter) {
    105         GrCachedLayer* layer = &(*iter);
    106         this->unlock(layer);
    107         SkDELETE(layer);
    108     }
    109     fLayerHash.rewind();
    110 
    111     // The atlas only lets go of its texture when the atlas is deleted.
    112     fAtlas.free();
    113     // GrLayerCache always assumes an atlas exists so recreate it. The atlas
    114     // lazily allocates a replacement texture so reallocating a new
    115     // atlas here won't disrupt a GrContext::abandonContext or freeGpuResources.
    116     // TODO: Make GrLayerCache lazily allocate the atlas manager?
    117     this->initAtlas();
    118 }
    119 
    120 GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID,
    121                                          int start, int stop,
    122                                          const SkIPoint& offset,
    123                                          const SkMatrix& ctm,
    124                                          const SkPaint* paint) {
    125     SkASSERT(pictureID != SK_InvalidGenID && start > 0 && stop > 0);
    126 
    127     GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop, offset, ctm, paint));
    128     fLayerHash.add(layer);
    129     return layer;
    130 }
    131 
    132 GrCachedLayer* GrLayerCache::findLayer(uint32_t pictureID,
    133                                        int start, int stop,
    134                                        const SkIPoint& offset,
    135                                        const SkMatrix& ctm) {
    136     SkASSERT(pictureID != SK_InvalidGenID && start > 0 && stop > 0);
    137     return fLayerHash.find(GrCachedLayer::Key(pictureID, start, stop, offset, ctm));
    138 }
    139 
    140 GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
    141                                                int start, int stop,
    142                                                const SkIPoint& offset,
    143                                                const SkMatrix& ctm,
    144                                                const SkPaint* paint) {
    145     SkASSERT(pictureID != SK_InvalidGenID && start > 0 && stop > 0);
    146     GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, start, stop, offset, ctm));
    147     if (NULL == layer) {
    148         layer = this->createLayer(pictureID, start, stop, offset, ctm, paint);
    149     }
    150 
    151     return layer;
    152 }
    153 
    154 bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool dontAtlas) {
    155     SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
    156 
    157     if (layer->locked()) {
    158         // This layer is already locked
    159 #ifdef SK_DEBUG
    160         if (layer->isAtlased()) {
    161             // It claims to be atlased
    162             SkASSERT(!dontAtlas);
    163             SkASSERT(layer->rect().width() == desc.fWidth);
    164             SkASSERT(layer->rect().height() == desc.fHeight);
    165         }
    166 #endif
    167         return false;
    168     }
    169 
    170     if (layer->isAtlased()) {
    171         // Hooray it is still in the atlas - make sure it stays there
    172         SkASSERT(!dontAtlas);
    173         layer->setLocked(true);
    174         fPlotLocks[layer->plot()->id()]++;
    175         return false;
    176     } else if (!dontAtlas && PlausiblyAtlasable(desc.fWidth, desc.fHeight)) {
    177         // Not in the atlas - will it fit?
    178         GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
    179         if (NULL == pictInfo) {
    180             pictInfo = SkNEW_ARGS(GrPictureInfo, (layer->pictureID()));
    181             fPictureHash.add(pictInfo);
    182         }
    183 
    184         SkIPoint16 loc;
    185         for (int i = 0; i < 2; ++i) { // extra pass in case we fail to add but are able to purge
    186             GrPlot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage,
    187                                               desc.fWidth, desc.fHeight,
    188                                               NULL, &loc);
    189             // addToAtlas can allocate the backing texture
    190             SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture()));
    191             if (plot) {
    192                 // The layer was successfully added to the atlas
    193                 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
    194                                                        SkToS16(desc.fWidth),
    195                                                        SkToS16(desc.fHeight));
    196                 layer->setTexture(fAtlas->getTexture(), bounds);
    197                 layer->setPlot(plot);
    198                 layer->setLocked(true);
    199                 fPlotLocks[layer->plot()->id()]++;
    200                 return true;
    201             }
    202 
    203             // The layer was rejected by the atlas (even though we know it is
    204             // plausibly atlas-able). See if a plot can be purged and try again.
    205             if (!this->purgePlot()) {
    206                 break;  // We weren't able to purge any plots
    207             }
    208         }
    209     }
    210 
    211     // The texture wouldn't fit in the cache - give it it's own texture.
    212     // This path always uses a new scratch texture and (thus) doesn't cache anything.
    213     // This can yield a lot of re-rendering
    214     SkAutoTUnref<GrTexture> tex(fContext->lockAndRefScratchTexture(desc,
    215                                                         GrContext::kApprox_ScratchTexMatch));
    216 
    217     layer->setTexture(tex, GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight)));
    218     layer->setLocked(true);
    219     return true;
    220 }
    221 
    222 void GrLayerCache::unlock(GrCachedLayer* layer) {
    223     SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);)
    224 
    225     if (NULL == layer || !layer->locked()) {
    226         // invalid or not locked
    227         return;
    228     }
    229 
    230     if (layer->isAtlased()) {
    231         const int plotID = layer->plot()->id();
    232 
    233         SkASSERT(fPlotLocks[plotID] > 0);
    234         fPlotLocks[plotID]--;
    235         // At this point we could aggressively clear out un-locked plots but
    236         // by delaying we may be able to reuse some of the atlased layers later.
    237 #if DISABLE_CACHING
    238         // This testing code aggressively removes the atlased layers. This
    239         // can be used to separate the performance contribution of less
    240         // render target pingponging from that due to the re-use of cached layers
    241         GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
    242         SkASSERT(pictInfo);
    243 
    244         GrAtlas::RemovePlot(&pictInfo->fPlotUsage, layer->plot());
    245 
    246         layer->setPlot(NULL);
    247         layer->setTexture(NULL, GrIRect16::MakeEmpty());
    248 #endif
    249 
    250     } else {
    251         fContext->unlockScratchTexture(layer->texture());
    252         layer->setTexture(NULL, GrIRect16::MakeEmpty());
    253     }
    254 
    255     layer->setLocked(false);
    256 }
    257 
    258 #ifdef SK_DEBUG
    259 void GrLayerCache::validate() const {
    260     int plotLocks[kNumPlotsX * kNumPlotsY];
    261     memset(plotLocks, 0, sizeof(plotLocks));
    262 
    263     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHash);
    264     for (; !iter.done(); ++iter) {
    265         const GrCachedLayer* layer = &(*iter);
    266 
    267         layer->validate(fAtlas->getTexture());
    268 
    269         const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID());
    270         if (pictInfo) {
    271             // In aggressive cleanup mode a picture info should only exist if
    272             // it has some atlased layers
    273 #if !DISABLE_CACHING
    274             SkASSERT(!pictInfo->fPlotUsage.isEmpty());
    275 #endif
    276         } else {
    277             // If there is no picture info for this layer then all of its
    278             // layers should be non-atlased.
    279             SkASSERT(!layer->isAtlased());
    280         }
    281 
    282         if (layer->plot()) {
    283             SkASSERT(pictInfo);
    284             SkASSERT(pictInfo->fPictureID == layer->pictureID());
    285 
    286             SkASSERT(pictInfo->fPlotUsage.contains(layer->plot()));
    287 
    288             if (layer->locked()) {
    289                 plotLocks[layer->plot()->id()]++;
    290             }
    291         }
    292     }
    293 
    294     for (int i = 0; i < kNumPlotsX*kNumPlotsY; ++i) {
    295         SkASSERT(plotLocks[i] == fPlotLocks[i]);
    296     }
    297 }
    298 
    299 class GrAutoValidateCache : ::SkNoncopyable {
    300 public:
    301     explicit GrAutoValidateCache(GrLayerCache* cache)
    302         : fCache(cache) {
    303         fCache->validate();
    304     }
    305     ~GrAutoValidateCache() {
    306         fCache->validate();
    307     }
    308 private:
    309     GrLayerCache* fCache;
    310 };
    311 #endif
    312 
    313 void GrLayerCache::purge(uint32_t pictureID) {
    314 
    315     SkDEBUGCODE(GrAutoValidateCache avc(this);)
    316 
    317     // We need to find all the layers associated with 'picture' and remove them.
    318     SkTDArray<GrCachedLayer*> toBeRemoved;
    319 
    320     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
    321     for (; !iter.done(); ++iter) {
    322         if (pictureID == (*iter).pictureID()) {
    323             *toBeRemoved.append() = &(*iter);
    324         }
    325     }
    326 
    327     for (int i = 0; i < toBeRemoved.count(); ++i) {
    328         this->unlock(toBeRemoved[i]);
    329         fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
    330         SkDELETE(toBeRemoved[i]);
    331     }
    332 
    333     GrPictureInfo* pictInfo = fPictureHash.find(pictureID);
    334     if (pictInfo) {
    335         fPictureHash.remove(pictureID);
    336         SkDELETE(pictInfo);
    337     }
    338 }
    339 
    340 bool GrLayerCache::purgePlot() {
    341     SkDEBUGCODE(GrAutoValidateCache avc(this);)
    342 
    343     GrAtlas::PlotIter iter;
    344     GrPlot* plot;
    345     for (plot = fAtlas->iterInit(&iter, GrAtlas::kLRUFirst_IterOrder);
    346          plot;
    347          plot = iter.prev()) {
    348         if (fPlotLocks[plot->id()] > 0) {
    349             continue;
    350         }
    351 
    352         this->purgePlot(plot);
    353         return true;
    354     }
    355 
    356     return false;
    357 }
    358 
    359 void GrLayerCache::purgePlot(GrPlot* plot) {
    360     SkASSERT(0 == fPlotLocks[plot->id()]);
    361 
    362     // We need to find all the layers in 'plot' and remove them.
    363     SkTDArray<GrCachedLayer*> toBeRemoved;
    364 
    365     SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash);
    366     for (; !iter.done(); ++iter) {
    367         if (plot == (*iter).plot()) {
    368             *toBeRemoved.append() = &(*iter);
    369         }
    370     }
    371 
    372     for (int i = 0; i < toBeRemoved.count(); ++i) {
    373         SkASSERT(!toBeRemoved[i]->locked());
    374 
    375         GrPictureInfo* pictInfo = fPictureHash.find(toBeRemoved[i]->pictureID());
    376         SkASSERT(pictInfo);
    377 
    378         GrAtlas::RemovePlot(&pictInfo->fPlotUsage, plot);
    379 
    380         // Aggressively remove layers and, if now totally uncached, picture info
    381         fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i]));
    382         SkDELETE(toBeRemoved[i]);
    383 
    384         if (pictInfo->fPlotUsage.isEmpty()) {
    385             fPictureHash.remove(pictInfo->fPictureID);
    386             SkDELETE(pictInfo);
    387         }
    388     }
    389 
    390     plot->resetRects();
    391 }
    392 
    393 void GrLayerCache::purgeAll() {
    394     GrAtlas::PlotIter iter;
    395     GrPlot* plot;
    396     for (plot = fAtlas->iterInit(&iter, GrAtlas::kLRUFirst_IterOrder);
    397          plot;
    398          plot = iter.prev()) {
    399         SkASSERT(0 == fPlotLocks[plot->id()]);
    400 
    401         this->purgePlot(plot);
    402     }
    403 }
    404 
    405 class GrPictureDeletionListener : public SkPicture::DeletionListener {
    406     virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{
    407         const GrPictureDeletedMessage message = { pictureID };
    408         SkMessageBus<GrPictureDeletedMessage>::Post(message);
    409     }
    410 };
    411 
    412 void GrLayerCache::trackPicture(const SkPicture* picture) {
    413     if (NULL == fDeletionListener) {
    414         fDeletionListener.reset(SkNEW(GrPictureDeletionListener));
    415     }
    416 
    417     picture->addDeletionListener(fDeletionListener);
    418 }
    419 
    420 void GrLayerCache::processDeletedPictures() {
    421     SkTDArray<GrPictureDeletedMessage> deletedPictures;
    422     fPictDeletionInbox.poll(&deletedPictures);
    423 
    424     for (int i = 0; i < deletedPictures.count(); i++) {
    425         this->purge(deletedPictures[i].pictureID);
    426     }
    427 }
    428 
    429