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