OLD | NEW |
| (Empty) |
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 | |
16 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: | |
60 GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer) | |
61 : fBackingTexture(backingTexture) | |
62 , fLayer(layer) { | |
63 if (fLayer) { | |
64 fLayer->validate(backingTexture); | |
65 } | |
66 } | |
67 ~GrAutoValidateLayer() { | |
68 if (fLayer) { | |
69 fLayer->validate(fBackingTexture); | |
70 } | |
71 } | |
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 | |
83 GrLayerCache::GrLayerCache(GrContext* context) | |
84 : fContext(context) { | |
85 memset(fPlotLocks, 0, sizeof(fPlotLocks)); | |
86 } | |
87 | |
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.reset(); | |
102 } | |
103 | |
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_GrPixel
Config, | |
110 kRenderTarget_GrSurfaceFlag, textureSize, | |
111 kNumPlotsX, kNumPlotsY)); | |
112 } | |
113 | |
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 | |
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, dstI
R, initialMat, key, | |
141 keySize, paint); | |
142 fLayerHash.add(layer); | |
143 return layer; | |
144 } | |
145 | |
146 GrCachedLayer* GrLayerCache::findLayer(uint32_t pictureID, const SkMatrix& initi
alMat, | |
147 const int* key, int keySize) { | |
148 SkASSERT(pictureID != SK_InvalidGenID); | |
149 return fLayerHash.find(GrCachedLayer::Key(pictureID, initialMat, key, keySiz
e)); | |
150 } | |
151 | |
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, initial
Mat, 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 | |
171 bool GrLayerCache::tryToAtlas(GrCachedLayer* layer, | |
172 const GrSurfaceDesc& desc, | |
173 bool* needsRendering) { | |
174 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTextureOrNull() : nu
llptr, 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 a
re able to purge | |
212 GrLayerAtlas::Plot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage, | |
213 desc.fWidth, desc.fHei
ght, | |
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.fHeig
ht); | |
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 | |
248 bool GrLayerCache::lock(GrCachedLayer* layer, const GrSurfaceDesc& desc, bool* n
eedsRendering) { | |
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 themselve
s | |
256 SkAutoTUnref<GrTexture> tex; | |
257 if (layer->fFilter) { | |
258 tex.reset(fContext->textureProvider()->createTexture(desc, SkBudgeted::k
Yes)); | |
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 | |
273 void GrLayerCache::unlock(GrCachedLayer* layer) { | |
274 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas ? fAtlas->getTextureOrNull() : nu
llptr, 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 layer
s | |
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 | |
317 void GrLayerCache::validate() const { | |
318 int plotLocks[kNumPlotsX * kNumPlotsY]; | |
319 memset(plotLocks, 0, sizeof(plotLocks)); | |
320 | |
321 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHas
h); | |
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: | |
356 explicit GrAutoValidateCache(GrLayerCache* cache) | |
357 : fCache(cache) { | |
358 fCache->validate(); | |
359 } | |
360 ~GrAutoValidateCache() { | |
361 fCache->validate(); | |
362 } | |
363 private: | |
364 GrLayerCache* fCache; | |
365 }; | |
366 #endif | |
367 | |
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 | |
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 | |
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, delet
e 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 | |
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 sk_sp<GrDrawContext> drawContext( | |
472 fContext->drawContext(sk_ref_sp(fAtlas->getTexture()->asR
enderTarget()))); | |
473 | |
474 if (drawContext) { | |
475 drawContext->discard(); | |
476 } | |
477 } | |
478 } | |
479 #endif | |
480 | |
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 | |
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 | |
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_DEBUG | |
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 | |
OLD | NEW |