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
init(size_t length)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
init()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
SkOneShotDiscardablePixelRef(const SkImageInfo & info,SkDiscardableMemory * dm,size_t rowBytes)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
~SkOneShotDiscardablePixelRef()87 SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
88 SkDELETE(fDM);
89 }
90
onNewLockPixels(LockRec * rec)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
onUnlockPixels()118 void SkOneShotDiscardablePixelRef::onUnlockPixels() {
119 SkASSERT(!fFirstTime);
120 fDM->unlock();
121 }
122
getAllocatedSizeInBytes() const123 size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
124 return this->info().getSafeSize(fRB);
125 }
126
127 class SkResourceCacheDiscardableAllocator : public SkBitmap::Allocator {
128 public:
SkResourceCacheDiscardableAllocator(SkResourceCache::DiscardableFactory factory)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
allocPixelRef(SkBitmap * bitmap,SkColorTable * ctable)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
SkResourceCache(DiscardableFactory factory)164 SkResourceCache::SkResourceCache(DiscardableFactory factory) {
165 this->init();
166 fDiscardableFactory = factory;
167
168 fAllocator = SkNEW_ARGS(SkResourceCacheDiscardableAllocator, (factory));
169 }
170
SkResourceCache(size_t byteLimit)171 SkResourceCache::SkResourceCache(size_t byteLimit) {
172 this->init();
173 fTotalByteLimit = byteLimit;
174 }
175
~SkResourceCache()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
find(const Key & key,VisitorProc visitor,void * context)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
add(Rec * rec)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
remove(Rec * rec)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
purgeAsNeeded(bool forcePurge)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
setTotalByteLimit(size_t newLimit)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
detach(Rec * rec)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
moveToHead(Rec * rec)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
addToHead(Rec * rec)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
validate() const328 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
dump() const372 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
setSingleAllocationByteLimit(size_t newLimit)379 size_t SkResourceCache::setSingleAllocationByteLimit(size_t newLimit) {
380 size_t oldLimit = fSingleAllocationByteLimit;
381 fSingleAllocationByteLimit = newLimit;
382 return oldLimit;
383 }
384
getSingleAllocationByteLimit() const385 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;
cleanup_gResourceCache()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. */
get_cache()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
GetTotalBytesUsed()420 size_t SkResourceCache::GetTotalBytesUsed() {
421 SkAutoMutexAcquire am(gMutex);
422 return get_cache()->getTotalBytesUsed();
423 }
424
GetTotalByteLimit()425 size_t SkResourceCache::GetTotalByteLimit() {
426 SkAutoMutexAcquire am(gMutex);
427 return get_cache()->getTotalByteLimit();
428 }
429
SetTotalByteLimit(size_t newLimit)430 size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) {
431 SkAutoMutexAcquire am(gMutex);
432 return get_cache()->setTotalByteLimit(newLimit);
433 }
434
GetDiscardableFactory()435 SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() {
436 SkAutoMutexAcquire am(gMutex);
437 return get_cache()->discardableFactory();
438 }
439
GetAllocator()440 SkBitmap::Allocator* SkResourceCache::GetAllocator() {
441 SkAutoMutexAcquire am(gMutex);
442 return get_cache()->allocator();
443 }
444
Dump()445 void SkResourceCache::Dump() {
446 SkAutoMutexAcquire am(gMutex);
447 get_cache()->dump();
448 }
449
SetSingleAllocationByteLimit(size_t size)450 size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) {
451 SkAutoMutexAcquire am(gMutex);
452 return get_cache()->setSingleAllocationByteLimit(size);
453 }
454
GetSingleAllocationByteLimit()455 size_t SkResourceCache::GetSingleAllocationByteLimit() {
456 SkAutoMutexAcquire am(gMutex);
457 return get_cache()->getSingleAllocationByteLimit();
458 }
459
PurgeAll()460 void SkResourceCache::PurgeAll() {
461 SkAutoMutexAcquire am(gMutex);
462 return get_cache()->purgeAll();
463 }
464
Find(const Key & key,VisitorProc visitor,void * context)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
Add(Rec * rec)470 void SkResourceCache::Add(Rec* rec) {
471 SkAutoMutexAcquire am(gMutex);
472 get_cache()->add(rec);
473 }
474
475 ///////////////////////////////////////////////////////////////////////////////
476
477 #include "SkGraphics.h"
478
GetResourceCacheTotalBytesUsed()479 size_t SkGraphics::GetResourceCacheTotalBytesUsed() {
480 return SkResourceCache::GetTotalBytesUsed();
481 }
482
GetResourceCacheTotalByteLimit()483 size_t SkGraphics::GetResourceCacheTotalByteLimit() {
484 return SkResourceCache::GetTotalByteLimit();
485 }
486
SetResourceCacheTotalByteLimit(size_t newLimit)487 size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) {
488 return SkResourceCache::SetTotalByteLimit(newLimit);
489 }
490
GetResourceCacheSingleAllocationByteLimit()491 size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() {
492 return SkResourceCache::GetSingleAllocationByteLimit();
493 }
494
SetResourceCacheSingleAllocationByteLimit(size_t newLimit)495 size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) {
496 return SkResourceCache::SetSingleAllocationByteLimit(newLimit);
497 }
498
PurgeResourceCache()499 void SkGraphics::PurgeResourceCache() {
500 return SkResourceCache::PurgeAll();
501 }
502
503