| 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 |