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
validate(const GrTexture * backingTexture) const16 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:
GrAutoValidateLayer(GrTexture * backingTexture,const GrCachedLayer * layer)60 GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer)
61 : fBackingTexture(backingTexture)
62 , fLayer(layer) {
63 if (fLayer) {
64 fLayer->validate(backingTexture);
65 }
66 }
~GrAutoValidateLayer()67 ~GrAutoValidateLayer() {
68 if (fLayer) {
69 fLayer->validate(fBackingTexture);
70 }
71 }
setBackingTexture(GrTexture * backingTexture)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
GrLayerCache(GrContext * context)83 GrLayerCache::GrLayerCache(GrContext* context)
84 : fContext(context) {
85 memset(fPlotLocks, 0, sizeof(fPlotLocks));
86 }
87
~GrLayerCache()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
initAtlas()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
freeAll()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
createLayer(uint32_t pictureID,int start,int stop,const SkIRect & srcIR,const SkIRect & dstIR,const SkMatrix & initialMat,const int * key,int keySize,const SkPaint * paint)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
findLayer(uint32_t pictureID,const SkMatrix & initialMat,const int * key,int keySize)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
findLayerOrCreate(uint32_t pictureID,int start,int stop,const SkIRect & srcIR,const SkIRect & dstIR,const SkMatrix & initialMat,const int * key,int keySize,const SkPaint * paint)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
tryToAtlas(GrCachedLayer * layer,const GrSurfaceDesc & desc,bool * needsRendering)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
lock(GrCachedLayer * layer,const GrSurfaceDesc & desc,bool * needsRendering)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
unlock(GrCachedLayer * layer)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
validate() const317 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:
GrAutoValidateCache(GrLayerCache * cache)356 explicit GrAutoValidateCache(GrLayerCache* cache)
357 : fCache(cache) {
358 fCache->validate();
359 }
~GrAutoValidateCache()360 ~GrAutoValidateCache() {
361 fCache->validate();
362 }
363 private:
364 GrLayerCache* fCache;
365 };
366 #endif
367
purge(uint32_t pictureID)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
purgePlots(bool justOne)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
purgePlot(GrLayerAtlas::Plot * plot)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
purgeAll()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
begin()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
end()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
processDeletedPictures()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
writeLayersToDisk(const SkString & dirName)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