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