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