• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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