SkResourceCache.cpp revision e6cb48382db869f8e194b83aec80fd495be76db7
1/* 2 * Copyright 2013 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 "SkChecksum.h" 9#include "SkResourceCache.h" 10#include "SkMipMap.h" 11#include "SkPixelRef.h" 12 13// This can be defined by the caller's build system 14//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE 15 16#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 17# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024 18#endif 19 20#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT 21 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024) 22#endif 23 24void SkResourceCache::Key::init(size_t length) { 25 SkASSERT(SkAlign4(length) == length); 26 // 2 is fCount32 and fHash 27 fCount32 = SkToS32(2 + (length >> 2)); 28 // skip both of our fields whe computing the murmur 29 fHash = SkChecksum::Murmur3(this->as32() + 2, (fCount32 - 2) << 2); 30} 31 32#include "SkTDynamicHash.h" 33 34class SkResourceCache::Hash : 35 public SkTDynamicHash<SkResourceCache::Rec, SkResourceCache::Key> {}; 36 37 38/////////////////////////////////////////////////////////////////////////////// 39 40void SkResourceCache::init() { 41 fHead = NULL; 42 fTail = NULL; 43 fHash = new Hash; 44 fTotalBytesUsed = 0; 45 fCount = 0; 46 fSingleAllocationByteLimit = 0; 47 fAllocator = NULL; 48 49 // One of these should be explicit set by the caller after we return. 50 fTotalByteLimit = 0; 51 fDiscardableFactory = NULL; 52} 53 54#include "SkDiscardableMemory.h" 55 56class SkOneShotDiscardablePixelRef : public SkPixelRef { 57public: 58 SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef) 59 // Ownership of the discardablememory is transfered to the pixelref 60 SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes); 61 ~SkOneShotDiscardablePixelRef(); 62 63protected: 64 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE; 65 virtual void onUnlockPixels() SK_OVERRIDE; 66 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE; 67 68private: 69 SkDiscardableMemory* fDM; 70 size_t fRB; 71 bool fFirstTime; 72 73 typedef SkPixelRef INHERITED; 74}; 75 76SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info, 77 SkDiscardableMemory* dm, 78 size_t rowBytes) 79 : INHERITED(info) 80 , fDM(dm) 81 , fRB(rowBytes) 82{ 83 SkASSERT(dm->data()); 84 fFirstTime = true; 85} 86 87SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() { 88 SkDELETE(fDM); 89} 90 91bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) { 92 if (fFirstTime) { 93 // we're already locked 94 SkASSERT(fDM->data()); 95 fFirstTime = false; 96 goto SUCCESS; 97 } 98 99 // A previous call to onUnlock may have deleted our DM, so check for that 100 if (NULL == fDM) { 101 return false; 102 } 103 104 if (!fDM->lock()) { 105 // since it failed, we delete it now, to free-up the resource 106 delete fDM; 107 fDM = NULL; 108 return false; 109 } 110 111SUCCESS: 112 rec->fPixels = fDM->data(); 113 rec->fColorTable = NULL; 114 rec->fRowBytes = fRB; 115 return true; 116} 117 118void SkOneShotDiscardablePixelRef::onUnlockPixels() { 119 SkASSERT(!fFirstTime); 120 fDM->unlock(); 121} 122 123size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const { 124 return this->info().getSafeSize(fRB); 125} 126 127class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator { 128public: 129 SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) { 130 SkASSERT(factory); 131 fFactory = factory; 132 } 133 134 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE; 135 136private: 137 SkResourceCache::DiscardableFactory fFactory; 138}; 139 140bool SkResourceCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 141 size_t size = bitmap->getSize(); 142 uint64_t size64 = bitmap->computeSize64(); 143 if (0 == size || size64 > (uint64_t)size) { 144 return false; 145 } 146 147 SkDiscardableMemory* dm = fFactory(size); 148 if (NULL == dm) { 149 return false; 150 } 151 152 // can we relax this? 153 if (kN32_SkColorType != bitmap->colorType()) { 154 return false; 155 } 156 157 SkImageInfo info = bitmap->info(); 158 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef, 159 (info, dm, bitmap->rowBytes())))->unref(); 160 bitmap->lockPixels(); 161 return bitmap->readyToDraw(); 162} 163 164SkResourceCache::SkResourceCache(DiscardableFactory factory) { 165 this->init(); 166 fDiscardableFactory = factory; 167 168 fAllocator = SkNEW_ARGS(SkResourceCacheDiscardableAllocator, (factory)); 169} 170 171SkResourceCache::SkResourceCache(size_t byteLimit) { 172 this->init(); 173 fTotalByteLimit = byteLimit; 174} 175 176SkResourceCache::~SkResourceCache() { 177 SkSafeUnref(fAllocator); 178 179 Rec* rec = fHead; 180 while (rec) { 181 Rec* next = rec->fNext; 182 SkDELETE(rec); 183 rec = next; 184 } 185 delete fHash; 186} 187 188//////////////////////////////////////////////////////////////////////////////// 189 190const SkResourceCache::Rec* SkResourceCache::findAndLock(const Key& key) { 191 Rec* rec = fHash->find(key); 192 if (rec) { 193 this->moveToHead(rec); // for our LRU 194 rec->fLockCount += 1; 195 } 196 return rec; 197} 198 199const SkResourceCache::Rec* SkResourceCache::addAndLock(Rec* rec) { 200 SkASSERT(rec); 201 // See if we already have this key (racy inserts, etc.) 202 const Rec* existing = this->findAndLock(rec->getKey()); 203 if (existing) { 204 SkDELETE(rec); 205 return existing; 206 } 207 208 this->addToHead(rec); 209 SkASSERT(1 == rec->fLockCount); 210 fHash->add(rec); 211 // We may (now) be overbudget, so see if we need to purge something. 212 this->purgeAsNeeded(); 213 return rec; 214} 215 216void SkResourceCache::add(Rec* rec) { 217 SkASSERT(rec); 218 // See if we already have this key (racy inserts, etc.) 219 const Rec* existing = this->findAndLock(rec->getKey()); 220 if (existing) { 221 SkDELETE(rec); 222 this->unlock(existing); 223 return; 224 } 225 226 this->addToHead(rec); 227 SkASSERT(1 == rec->fLockCount); 228 fHash->add(rec); 229 this->unlock(rec); 230} 231 232void SkResourceCache::unlock(SkResourceCache::ID id) { 233 SkASSERT(id); 234 235#ifdef SK_DEBUG 236 { 237 bool found = false; 238 Rec* rec = fHead; 239 while (rec != NULL) { 240 if (rec == id) { 241 found = true; 242 break; 243 } 244 rec = rec->fNext; 245 } 246 SkASSERT(found); 247 } 248#endif 249 const Rec* rec = id; 250 SkASSERT(rec->fLockCount > 0); 251 // We're under our lock, and we're the only possible mutator, so unconsting is fine. 252 const_cast<Rec*>(rec)->fLockCount -= 1; 253 254 // we may have been over-budget, but now have released something, so check 255 // if we should purge. 256 if (0 == rec->fLockCount) { 257 this->purgeAsNeeded(); 258 } 259} 260 261void SkResourceCache::remove(Rec* rec) { 262 SkASSERT(0 == rec->fLockCount); 263 264 size_t used = rec->bytesUsed(); 265 SkASSERT(used <= fTotalBytesUsed); 266 267 this->detach(rec); 268 fHash->remove(rec->getKey()); 269 270 SkDELETE(rec); 271 272 fTotalBytesUsed -= used; 273 fCount -= 1; 274} 275 276void SkResourceCache::purgeAsNeeded(bool forcePurge) { 277 size_t byteLimit; 278 int countLimit; 279 280 if (fDiscardableFactory) { 281 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT; 282 byteLimit = SK_MaxU32; // no limit based on bytes 283 } else { 284 countLimit = SK_MaxS32; // no limit based on count 285 byteLimit = fTotalByteLimit; 286 } 287 288 Rec* rec = fTail; 289 while (rec) { 290 if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) { 291 break; 292 } 293 294 Rec* prev = rec->fPrev; 295 if (0 == rec->fLockCount) { 296 this->remove(rec); 297 } 298 rec = prev; 299 } 300} 301 302size_t SkResourceCache::setTotalByteLimit(size_t newLimit) { 303 size_t prevLimit = fTotalByteLimit; 304 fTotalByteLimit = newLimit; 305 if (newLimit < prevLimit) { 306 this->purgeAsNeeded(); 307 } 308 return prevLimit; 309} 310 311/////////////////////////////////////////////////////////////////////////////// 312 313void SkResourceCache::detach(Rec* rec) { 314 Rec* prev = rec->fPrev; 315 Rec* next = rec->fNext; 316 317 if (!prev) { 318 SkASSERT(fHead == rec); 319 fHead = next; 320 } else { 321 prev->fNext = next; 322 } 323 324 if (!next) { 325 fTail = prev; 326 } else { 327 next->fPrev = prev; 328 } 329 330 rec->fNext = rec->fPrev = NULL; 331} 332 333void SkResourceCache::moveToHead(Rec* rec) { 334 if (fHead == rec) { 335 return; 336 } 337 338 SkASSERT(fHead); 339 SkASSERT(fTail); 340 341 this->validate(); 342 343 this->detach(rec); 344 345 fHead->fPrev = rec; 346 rec->fNext = fHead; 347 fHead = rec; 348 349 this->validate(); 350} 351 352void SkResourceCache::addToHead(Rec* rec) { 353 this->validate(); 354 355 rec->fPrev = NULL; 356 rec->fNext = fHead; 357 if (fHead) { 358 fHead->fPrev = rec; 359 } 360 fHead = rec; 361 if (!fTail) { 362 fTail = rec; 363 } 364 fTotalBytesUsed += rec->bytesUsed(); 365 fCount += 1; 366 367 this->validate(); 368} 369 370/////////////////////////////////////////////////////////////////////////////// 371 372#ifdef SK_DEBUG 373void SkResourceCache::validate() const { 374 if (NULL == fHead) { 375 SkASSERT(NULL == fTail); 376 SkASSERT(0 == fTotalBytesUsed); 377 return; 378 } 379 380 if (fHead == fTail) { 381 SkASSERT(NULL == fHead->fPrev); 382 SkASSERT(NULL == fHead->fNext); 383 SkASSERT(fHead->bytesUsed() == fTotalBytesUsed); 384 return; 385 } 386 387 SkASSERT(NULL == fHead->fPrev); 388 SkASSERT(fHead->fNext); 389 SkASSERT(NULL == fTail->fNext); 390 SkASSERT(fTail->fPrev); 391 392 size_t used = 0; 393 int count = 0; 394 const Rec* rec = fHead; 395 while (rec) { 396 count += 1; 397 used += rec->bytesUsed(); 398 SkASSERT(used <= fTotalBytesUsed); 399 rec = rec->fNext; 400 } 401 SkASSERT(fCount == count); 402 403 rec = fTail; 404 while (rec) { 405 SkASSERT(count > 0); 406 count -= 1; 407 SkASSERT(used >= rec->bytesUsed()); 408 used -= rec->bytesUsed(); 409 rec = rec->fPrev; 410 } 411 412 SkASSERT(0 == count); 413 SkASSERT(0 == used); 414} 415#endif 416 417void SkResourceCache::dump() const { 418 this->validate(); 419 420 const Rec* rec = fHead; 421 int locked = 0; 422 while (rec) { 423 locked += rec->fLockCount > 0; 424 rec = rec->fNext; 425 } 426 427 SkDebugf("SkResourceCache: count=%d bytes=%d locked=%d %s\n", 428 fCount, fTotalBytesUsed, locked, 429 fDiscardableFactory ? "discardable" : "malloc"); 430} 431 432size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) { 433 size_t oldLimit = fSingleAllocationByteLimit; 434 fSingleAllocationByteLimit = newLimit; 435 return oldLimit; 436} 437 438size_t SkResourceCache::getSingleAllocationByteLimit() const { 439 return fSingleAllocationByteLimit; 440} 441 442/////////////////////////////////////////////////////////////////////////////// 443 444#include "SkThread.h" 445 446SK_DECLARE_STATIC_MUTEX(gMutex); 447static SkResourceCache* gResourceCache = NULL; 448static void cleanup_gResourceCache() { 449 // We'll clean this up in our own tests, but disable for clients. 450 // Chrome seems to have funky multi-process things going on in unit tests that 451 // makes this unsafe to delete when the main process atexit()s. 452 // SkLazyPtr does the same sort of thing. 453#if SK_DEVELOPER 454 SkDELETE(gResourceCache); 455#endif 456} 457 458/** Must hold gMutex when calling. */ 459static SkResourceCache* get_cache() { 460 // gMutex is always held when this is called, so we don't need to be fancy in here. 461 gMutex.assertHeld(); 462 if (NULL == gResourceCache) { 463#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE 464 gResourceCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create)); 465#else 466 gResourceCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT)); 467#endif 468 atexit(cleanup_gResourceCache); 469 } 470 return gResourceCache; 471} 472 473void SkResourceCache::Unlock(SkResourceCache::ID id) { 474 SkAutoMutexAcquire am(gMutex); 475 get_cache()->unlock(id); 476 477// get_cache()->dump(); 478} 479 480void SkResourceCache::Remove(SkResourceCache::ID id) { 481 SkAutoMutexAcquire am(gMutex); 482 SkASSERT(id); 483 484#ifdef SK_DEBUG 485 { 486 bool found = false; 487 Rec* rec = get_cache()->fHead; 488 while (rec != NULL) { 489 if (rec == id) { 490 found = true; 491 break; 492 } 493 rec = rec->fNext; 494 } 495 SkASSERT(found); 496 } 497#endif 498 const Rec* rec = id; 499 get_cache()->remove(const_cast<Rec*>(rec)); 500} 501 502size_t SkResourceCache::GetTotalBytesUsed() { 503 SkAutoMutexAcquire am(gMutex); 504 return get_cache()->getTotalBytesUsed(); 505} 506 507size_t SkResourceCache::GetTotalByteLimit() { 508 SkAutoMutexAcquire am(gMutex); 509 return get_cache()->getTotalByteLimit(); 510} 511 512size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) { 513 SkAutoMutexAcquire am(gMutex); 514 return get_cache()->setTotalByteLimit(newLimit); 515} 516 517SkBitmap::Allocator* SkResourceCache::GetAllocator() { 518 SkAutoMutexAcquire am(gMutex); 519 return get_cache()->allocator(); 520} 521 522void SkResourceCache::Dump() { 523 SkAutoMutexAcquire am(gMutex); 524 get_cache()->dump(); 525} 526 527size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) { 528 SkAutoMutexAcquire am(gMutex); 529 return get_cache()->setSingleAllocationByteLimit(size); 530} 531 532size_t SkResourceCache::GetSingleAllocationByteLimit() { 533 SkAutoMutexAcquire am(gMutex); 534 return get_cache()->getSingleAllocationByteLimit(); 535} 536 537void SkResourceCache::PurgeAll() { 538 SkAutoMutexAcquire am(gMutex); 539 return get_cache()->purgeAll(); 540} 541 542const SkResourceCache::Rec* SkResourceCache::FindAndLock(const Key& key) { 543 SkAutoMutexAcquire am(gMutex); 544 return get_cache()->findAndLock(key); 545} 546 547const SkResourceCache::Rec* SkResourceCache::AddAndLock(Rec* rec) { 548 SkAutoMutexAcquire am(gMutex); 549 return get_cache()->addAndLock(rec); 550} 551 552void SkResourceCache::Add(Rec* rec) { 553 SkAutoMutexAcquire am(gMutex); 554 get_cache()->add(rec); 555} 556 557/////////////////////////////////////////////////////////////////////////////// 558 559#include "SkGraphics.h" 560 561size_t SkGraphics::GetResourceCacheTotalBytesUsed() { 562 return SkResourceCache::GetTotalBytesUsed(); 563} 564 565size_t SkGraphics::GetResourceCacheTotalByteLimit() { 566 return SkResourceCache::GetTotalByteLimit(); 567} 568 569size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) { 570 return SkResourceCache::SetTotalByteLimit(newLimit); 571} 572 573size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() { 574 return SkResourceCache::GetSingleAllocationByteLimit(); 575} 576 577size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) { 578 return SkResourceCache::SetSingleAllocationByteLimit(newLimit); 579} 580 581void SkGraphics::PurgeResourceCache() { 582 return SkResourceCache::PurgeAll(); 583} 584 585