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