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
14void 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
54class GrAutoValidateLayer : ::SkNoncopyable {
55public:
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
73private:
74    const GrTexture* fBackingTexture;
75    const GrCachedLayer* fLayer;
76};
77#endif
78
79GrLayerCache::GrLayerCache(GrContext* context)
80    : fContext(context) {
81    memset(fPlotLocks, 0, sizeof(fPlotLocks));
82}
83
84GrLayerCache::~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
100void 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
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& 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
141GrCachedLayer* 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
147GrCachedLayer* 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
166bool 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
243bool 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
267void 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
311void 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
348class GrAutoValidateCache : ::SkNoncopyable {
349public:
350    explicit GrAutoValidateCache(GrLayerCache* cache)
351        : fCache(cache) {
352        fCache->validate();
353    }
354    ~GrAutoValidateCache() {
355        fCache->validate();
356    }
357private:
358    GrLayerCache* fCache;
359};
360#endif
361
362void 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
390bool 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
410void 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
451void 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
472void 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
482void 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