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 24 void 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 34 class SkResourceCache::Hash : 35 public SkTDynamicHash<SkResourceCache::Rec, SkResourceCache::Key> {}; 36 37 38 /////////////////////////////////////////////////////////////////////////////// 39 40 void 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 56 class SkOneShotDiscardablePixelRef : public SkPixelRef { 57 public: 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 63 protected: 64 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE; 65 virtual void onUnlockPixels() SK_OVERRIDE; 66 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE; 67 68 private: 69 SkDiscardableMemory* fDM; 70 size_t fRB; 71 bool fFirstTime; 72 73 typedef SkPixelRef INHERITED; 74 }; 75 76 SkOneShotDiscardablePixelRef::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 87 SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() { 88 SkDELETE(fDM); 89 } 90 91 bool 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 111 SUCCESS: 112 rec->fPixels = fDM->data(); 113 rec->fColorTable = NULL; 114 rec->fRowBytes = fRB; 115 return true; 116 } 117 118 void SkOneShotDiscardablePixelRef::onUnlockPixels() { 119 SkASSERT(!fFirstTime); 120 fDM->unlock(); 121 } 122 123 size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const { 124 return this->info().getSafeSize(fRB); 125 } 126 127 class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator { 128 public: 129 SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory) { 130 SkASSERT(factory); 131 fFactory = factory; 132 } 133 134 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE; 135 136 private: 137 SkResourceCache::DiscardableFactory fFactory; 138 }; 139 140 bool 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 164 SkResourceCache::SkResourceCache(DiscardableFactory factory) { 165 this->init(); 166 fDiscardableFactory = factory; 167 168 fAllocator = SkNEW_ARGS(SkResourceCacheDiscardableAllocator, (factory)); 169 } 170 171 SkResourceCache::SkResourceCache(size_t byteLimit) { 172 this->init(); 173 fTotalByteLimit = byteLimit; 174 } 175 176 SkResourceCache::~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 190 bool SkResourceCache::find(const Key& key, VisitorProc visitor, void* context) { 191 Rec* rec = fHash->find(key); 192 if (rec) { 193 if (visitor(*rec, context)) { 194 this->moveToHead(rec); // for our LRU 195 return true; 196 } else { 197 this->remove(rec); // stale 198 return false; 199 } 200 } 201 return false; 202 } 203 204 void SkResourceCache::add(Rec* rec) { 205 SkASSERT(rec); 206 // See if we already have this key (racy inserts, etc.) 207 Rec* existing = fHash->find(rec->getKey()); 208 if (existing) { 209 SkDELETE(rec); 210 return; 211 } 212 213 this->addToHead(rec); 214 fHash->add(rec); 215 216 // since the new rec may push us over-budget, we perform a purge check now 217 this->purgeAsNeeded(); 218 } 219 220 void SkResourceCache::remove(Rec* rec) { 221 size_t used = rec->bytesUsed(); 222 SkASSERT(used <= fTotalBytesUsed); 223 224 this->detach(rec); 225 fHash->remove(rec->getKey()); 226 227 SkDELETE(rec); 228 229 fTotalBytesUsed -= used; 230 fCount -= 1; 231 } 232 233 void SkResourceCache::purgeAsNeeded(bool forcePurge) { 234 size_t byteLimit; 235 int countLimit; 236 237 if (fDiscardableFactory) { 238 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT; 239 byteLimit = SK_MaxU32; // no limit based on bytes 240 } else { 241 countLimit = SK_MaxS32; // no limit based on count 242 byteLimit = fTotalByteLimit; 243 } 244 245 Rec* rec = fTail; 246 while (rec) { 247 if (!forcePurge && fTotalBytesUsed < byteLimit && fCount < countLimit) { 248 break; 249 } 250 251 Rec* prev = rec->fPrev; 252 this->remove(rec); 253 rec = prev; 254 } 255 } 256 257 size_t SkResourceCache::setTotalByteLimit(size_t newLimit) { 258 size_t prevLimit = fTotalByteLimit; 259 fTotalByteLimit = newLimit; 260 if (newLimit < prevLimit) { 261 this->purgeAsNeeded(); 262 } 263 return prevLimit; 264 } 265 266 /////////////////////////////////////////////////////////////////////////////// 267 268 void SkResourceCache::detach(Rec* rec) { 269 Rec* prev = rec->fPrev; 270 Rec* next = rec->fNext; 271 272 if (!prev) { 273 SkASSERT(fHead == rec); 274 fHead = next; 275 } else { 276 prev->fNext = next; 277 } 278 279 if (!next) { 280 fTail = prev; 281 } else { 282 next->fPrev = prev; 283 } 284 285 rec->fNext = rec->fPrev = NULL; 286 } 287 288 void SkResourceCache::moveToHead(Rec* rec) { 289 if (fHead == rec) { 290 return; 291 } 292 293 SkASSERT(fHead); 294 SkASSERT(fTail); 295 296 this->validate(); 297 298 this->detach(rec); 299 300 fHead->fPrev = rec; 301 rec->fNext = fHead; 302 fHead = rec; 303 304 this->validate(); 305 } 306 307 void SkResourceCache::addToHead(Rec* rec) { 308 this->validate(); 309 310 rec->fPrev = NULL; 311 rec->fNext = fHead; 312 if (fHead) { 313 fHead->fPrev = rec; 314 } 315 fHead = rec; 316 if (!fTail) { 317 fTail = rec; 318 } 319 fTotalBytesUsed += rec->bytesUsed(); 320 fCount += 1; 321 322 this->validate(); 323 } 324 325 /////////////////////////////////////////////////////////////////////////////// 326 327 #ifdef SK_DEBUG 328 void SkResourceCache::validate() const { 329 if (NULL == fHead) { 330 SkASSERT(NULL == fTail); 331 SkASSERT(0 == fTotalBytesUsed); 332 return; 333 } 334 335 if (fHead == fTail) { 336 SkASSERT(NULL == fHead->fPrev); 337 SkASSERT(NULL == fHead->fNext); 338 SkASSERT(fHead->bytesUsed() == fTotalBytesUsed); 339 return; 340 } 341 342 SkASSERT(NULL == fHead->fPrev); 343 SkASSERT(fHead->fNext); 344 SkASSERT(NULL == fTail->fNext); 345 SkASSERT(fTail->fPrev); 346 347 size_t used = 0; 348 int count = 0; 349 const Rec* rec = fHead; 350 while (rec) { 351 count += 1; 352 used += rec->bytesUsed(); 353 SkASSERT(used <= fTotalBytesUsed); 354 rec = rec->fNext; 355 } 356 SkASSERT(fCount == count); 357 358 rec = fTail; 359 while (rec) { 360 SkASSERT(count > 0); 361 count -= 1; 362 SkASSERT(used >= rec->bytesUsed()); 363 used -= rec->bytesUsed(); 364 rec = rec->fPrev; 365 } 366 367 SkASSERT(0 == count); 368 SkASSERT(0 == used); 369 } 370 #endif 371 372 void SkResourceCache::dump() const { 373 this->validate(); 374 375 SkDebugf("SkResourceCache: count=%d bytes=%d %s\n", 376 fCount, fTotalBytesUsed, fDiscardableFactory ? "discardable" : "malloc"); 377 } 378 379 size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) { 380 size_t oldLimit = fSingleAllocationByteLimit; 381 fSingleAllocationByteLimit = newLimit; 382 return oldLimit; 383 } 384 385 size_t SkResourceCache::getSingleAllocationByteLimit() const { 386 return fSingleAllocationByteLimit; 387 } 388 389 /////////////////////////////////////////////////////////////////////////////// 390 391 #include "SkThread.h" 392 393 SK_DECLARE_STATIC_MUTEX(gMutex); 394 static SkResourceCache* gResourceCache = NULL; 395 static void cleanup_gResourceCache() { 396 // We'll clean this up in our own tests, but disable for clients. 397 // Chrome seems to have funky multi-process things going on in unit tests that 398 // makes this unsafe to delete when the main process atexit()s. 399 // SkLazyPtr does the same sort of thing. 400 #if SK_DEVELOPER 401 SkDELETE(gResourceCache); 402 #endif 403 } 404 405 /** Must hold gMutex when calling. */ 406 static SkResourceCache* get_cache() { 407 // gMutex is always held when this is called, so we don't need to be fancy in here. 408 gMutex.assertHeld(); 409 if (NULL == gResourceCache) { 410 #ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE 411 gResourceCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create)); 412 #else 413 gResourceCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT)); 414 #endif 415 atexit(cleanup_gResourceCache); 416 } 417 return gResourceCache; 418 } 419 420 size_t SkResourceCache::GetTotalBytesUsed() { 421 SkAutoMutexAcquire am(gMutex); 422 return get_cache()->getTotalBytesUsed(); 423 } 424 425 size_t SkResourceCache::GetTotalByteLimit() { 426 SkAutoMutexAcquire am(gMutex); 427 return get_cache()->getTotalByteLimit(); 428 } 429 430 size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) { 431 SkAutoMutexAcquire am(gMutex); 432 return get_cache()->setTotalByteLimit(newLimit); 433 } 434 435 SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() { 436 SkAutoMutexAcquire am(gMutex); 437 return get_cache()->discardableFactory(); 438 } 439 440 SkBitmap::Allocator* SkResourceCache::GetAllocator() { 441 SkAutoMutexAcquire am(gMutex); 442 return get_cache()->allocator(); 443 } 444 445 void SkResourceCache::Dump() { 446 SkAutoMutexAcquire am(gMutex); 447 get_cache()->dump(); 448 } 449 450 size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) { 451 SkAutoMutexAcquire am(gMutex); 452 return get_cache()->setSingleAllocationByteLimit(size); 453 } 454 455 size_t SkResourceCache::GetSingleAllocationByteLimit() { 456 SkAutoMutexAcquire am(gMutex); 457 return get_cache()->getSingleAllocationByteLimit(); 458 } 459 460 void SkResourceCache::PurgeAll() { 461 SkAutoMutexAcquire am(gMutex); 462 return get_cache()->purgeAll(); 463 } 464 465 bool SkResourceCache::Find(const Key& key, VisitorProc visitor, void* context) { 466 SkAutoMutexAcquire am(gMutex); 467 return get_cache()->find(key, visitor, context); 468 } 469 470 void SkResourceCache::Add(Rec* rec) { 471 SkAutoMutexAcquire am(gMutex); 472 get_cache()->add(rec); 473 } 474 475 /////////////////////////////////////////////////////////////////////////////// 476 477 #include "SkGraphics.h" 478 479 size_t SkGraphics::GetResourceCacheTotalBytesUsed() { 480 return SkResourceCache::GetTotalBytesUsed(); 481 } 482 483 size_t SkGraphics::GetResourceCacheTotalByteLimit() { 484 return SkResourceCache::GetTotalByteLimit(); 485 } 486 487 size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) { 488 return SkResourceCache::SetTotalByteLimit(newLimit); 489 } 490 491 size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() { 492 return SkResourceCache::GetSingleAllocationByteLimit(); 493 } 494 495 size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) { 496 return SkResourceCache::SetSingleAllocationByteLimit(newLimit); 497 } 498 499 void SkGraphics::PurgeResourceCache() { 500 return SkResourceCache::PurgeAll(); 501 } 502 503