| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "GrAtlas.h" | 8 #include "GrAtlas.h" |
| 9 #include "GrGpu.h" | 9 #include "GrGpu.h" |
| 10 #include "GrLayerCache.h" | 10 #include "GrLayerCache.h" |
| 11 | 11 |
| 12 DECLARE_SKMESSAGEBUS_MESSAGE(GrPictureDeletedMessage); | 12 DECLARE_SKMESSAGEBUS_MESSAGE(GrPictureDeletedMessage); |
| 13 | 13 |
| 14 #ifdef SK_DEBUG | 14 #ifdef SK_DEBUG |
| 15 void GrCachedLayer::validate(const GrTexture* backingTexture) const { | 15 void GrCachedLayer::validate(const GrTexture* backingTexture) const { |
| 16 SkASSERT(SK_InvalidGenID != fKey.getPictureID()); | 16 SkASSERT(SK_InvalidGenID != fKey.getPictureID()); |
| 17 SkASSERT(-1 != fKey.getLayerID()); | 17 SkASSERT(-1 != fKey.getLayerID()); |
| 18 | 18 |
| 19 | 19 |
| 20 if (NULL != fTexture) { | 20 if (NULL != fTexture) { |
| 21 // If the layer is in some texture then it must occupy some rectangle | 21 // If the layer is in some texture then it must occupy some rectangle |
| 22 SkASSERT(!fRect.isEmpty()); | 22 SkASSERT(!fRect.isEmpty()); |
| 23 if (!this->isAtlased()) { | 23 if (!this->isAtlased()) { |
| 24 // If it isn't atlased then the rectangle should start at the origin | 24 // If it isn't atlased then the rectangle should start at the origin |
| 25 SkASSERT(0.0f == fRect.fLeft && 0.0f == fRect.fTop); | 25 SkASSERT(0.0f == fRect.fLeft && 0.0f == fRect.fTop); |
| 26 } | 26 } |
| 27 } else { | 27 } else { |
| 28 SkASSERT(fRect.isEmpty()); | 28 SkASSERT(fRect.isEmpty()); |
| 29 SkASSERT(NULL == fPlot); | 29 SkASSERT(NULL == fPlot); |
| 30 SkASSERT(!fLocked); // layers without a texture cannot be locked |
| 30 } | 31 } |
| 31 | 32 |
| 32 if (NULL != fPlot) { | 33 if (NULL != fPlot) { |
| 33 // If a layer has a plot (i.e., is atlased) then it must point to | 34 // If a layer has a plot (i.e., is atlased) then it must point to |
| 34 // the backing texture. Additionally, its rect should be non-empty. | 35 // the backing texture. Additionally, its rect should be non-empty. |
| 35 SkASSERT(NULL != fTexture && backingTexture == fTexture); | 36 SkASSERT(NULL != fTexture && backingTexture == fTexture); |
| 36 SkASSERT(!fRect.isEmpty()); | 37 SkASSERT(!fRect.isEmpty()); |
| 37 } | 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(NULL != fTexture); |
| 44 SkASSERT(!fRect.isEmpty()); |
| 45 } |
| 38 } | 46 } |
| 39 | 47 |
| 40 class GrAutoValidateLayer : ::SkNoncopyable { | 48 class GrAutoValidateLayer : ::SkNoncopyable { |
| 41 public: | 49 public: |
| 42 GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer) | 50 GrAutoValidateLayer(GrTexture* backingTexture, const GrCachedLayer* layer) |
| 43 : fBackingTexture(backingTexture) | 51 : fBackingTexture(backingTexture) |
| 44 , fLayer(layer) { | 52 , fLayer(layer) { |
| 45 if (NULL != fLayer) { | 53 if (NULL != fLayer) { |
| 46 fLayer->validate(backingTexture); | 54 fLayer->validate(backingTexture); |
| 47 } | 55 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 58 | 66 |
| 59 private: | 67 private: |
| 60 const GrTexture* fBackingTexture; | 68 const GrTexture* fBackingTexture; |
| 61 const GrCachedLayer* fLayer; | 69 const GrCachedLayer* fLayer; |
| 62 }; | 70 }; |
| 63 #endif | 71 #endif |
| 64 | 72 |
| 65 GrLayerCache::GrLayerCache(GrContext* context) | 73 GrLayerCache::GrLayerCache(GrContext* context) |
| 66 : fContext(context) { | 74 : fContext(context) { |
| 67 this->initAtlas(); | 75 this->initAtlas(); |
| 76 memset(fPlotLocks, 0, sizeof(fPlotLocks)); |
| 68 } | 77 } |
| 69 | 78 |
| 70 GrLayerCache::~GrLayerCache() { | 79 GrLayerCache::~GrLayerCache() { |
| 71 | 80 |
| 72 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash); | 81 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash); |
| 73 for (; !iter.done(); ++iter) { | 82 for (; !iter.done(); ++iter) { |
| 74 GrCachedLayer* layer = &(*iter); | 83 GrCachedLayer* layer = &(*iter); |
| 75 this->unlock(layer); | 84 this->unlock(layer); |
| 76 SkDELETE(layer); | 85 SkDELETE(layer); |
| 77 } | 86 } |
| 78 | 87 |
| 79 // The atlas only lets go of its texture when the atlas is deleted. | 88 // The atlas only lets go of its texture when the atlas is deleted. |
| 80 fAtlas.free(); | 89 fAtlas.free(); |
| 81 } | 90 } |
| 82 | 91 |
| 83 void GrLayerCache::initAtlas() { | 92 void GrLayerCache::initAtlas() { |
| 84 static const int kAtlasTextureWidth = 1024; | |
| 85 static const int kAtlasTextureHeight = 1024; | |
| 86 | |
| 87 SkASSERT(NULL == fAtlas.get()); | 93 SkASSERT(NULL == fAtlas.get()); |
| 88 | 94 |
| 89 // The layer cache only gets 1 plot | |
| 90 SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight)
; | 95 SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight)
; |
| 91 fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfi
g, | 96 fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfi
g, |
| 92 kRenderTarget_GrTextureFlagBit, | 97 kRenderTarget_GrTextureFlagBit, |
| 93 textureSize, kNumPlotsX, kNumPlotsY, false
))); | 98 textureSize, kNumPlotsX, kNumPlotsY, false
))); |
| 94 } | 99 } |
| 95 | 100 |
| 96 void GrLayerCache::freeAll() { | 101 void GrLayerCache::freeAll() { |
| 97 | 102 |
| 98 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash); | 103 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash); |
| 99 for (; !iter.done(); ++iter) { | 104 for (; !iter.done(); ++iter) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 if (NULL == layer) { | 136 if (NULL == layer) { |
| 132 layer = this->createLayer(picture, layerID); | 137 layer = this->createLayer(picture, layerID); |
| 133 } | 138 } |
| 134 | 139 |
| 135 return layer; | 140 return layer; |
| 136 } | 141 } |
| 137 | 142 |
| 138 bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) { | 143 bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) { |
| 139 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);) | 144 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);) |
| 140 | 145 |
| 141 if (NULL != layer->texture()) { | 146 if (layer->locked()) { |
| 142 // This layer is already locked | 147 // This layer is already locked |
| 143 #ifdef SK_DEBUG | 148 #ifdef SK_DEBUG |
| 144 if (layer->isAtlased()) { | 149 if (layer->isAtlased()) { |
| 145 // It claims to be atlased | 150 // It claims to be atlased |
| 146 SkASSERT(layer->rect().width() == desc.fWidth); | 151 SkASSERT(layer->rect().width() == desc.fWidth); |
| 147 SkASSERT(layer->rect().height() == desc.fHeight); | 152 SkASSERT(layer->rect().height() == desc.fHeight); |
| 148 } | 153 } |
| 149 #endif | 154 #endif |
| 150 return true; | 155 return true; |
| 151 } | 156 } |
| 152 | 157 |
| 153 #if USE_ATLAS | 158 #if USE_ATLAS |
| 154 { | 159 if (layer->isAtlased()) { |
| 160 // Hooray it is still in the atlas - make sure it stays there |
| 161 layer->setLocked(true); |
| 162 fPlotLocks[layer->plot()->id()]++; |
| 163 return true; |
| 164 } else if (PlausiblyAtlasable(desc.fWidth, desc.fHeight)) { |
| 165 // Not in the atlas - will it fit? |
| 155 GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); | 166 GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); |
| 156 if (NULL == pictInfo) { | 167 if (NULL == pictInfo) { |
| 157 pictInfo = SkNEW_ARGS(GrPictureInfo, (layer->pictureID())); | 168 pictInfo = SkNEW_ARGS(GrPictureInfo, (layer->pictureID())); |
| 158 fPictureHash.add(pictInfo); | 169 fPictureHash.add(pictInfo); |
| 159 } | 170 } |
| 160 | 171 |
| 161 SkIPoint16 loc; | 172 SkIPoint16 loc; |
| 162 GrPlot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage, | 173 for (int i = 0; i < 2; ++i) { // extra pass in case we fail to add but a
re able to purge |
| 163 desc.fWidth, desc.fHeight, | 174 GrPlot* plot = fAtlas->addToAtlas(&pictInfo->fPlotUsage, |
| 164 NULL, &loc); | 175 desc.fWidth, desc.fHeight, |
| 165 // addToAtlas can allocate the backing texture | 176 NULL, &loc); |
| 166 SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture())); | 177 // addToAtlas can allocate the backing texture |
| 167 if (NULL != plot) { | 178 SkDEBUGCODE(avl.setBackingTexture(fAtlas->getTexture())); |
| 168 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY, | 179 if (NULL != plot) { |
| 169 SkToS16(desc.fWidth), SkToS16
(desc.fHeight)); | 180 // The layer was successfully added to the atlas |
| 170 layer->setTexture(fAtlas->getTexture(), bounds); | 181 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY, |
| 171 layer->setPlot(plot); | 182 SkToS16(desc.fWidth), |
| 172 return false; | 183 SkToS16(desc.fHeight)); |
| 184 layer->setTexture(fAtlas->getTexture(), bounds); |
| 185 layer->setPlot(plot); |
| 186 layer->setLocked(true); |
| 187 fPlotLocks[layer->plot()->id()]++; |
| 188 return false; |
| 189 } |
| 190 |
| 191 // The layer was rejected by the atlas (even though we know it is |
| 192 // plausibly atlas-able). See if a plot can be purged and try again. |
| 193 if (!this->purgePlot()) { |
| 194 break; // We weren't able to purge any plots |
| 195 } |
| 173 } | 196 } |
| 174 } | 197 } |
| 175 #endif | 198 #endif |
| 176 | 199 |
| 177 // The texture wouldn't fit in the cache - give it it's own texture. | 200 // The texture wouldn't fit in the cache - give it it's own texture. |
| 178 // This path always uses a new scratch texture and (thus) doesn't cache anyt
hing. | 201 // This path always uses a new scratch texture and (thus) doesn't cache anyt
hing. |
| 179 // This can yield a lot of re-rendering | 202 // This can yield a lot of re-rendering |
| 180 layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kAppro
x_ScratchTexMatch), | 203 layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kAppro
x_ScratchTexMatch), |
| 181 GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeig
ht))); | 204 GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeig
ht))); |
| 205 layer->setLocked(true); |
| 182 return false; | 206 return false; |
| 183 } | 207 } |
| 184 | 208 |
| 185 void GrLayerCache::unlock(GrCachedLayer* layer) { | 209 void GrLayerCache::unlock(GrCachedLayer* layer) { |
| 186 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);) | 210 SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);) |
| 187 | 211 |
| 188 if (NULL == layer || NULL == layer->texture()) { | 212 if (NULL == layer || !layer->locked()) { |
| 213 // invalid or not locked |
| 189 return; | 214 return; |
| 190 } | 215 } |
| 191 | 216 |
| 192 if (layer->isAtlased()) { | 217 if (layer->isAtlased()) { |
| 193 SkASSERT(layer->texture() == fAtlas->getTexture()); | 218 const int plotID = layer->plot()->id(); |
| 194 | 219 |
| 195 GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); | 220 SkASSERT(fPlotLocks[plotID] > 0); |
| 196 SkASSERT(NULL != pictInfo); | 221 fPlotLocks[plotID]--; |
| 197 pictInfo->fPlotUsage.isEmpty(); // just to silence compiler warnings for
the time being | 222 // At this point we could aggressively clear out un-locked plots but |
| 198 | 223 // by delaying we may be able to reuse some of the atlased layers later. |
| 199 // TODO: purging from atlas goes here | |
| 200 } else { | 224 } else { |
| 201 fContext->unlockScratchTexture(layer->texture()); | 225 fContext->unlockScratchTexture(layer->texture()); |
| 202 layer->setTexture(NULL, GrIRect16::MakeEmpty()); | 226 layer->setTexture(NULL, GrIRect16::MakeEmpty()); |
| 203 } | 227 } |
| 228 |
| 229 layer->setLocked(false); |
| 204 } | 230 } |
| 205 | 231 |
| 206 #ifdef SK_DEBUG | 232 #ifdef SK_DEBUG |
| 207 void GrLayerCache::validate() const { | 233 void GrLayerCache::validate() const { |
| 234 int plotLocks[kNumPlotsX * kNumPlotsY]; |
| 235 memset(plotLocks, 0, sizeof(plotLocks)); |
| 236 |
| 208 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHas
h); | 237 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::ConstIter iter(&fLayerHas
h); |
| 209 for (; !iter.done(); ++iter) { | 238 for (; !iter.done(); ++iter) { |
| 210 (*iter).validate(fAtlas->getTexture()); | 239 const GrCachedLayer* layer = &(*iter); |
| 240 |
| 241 layer->validate(fAtlas->getTexture()); |
| 242 |
| 243 const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); |
| 244 if (NULL != pictInfo) { |
| 245 // In aggressive cleanup mode a picture info should only exist if |
| 246 // it has some atlased layers |
| 247 SkASSERT(!pictInfo->fPlotUsage.isEmpty()); |
| 248 } else { |
| 249 // If there is no picture info for this layer then all of its |
| 250 // layers should be non-atlased. |
| 251 SkASSERT(!layer->isAtlased()); |
| 252 } |
| 253 |
| 254 if (NULL != layer->plot()) { |
| 255 SkASSERT(NULL != pictInfo); |
| 256 SkASSERT(pictInfo->fPictureID == layer->pictureID()); |
| 257 |
| 258 SkASSERT(pictInfo->fPlotUsage.contains(layer->plot())); |
| 259 |
| 260 if (layer->locked()) { |
| 261 plotLocks[layer->plot()->id()]++; |
| 262 } |
| 263 } |
| 264 } |
| 265 |
| 266 for (int i = 0; i < kNumPlotsX*kNumPlotsY; ++i) { |
| 267 SkASSERT(plotLocks[i] == fPlotLocks[i]); |
| 211 } | 268 } |
| 212 } | 269 } |
| 213 | 270 |
| 214 class GrAutoValidateCache : ::SkNoncopyable { | 271 class GrAutoValidateCache : ::SkNoncopyable { |
| 215 public: | 272 public: |
| 216 explicit GrAutoValidateCache(GrLayerCache* cache) | 273 explicit GrAutoValidateCache(GrLayerCache* cache) |
| 217 : fCache(cache) { | 274 : fCache(cache) { |
| 218 fCache->validate(); | 275 fCache->validate(); |
| 219 } | 276 } |
| 220 ~GrAutoValidateCache() { | 277 ~GrAutoValidateCache() { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 245 SkDELETE(toBeRemoved[i]); | 302 SkDELETE(toBeRemoved[i]); |
| 246 } | 303 } |
| 247 | 304 |
| 248 GrPictureInfo* pictInfo = fPictureHash.find(pictureID); | 305 GrPictureInfo* pictInfo = fPictureHash.find(pictureID); |
| 249 if (NULL != pictInfo) { | 306 if (NULL != pictInfo) { |
| 250 fPictureHash.remove(pictureID); | 307 fPictureHash.remove(pictureID); |
| 251 SkDELETE(pictInfo); | 308 SkDELETE(pictInfo); |
| 252 } | 309 } |
| 253 } | 310 } |
| 254 | 311 |
| 312 bool GrLayerCache::purgePlot() { |
| 313 SkDEBUGCODE(GrAutoValidateCache avc(this);) |
| 314 |
| 315 GrAtlas::PlotIter iter; |
| 316 GrPlot* plot; |
| 317 for (plot = fAtlas->iterInit(&iter, GrAtlas::kLRUFirst_IterOrder); |
| 318 NULL != plot; |
| 319 plot = iter.prev()) { |
| 320 if (fPlotLocks[plot->id()] > 0) { |
| 321 continue; |
| 322 } |
| 323 |
| 324 // We need to find all the layers in 'plot' and remove them. |
| 325 SkTDArray<GrCachedLayer*> toBeRemoved; |
| 326 |
| 327 SkTDynamicHash<GrCachedLayer, GrCachedLayer::Key>::Iter iter(&fLayerHash
); |
| 328 for (; !iter.done(); ++iter) { |
| 329 if (plot == (*iter).plot()) { |
| 330 *toBeRemoved.append() = &(*iter); |
| 331 } |
| 332 } |
| 333 |
| 334 for (int i = 0; i < toBeRemoved.count(); ++i) { |
| 335 SkASSERT(!toBeRemoved[i]->locked()); |
| 336 |
| 337 GrPictureInfo* pictInfo = fPictureHash.find(toBeRemoved[i]->pictureI
D()); |
| 338 SkASSERT(NULL != pictInfo); |
| 339 |
| 340 GrAtlas::RemovePlot(&pictInfo->fPlotUsage, plot); |
| 341 |
| 342 // Aggressively remove layers and, if now totally uncached, picture
info |
| 343 fLayerHash.remove(GrCachedLayer::GetKey(*toBeRemoved[i])); |
| 344 SkDELETE(toBeRemoved[i]); |
| 345 |
| 346 if (pictInfo->fPlotUsage.isEmpty()) { |
| 347 fPictureHash.remove(pictInfo->fPictureID); |
| 348 SkDELETE(pictInfo); |
| 349 } |
| 350 } |
| 351 |
| 352 plot->resetRects(); |
| 353 return true; |
| 354 } |
| 355 |
| 356 return false; |
| 357 } |
| 358 |
| 255 class GrPictureDeletionListener : public SkPicture::DeletionListener { | 359 class GrPictureDeletionListener : public SkPicture::DeletionListener { |
| 256 virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{ | 360 virtual void onDeletion(uint32_t pictureID) SK_OVERRIDE{ |
| 257 const GrPictureDeletedMessage message = { pictureID }; | 361 const GrPictureDeletedMessage message = { pictureID }; |
| 258 SkMessageBus<GrPictureDeletedMessage>::Post(message); | 362 SkMessageBus<GrPictureDeletedMessage>::Post(message); |
| 259 } | 363 } |
| 260 }; | 364 }; |
| 261 | 365 |
| 262 void GrLayerCache::trackPicture(const SkPicture* picture) { | 366 void GrLayerCache::trackPicture(const SkPicture* picture) { |
| 263 if (NULL == fDeletionListener) { | 367 if (NULL == fDeletionListener) { |
| 264 fDeletionListener.reset(SkNEW(GrPictureDeletionListener)); | 368 fDeletionListener.reset(SkNEW(GrPictureDeletionListener)); |
| 265 } | 369 } |
| 266 | 370 |
| 267 picture->addDeletionListener(fDeletionListener); | 371 picture->addDeletionListener(fDeletionListener); |
| 268 } | 372 } |
| 269 | 373 |
| 270 void GrLayerCache::processDeletedPictures() { | 374 void GrLayerCache::processDeletedPictures() { |
| 271 SkTDArray<GrPictureDeletedMessage> deletedPictures; | 375 SkTDArray<GrPictureDeletedMessage> deletedPictures; |
| 272 fPictDeletionInbox.poll(&deletedPictures); | 376 fPictDeletionInbox.poll(&deletedPictures); |
| 273 | 377 |
| 274 for (int i = 0; i < deletedPictures.count(); i++) { | 378 for (int i = 0; i < deletedPictures.count(); i++) { |
| 275 this->purge(deletedPictures[i].pictureID); | 379 this->purge(deletedPictures[i].pictureID); |
| 276 } | 380 } |
| 277 } | 381 } |
| 278 | 382 |
| OLD | NEW |