blob: 3e8929358c65bbdc2ccaee2da15fcce09fc6d238 [file] [log] [blame]
reed@google.com602a1d72013-07-23 19:13:54 +00001/*
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
mtklein67a32712014-07-10 06:03:46 -07008#include "SkChecksum.h"
reed@google.com602a1d72013-07-23 19:13:54 +00009#include "SkScaledImageCache.h"
reed@google.comd94697c2013-07-24 14:31:33 +000010#include "SkMipMap.h"
reed@google.com602a1d72013-07-23 19:13:54 +000011#include "SkPixelRef.h"
12#include "SkRect.h"
13
reed@google.come4eb1222013-12-09 22:29:30 +000014// This can be defined by the caller's build system
15//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
16
17#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT
18# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024
19#endif
20
reed@google.com602a1d72013-07-23 19:13:54 +000021#ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT
22 #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024)
23#endif
24
commit-bot@chromium.org75854792013-10-29 19:55:00 +000025static inline SkScaledImageCache::ID* rec_to_id(SkScaledImageCache::Rec* rec) {
26 return reinterpret_cast<SkScaledImageCache::ID*>(rec);
27}
28
29static inline SkScaledImageCache::Rec* id_to_rec(SkScaledImageCache::ID* id) {
30 return reinterpret_cast<SkScaledImageCache::Rec*>(id);
31}
reed@google.com5d1e5582013-07-25 14:36:15 +000032
reed4f987e92014-08-20 13:41:56 -070033void SkScaledImageCache::Key::init(size_t length) {
34 SkASSERT(SkAlign4(length) == length);
35 // 2 is fCount32 and fHash
36 fCount32 = SkToS32(2 + (length >> 2));
37 // skip both of our fields whe computing the murmur
38 fHash = SkChecksum::Murmur3(this->as32() + 2, (fCount32 - 2) << 2);
39}
reed@google.com602a1d72013-07-23 19:13:54 +000040
reed4f987e92014-08-20 13:41:56 -070041SkScaledImageCache::Key* SkScaledImageCache::Key::clone() const {
42 size_t size = fCount32 << 2;
43 void* copy = sk_malloc_throw(size);
44 memcpy(copy, this, size);
45 return (Key*)copy;
46}
reed@google.com602a1d72013-07-23 19:13:54 +000047
48struct SkScaledImageCache::Rec {
reed4f987e92014-08-20 13:41:56 -070049 Rec(const Key& key, const SkBitmap& bm) : fKey(key.clone()), fBitmap(bm) {
reed@google.com602a1d72013-07-23 19:13:54 +000050 fLockCount = 1;
reed@google.comd94697c2013-07-24 14:31:33 +000051 fMip = NULL;
reed@google.com602a1d72013-07-23 19:13:54 +000052 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +000053
reed4f987e92014-08-20 13:41:56 -070054 Rec(const Key& key, const SkMipMap* mip) : fKey(key.clone()) {
reed@google.comd94697c2013-07-24 14:31:33 +000055 fLockCount = 1;
56 fMip = mip;
57 mip->ref();
58 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +000059
reed@google.comd94697c2013-07-24 14:31:33 +000060 ~Rec() {
61 SkSafeUnref(fMip);
reed4f987e92014-08-20 13:41:56 -070062 sk_free(fKey);
reed@google.comd94697c2013-07-24 14:31:33 +000063 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +000064
reed4f987e92014-08-20 13:41:56 -070065 static const Key& GetKey(const Rec& rec) { return *rec.fKey; }
66 static uint32_t Hash(const Key& key) { return key.hash(); }
commit-bot@chromium.org55bd9402014-04-02 19:17:00 +000067
reed@google.com602a1d72013-07-23 19:13:54 +000068 size_t bytesUsed() const {
reed@google.comd94697c2013-07-24 14:31:33 +000069 return fMip ? fMip->getSize() : fBitmap.getSize();
reed@google.com602a1d72013-07-23 19:13:54 +000070 }
71
72 Rec* fNext;
73 Rec* fPrev;
74
75 // this guy wants to be 64bit aligned
reed4f987e92014-08-20 13:41:56 -070076 Key* fKey;
reed@google.com602a1d72013-07-23 19:13:54 +000077
78 int32_t fLockCount;
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +000079
reed@google.comd94697c2013-07-24 14:31:33 +000080 // we use either fBitmap or fMip, but not both
reed@google.com602a1d72013-07-23 19:13:54 +000081 SkBitmap fBitmap;
reed@google.comd94697c2013-07-24 14:31:33 +000082 const SkMipMap* fMip;
reed@google.com602a1d72013-07-23 19:13:54 +000083};
84
reed@google.com5d1e5582013-07-25 14:36:15 +000085#include "SkTDynamicHash.h"
86
commit-bot@chromium.org55bd9402014-04-02 19:17:00 +000087class SkScaledImageCache::Hash :
88 public SkTDynamicHash<SkScaledImageCache::Rec, SkScaledImageCache::Key> {};
commit-bot@chromium.org158f6462014-04-02 17:03:09 +000089
reed@google.com5d1e5582013-07-25 14:36:15 +000090
91///////////////////////////////////////////////////////////////////////////////
92
reed@google.comb8d17fe2013-07-25 17:42:24 +000093// experimental hash to speed things up
reed@google.comea348cb2013-07-25 17:50:37 +000094#define USE_HASH
reed@google.com5d1e5582013-07-25 14:36:15 +000095
commit-bot@chromium.org75854792013-10-29 19:55:00 +000096#if !defined(USE_HASH)
97static inline SkScaledImageCache::Rec* find_rec_in_list(
98 SkScaledImageCache::Rec* head, const Key & key) {
99 SkScaledImageCache::Rec* rec = head;
100 while ((rec != NULL) && (rec->fKey != key)) {
101 rec = rec->fNext;
102 }
103 return rec;
104}
105#endif
106
reed@google.come4eb1222013-12-09 22:29:30 +0000107void SkScaledImageCache::init() {
reed@google.com602a1d72013-07-23 19:13:54 +0000108 fHead = NULL;
109 fTail = NULL;
reed@google.com5d1e5582013-07-25 14:36:15 +0000110#ifdef USE_HASH
111 fHash = new Hash;
112#else
113 fHash = NULL;
114#endif
halcanary805ef152014-07-17 06:58:01 -0700115 fTotalBytesUsed = 0;
reed@google.com602a1d72013-07-23 19:13:54 +0000116 fCount = 0;
halcanary805ef152014-07-17 06:58:01 -0700117 fSingleAllocationByteLimit = 0;
reed@google.come4eb1222013-12-09 22:29:30 +0000118 fAllocator = NULL;
119
120 // One of these should be explicit set by the caller after we return.
halcanary805ef152014-07-17 06:58:01 -0700121 fTotalByteLimit = 0;
reed@google.come4eb1222013-12-09 22:29:30 +0000122 fDiscardableFactory = NULL;
123}
124
125#include "SkDiscardableMemory.h"
126
127class SkOneShotDiscardablePixelRef : public SkPixelRef {
128public:
commit-bot@chromium.org227c2462014-01-24 18:33:07 +0000129 SK_DECLARE_INST_COUNT(SkOneShotDiscardablePixelRef)
reed@google.come4eb1222013-12-09 22:29:30 +0000130 // Ownership of the discardablememory is transfered to the pixelref
131 SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes);
132 ~SkOneShotDiscardablePixelRef();
133
reed@google.come4eb1222013-12-09 22:29:30 +0000134protected:
reed@google.comd0419b12014-01-06 17:08:27 +0000135 virtual bool onNewLockPixels(LockRec*) SK_OVERRIDE;
reed@google.come4eb1222013-12-09 22:29:30 +0000136 virtual void onUnlockPixels() SK_OVERRIDE;
137 virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
138
139private:
reed@google.come4eb1222013-12-09 22:29:30 +0000140 SkDiscardableMemory* fDM;
141 size_t fRB;
142 bool fFirstTime;
143
144 typedef SkPixelRef INHERITED;
145};
146
147SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info,
148 SkDiscardableMemory* dm,
149 size_t rowBytes)
150 : INHERITED(info)
151 , fDM(dm)
152 , fRB(rowBytes)
153{
reed@google.come4eb1222013-12-09 22:29:30 +0000154 SkASSERT(dm->data());
155 fFirstTime = true;
156}
157
158SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() {
159 SkDELETE(fDM);
160}
161
reed@google.comd0419b12014-01-06 17:08:27 +0000162bool SkOneShotDiscardablePixelRef::onNewLockPixels(LockRec* rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000163 if (fFirstTime) {
164 // we're already locked
reed@google.comc83a91f2013-12-13 13:41:14 +0000165 SkASSERT(fDM->data());
reed@google.come4eb1222013-12-09 22:29:30 +0000166 fFirstTime = false;
reed@google.comd0419b12014-01-06 17:08:27 +0000167 goto SUCCESS;
reed@google.come4eb1222013-12-09 22:29:30 +0000168 }
commit-bot@chromium.org281713e2013-12-12 18:08:08 +0000169
reed@google.comfa7fd802013-12-12 21:37:25 +0000170 // A previous call to onUnlock may have deleted our DM, so check for that
171 if (NULL == fDM) {
reed@google.comd0419b12014-01-06 17:08:27 +0000172 return false;
reed@google.comfa7fd802013-12-12 21:37:25 +0000173 }
174
175 if (!fDM->lock()) {
176 // since it failed, we delete it now, to free-up the resource
177 delete fDM;
178 fDM = NULL;
reed@google.comd0419b12014-01-06 17:08:27 +0000179 return false;
reed@google.comfa7fd802013-12-12 21:37:25 +0000180 }
reed@google.comd0419b12014-01-06 17:08:27 +0000181
182SUCCESS:
183 rec->fPixels = fDM->data();
184 rec->fColorTable = NULL;
185 rec->fRowBytes = fRB;
186 return true;
reed@google.come4eb1222013-12-09 22:29:30 +0000187}
188
189void SkOneShotDiscardablePixelRef::onUnlockPixels() {
190 SkASSERT(!fFirstTime);
reed@google.comc83a91f2013-12-13 13:41:14 +0000191 fDM->unlock();
reed@google.come4eb1222013-12-09 22:29:30 +0000192}
193
194size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const {
reed@google.comd0419b12014-01-06 17:08:27 +0000195 return this->info().getSafeSize(fRB);
reed@google.come4eb1222013-12-09 22:29:30 +0000196}
197
198class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator {
199public:
200 SkScaledImageCacheDiscardableAllocator(
201 SkScaledImageCache::DiscardableFactory factory) {
202 SkASSERT(factory);
203 fFactory = factory;
204 }
205
206 virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
skia.committer@gmail.comcf0803b2013-12-10 07:02:03 +0000207
reed@google.come4eb1222013-12-09 22:29:30 +0000208private:
209 SkScaledImageCache::DiscardableFactory fFactory;
210};
211
212bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap,
213 SkColorTable* ctable) {
214 size_t size = bitmap->getSize();
sugoic197c8a2014-07-03 10:44:26 -0700215 uint64_t size64 = bitmap->computeSize64();
216 if (0 == size || size64 > (uint64_t)size) {
reed@google.come4eb1222013-12-09 22:29:30 +0000217 return false;
218 }
219
220 SkDiscardableMemory* dm = fFactory(size);
221 if (NULL == dm) {
222 return false;
223 }
224
reed@google.com900ecf22014-02-20 20:55:37 +0000225 // can we relax this?
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000226 if (kN32_SkColorType != bitmap->colorType()) {
reed@google.come4eb1222013-12-09 22:29:30 +0000227 return false;
228 }
229
reed@google.com900ecf22014-02-20 20:55:37 +0000230 SkImageInfo info = bitmap->info();
commit-bot@chromium.orge13af712014-01-13 20:39:14 +0000231 bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef,
reed@google.come4eb1222013-12-09 22:29:30 +0000232 (info, dm, bitmap->rowBytes())))->unref();
233 bitmap->lockPixels();
234 return bitmap->readyToDraw();
235}
236
237SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) {
238 this->init();
239 fDiscardableFactory = factory;
240
241 fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory));
242}
243
244SkScaledImageCache::SkScaledImageCache(size_t byteLimit) {
245 this->init();
halcanary805ef152014-07-17 06:58:01 -0700246 fTotalByteLimit = byteLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000247}
248
249SkScaledImageCache::~SkScaledImageCache() {
reed@google.come4eb1222013-12-09 22:29:30 +0000250 SkSafeUnref(fAllocator);
251
reed@google.com602a1d72013-07-23 19:13:54 +0000252 Rec* rec = fHead;
253 while (rec) {
254 Rec* next = rec->fNext;
255 SkDELETE(rec);
256 rec = next;
257 }
reed@google.com5d1e5582013-07-25 14:36:15 +0000258 delete fHash;
reed@google.com602a1d72013-07-23 19:13:54 +0000259}
260
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000261////////////////////////////////////////////////////////////////////////////////
262
reed4f987e92014-08-20 13:41:56 -0700263struct GenWHKey : public SkScaledImageCache::Key {
264public:
265 GenWHKey(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds)
266 : fGenID(genID)
267 , fScaleX(scaleX)
268 , fScaleY(scaleY)
269 , fBounds(bounds) {
270 this->init(7 * sizeof(uint32_t));
271 }
272
273 uint32_t fGenID;
274 SkScalar fScaleX;
275 SkScalar fScaleY;
276 SkIRect fBounds;
277};
rmistry@google.comd6bab022013-12-02 13:50:38 +0000278
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000279SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
reed@google.com602a1d72013-07-23 19:13:54 +0000280 SkScalar scaleX,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000281 SkScalar scaleY,
282 const SkIRect& bounds) {
reed4f987e92014-08-20 13:41:56 -0700283 return this->findAndLock(GenWHKey(genID, scaleX, scaleY, bounds));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000284}
285
286/**
287 This private method is the fully general record finder. All other
288 record finders should call this function or the one above. */
289SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
reed@google.com5d1e5582013-07-25 14:36:15 +0000290#ifdef USE_HASH
291 Rec* rec = fHash->find(key);
292#else
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000293 Rec* rec = find_rec_in_list(fHead, key);
reed@google.com5d1e5582013-07-25 14:36:15 +0000294#endif
reed@google.com5d1e5582013-07-25 14:36:15 +0000295 if (rec) {
296 this->moveToHead(rec); // for our LRU
297 rec->fLockCount += 1;
298 }
299 return rec;
reed@google.com602a1d72013-07-23 19:13:54 +0000300}
301
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000302/**
303 This function finds the bounds of the bitmap *within its pixelRef*.
304 If the bitmap lacks a pixelRef, it will return an empty rect, since
305 that doesn't make sense. This may be a useful enough function that
306 it should be somewhere else (in SkBitmap?). */
307static SkIRect get_bounds_from_bitmap(const SkBitmap& bm) {
308 if (!(bm.pixelRef())) {
309 return SkIRect::MakeEmpty();
310 }
reed@google.com672588b2014-01-08 15:42:01 +0000311 SkIPoint origin = bm.pixelRefOrigin();
312 return SkIRect::MakeXYWH(origin.fX, origin.fY, bm.width(), bm.height());
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000313}
314
315
316SkScaledImageCache::ID* SkScaledImageCache::findAndLock(uint32_t genID,
317 int32_t width,
318 int32_t height,
319 SkBitmap* bitmap) {
320 Rec* rec = this->findAndLock(genID, SK_Scalar1, SK_Scalar1,
321 SkIRect::MakeWH(width, height));
322 if (rec) {
323 SkASSERT(NULL == rec->fMip);
324 SkASSERT(rec->fBitmap.pixelRef());
325 *bitmap = rec->fBitmap;
326 }
327 return rec_to_id(rec);
328}
329
reed@google.comd94697c2013-07-24 14:31:33 +0000330SkScaledImageCache::ID* SkScaledImageCache::findAndLock(const SkBitmap& orig,
331 SkScalar scaleX,
332 SkScalar scaleY,
333 SkBitmap* scaled) {
334 if (0 == scaleX || 0 == scaleY) {
335 // degenerate, and the key we use for mipmaps
336 return NULL;
337 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000338 Rec* rec = this->findAndLock(orig.getGenerationID(), scaleX,
339 scaleY, get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000340 if (rec) {
341 SkASSERT(NULL == rec->fMip);
342 SkASSERT(rec->fBitmap.pixelRef());
343 *scaled = rec->fBitmap;
344 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000345 return rec_to_id(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000346}
347
348SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig,
349 SkMipMap const ** mip) {
skia.committer@gmail.comb77f0f42013-10-30 07:01:56 +0000350 Rec* rec = this->findAndLock(orig.getGenerationID(), 0, 0,
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000351 get_bounds_from_bitmap(orig));
reed@google.comd94697c2013-07-24 14:31:33 +0000352 if (rec) {
353 SkASSERT(rec->fMip);
354 SkASSERT(NULL == rec->fBitmap.pixelRef());
355 *mip = rec->fMip;
356 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000357 return rec_to_id(rec);
358}
359
360
361////////////////////////////////////////////////////////////////////////////////
362/**
363 This private method is the fully general record adder. All other
364 record adders should call this funtion. */
rmistry@google.comd6bab022013-12-02 13:50:38 +0000365SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000366 SkASSERT(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000367 // See if we already have this key (racy inserts, etc.)
reed4f987e92014-08-20 13:41:56 -0700368 Rec* existing = this->findAndLock(*rec->fKey);
robertphillips@google.com5db04df2013-12-18 18:48:08 +0000369 if (NULL != existing) {
370 // Since we already have a matching entry, just delete the new one and return.
371 // Call sites cannot assume the passed in object will live past this call.
commit-bot@chromium.org5e4112b2014-03-05 13:44:18 +0000372 existing->fBitmap = rec->fBitmap;
robertphillips@google.com5db04df2013-12-18 18:48:08 +0000373 SkDELETE(rec);
rmistry@google.comd6bab022013-12-02 13:50:38 +0000374 return rec_to_id(existing);
375 }
376
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000377 this->addToHead(rec);
378 SkASSERT(1 == rec->fLockCount);
379#ifdef USE_HASH
380 SkASSERT(fHash);
381 fHash->add(rec);
382#endif
383 // We may (now) be overbudget, so see if we need to purge something.
384 this->purgeAsNeeded();
rmistry@google.comd6bab022013-12-02 13:50:38 +0000385 return rec_to_id(rec);
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000386}
387
388SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
389 int32_t width,
390 int32_t height,
391 const SkBitmap& bitmap) {
reed4f987e92014-08-20 13:41:56 -0700392 GenWHKey key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000393 Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000394 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000395}
396
reed@google.com602a1d72013-07-23 19:13:54 +0000397SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
398 SkScalar scaleX,
399 SkScalar scaleY,
400 const SkBitmap& scaled) {
reed@google.comd94697c2013-07-24 14:31:33 +0000401 if (0 == scaleX || 0 == scaleY) {
402 // degenerate, and the key we use for mipmaps
403 return NULL;
404 }
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000405 SkIRect bounds = get_bounds_from_bitmap(orig);
406 if (bounds.isEmpty()) {
reed@google.com602a1d72013-07-23 19:13:54 +0000407 return NULL;
408 }
reed4f987e92014-08-20 13:41:56 -0700409 GenWHKey key(orig.getGenerationID(), scaleX, scaleY, bounds);
reed@google.com602a1d72013-07-23 19:13:54 +0000410 Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000411 return this->addAndLock(rec);
reed@google.comd94697c2013-07-24 14:31:33 +0000412}
reed@google.com602a1d72013-07-23 19:13:54 +0000413
reed@google.comd94697c2013-07-24 14:31:33 +0000414SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
415 const SkMipMap* mip) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000416 SkIRect bounds = get_bounds_from_bitmap(orig);
417 if (bounds.isEmpty()) {
reed@google.comd94697c2013-07-24 14:31:33 +0000418 return NULL;
419 }
reed4f987e92014-08-20 13:41:56 -0700420 GenWHKey key(orig.getGenerationID(), 0, 0, bounds);
reed@google.comd94697c2013-07-24 14:31:33 +0000421 Rec* rec = SkNEW_ARGS(Rec, (key, mip));
rmistry@google.comd6bab022013-12-02 13:50:38 +0000422 return this->addAndLock(rec);
reed@google.com602a1d72013-07-23 19:13:54 +0000423}
424
425void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
426 SkASSERT(id);
427
428#ifdef SK_DEBUG
429 {
430 bool found = false;
431 Rec* rec = fHead;
432 while (rec != NULL) {
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000433 if (rec == id_to_rec(id)) {
reed@google.com602a1d72013-07-23 19:13:54 +0000434 found = true;
435 break;
436 }
437 rec = rec->fNext;
438 }
439 SkASSERT(found);
440 }
441#endif
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000442 Rec* rec = id_to_rec(id);
reed@google.com602a1d72013-07-23 19:13:54 +0000443 SkASSERT(rec->fLockCount > 0);
444 rec->fLockCount -= 1;
445
reed@google.com602a1d72013-07-23 19:13:54 +0000446 // we may have been over-budget, but now have released something, so check
447 // if we should purge.
448 if (0 == rec->fLockCount) {
449 this->purgeAsNeeded();
450 }
451}
452
453void SkScaledImageCache::purgeAsNeeded() {
reed@google.come4eb1222013-12-09 22:29:30 +0000454 size_t byteLimit;
455 int countLimit;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000456
reed@google.come4eb1222013-12-09 22:29:30 +0000457 if (fDiscardableFactory) {
458 countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT;
459 byteLimit = SK_MaxU32; // no limit based on bytes
460 } else {
461 countLimit = SK_MaxS32; // no limit based on count
halcanary805ef152014-07-17 06:58:01 -0700462 byteLimit = fTotalByteLimit;
reed@google.come4eb1222013-12-09 22:29:30 +0000463 }
464
halcanary805ef152014-07-17 06:58:01 -0700465 size_t bytesUsed = fTotalBytesUsed;
reed@google.come4eb1222013-12-09 22:29:30 +0000466 int countUsed = fCount;
skia.committer@gmail.comcf0803b2013-12-10 07:02:03 +0000467
reed@google.com602a1d72013-07-23 19:13:54 +0000468 Rec* rec = fTail;
469 while (rec) {
reed@google.come4eb1222013-12-09 22:29:30 +0000470 if (bytesUsed < byteLimit && countUsed < countLimit) {
reed@google.com602a1d72013-07-23 19:13:54 +0000471 break;
472 }
reed@google.come4eb1222013-12-09 22:29:30 +0000473
reed@google.com602a1d72013-07-23 19:13:54 +0000474 Rec* prev = rec->fPrev;
475 if (0 == rec->fLockCount) {
reed@google.com602a1d72013-07-23 19:13:54 +0000476 size_t used = rec->bytesUsed();
477 SkASSERT(used <= bytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000478 this->detach(rec);
reed@google.com5d1e5582013-07-25 14:36:15 +0000479#ifdef USE_HASH
reed4f987e92014-08-20 13:41:56 -0700480 fHash->remove(*rec->fKey);
reed@google.com5d1e5582013-07-25 14:36:15 +0000481#endif
skia.committer@gmail.com956b3102013-07-26 07:00:58 +0000482
reed@google.com602a1d72013-07-23 19:13:54 +0000483 SkDELETE(rec);
reed@google.come4eb1222013-12-09 22:29:30 +0000484
485 bytesUsed -= used;
486 countUsed -= 1;
reed@google.com602a1d72013-07-23 19:13:54 +0000487 }
488 rec = prev;
489 }
reed@google.come4eb1222013-12-09 22:29:30 +0000490
halcanary805ef152014-07-17 06:58:01 -0700491 fTotalBytesUsed = bytesUsed;
reed@google.come4eb1222013-12-09 22:29:30 +0000492 fCount = countUsed;
reed@google.com602a1d72013-07-23 19:13:54 +0000493}
494
halcanary805ef152014-07-17 06:58:01 -0700495size_t SkScaledImageCache::setTotalByteLimit(size_t newLimit) {
496 size_t prevLimit = fTotalByteLimit;
497 fTotalByteLimit = newLimit;
reed@google.com602a1d72013-07-23 19:13:54 +0000498 if (newLimit < prevLimit) {
499 this->purgeAsNeeded();
500 }
501 return prevLimit;
502}
503
504///////////////////////////////////////////////////////////////////////////////
505
506void SkScaledImageCache::detach(Rec* rec) {
507 Rec* prev = rec->fPrev;
508 Rec* next = rec->fNext;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000509
reed@google.com602a1d72013-07-23 19:13:54 +0000510 if (!prev) {
511 SkASSERT(fHead == rec);
512 fHead = next;
513 } else {
514 prev->fNext = next;
515 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000516
reed@google.com602a1d72013-07-23 19:13:54 +0000517 if (!next) {
518 fTail = prev;
519 } else {
520 next->fPrev = prev;
521 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000522
reed@google.com602a1d72013-07-23 19:13:54 +0000523 rec->fNext = rec->fPrev = NULL;
524}
525
526void SkScaledImageCache::moveToHead(Rec* rec) {
527 if (fHead == rec) {
528 return;
529 }
530
531 SkASSERT(fHead);
532 SkASSERT(fTail);
533
534 this->validate();
535
536 this->detach(rec);
537
538 fHead->fPrev = rec;
539 rec->fNext = fHead;
540 fHead = rec;
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000541
reed@google.com602a1d72013-07-23 19:13:54 +0000542 this->validate();
543}
544
545void SkScaledImageCache::addToHead(Rec* rec) {
546 this->validate();
547
548 rec->fPrev = NULL;
549 rec->fNext = fHead;
550 if (fHead) {
551 fHead->fPrev = rec;
552 }
553 fHead = rec;
554 if (!fTail) {
555 fTail = rec;
556 }
halcanary805ef152014-07-17 06:58:01 -0700557 fTotalBytesUsed += rec->bytesUsed();
reed@google.com602a1d72013-07-23 19:13:54 +0000558 fCount += 1;
559
560 this->validate();
561}
562
reed@google.comfa7fd802013-12-12 21:37:25 +0000563///////////////////////////////////////////////////////////////////////////////
564
reed@google.com602a1d72013-07-23 19:13:54 +0000565#ifdef SK_DEBUG
566void SkScaledImageCache::validate() const {
567 if (NULL == fHead) {
568 SkASSERT(NULL == fTail);
halcanary805ef152014-07-17 06:58:01 -0700569 SkASSERT(0 == fTotalBytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000570 return;
571 }
572
573 if (fHead == fTail) {
574 SkASSERT(NULL == fHead->fPrev);
575 SkASSERT(NULL == fHead->fNext);
halcanary805ef152014-07-17 06:58:01 -0700576 SkASSERT(fHead->bytesUsed() == fTotalBytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000577 return;
578 }
579
580 SkASSERT(NULL == fHead->fPrev);
581 SkASSERT(NULL != fHead->fNext);
582 SkASSERT(NULL == fTail->fNext);
583 SkASSERT(NULL != fTail->fPrev);
584
585 size_t used = 0;
586 int count = 0;
587 const Rec* rec = fHead;
588 while (rec) {
589 count += 1;
590 used += rec->bytesUsed();
halcanary805ef152014-07-17 06:58:01 -0700591 SkASSERT(used <= fTotalBytesUsed);
reed@google.com602a1d72013-07-23 19:13:54 +0000592 rec = rec->fNext;
593 }
594 SkASSERT(fCount == count);
595
596 rec = fTail;
597 while (rec) {
598 SkASSERT(count > 0);
599 count -= 1;
600 SkASSERT(used >= rec->bytesUsed());
601 used -= rec->bytesUsed();
602 rec = rec->fPrev;
603 }
skia.committer@gmail.com7f1af502013-07-24 07:01:12 +0000604
reed@google.com602a1d72013-07-23 19:13:54 +0000605 SkASSERT(0 == count);
606 SkASSERT(0 == used);
607}
608#endif
609
reed@google.comfa7fd802013-12-12 21:37:25 +0000610void SkScaledImageCache::dump() const {
611 this->validate();
612
613 const Rec* rec = fHead;
614 int locked = 0;
615 while (rec) {
616 locked += rec->fLockCount > 0;
617 rec = rec->fNext;
618 }
619
620 SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n",
halcanary805ef152014-07-17 06:58:01 -0700621 fCount, fTotalBytesUsed, locked,
reed@google.comfa7fd802013-12-12 21:37:25 +0000622 fDiscardableFactory ? "discardable" : "malloc");
623}
624
halcanary805ef152014-07-17 06:58:01 -0700625size_t SkScaledImageCache::setSingleAllocationByteLimit(size_t newLimit) {
626 size_t oldLimit = fSingleAllocationByteLimit;
627 fSingleAllocationByteLimit = newLimit;
628 return oldLimit;
629}
630
631size_t SkScaledImageCache::getSingleAllocationByteLimit() const {
632 return fSingleAllocationByteLimit;
633}
634
reed@google.com602a1d72013-07-23 19:13:54 +0000635///////////////////////////////////////////////////////////////////////////////
636
637#include "SkThread.h"
638
reed@google.combe19dbe2013-07-24 15:06:34 +0000639SK_DECLARE_STATIC_MUTEX(gMutex);
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000640static SkScaledImageCache* gScaledImageCache = NULL;
mtklein77ec7a62014-06-10 11:13:47 -0700641static void cleanup_gScaledImageCache() {
642 // We'll clean this up in our own tests, but disable for clients.
643 // Chrome seems to have funky multi-process things going on in unit tests that
644 // makes this unsafe to delete when the main process atexit()s.
645 // SkLazyPtr does the same sort of thing.
646#if SK_DEVELOPER
647 SkDELETE(gScaledImageCache);
648#endif
649}
reed@google.com602a1d72013-07-23 19:13:54 +0000650
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000651/** Must hold gMutex when calling. */
reed@google.com602a1d72013-07-23 19:13:54 +0000652static SkScaledImageCache* get_cache() {
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000653 // gMutex is always held when this is called, so we don't need to be fancy in here.
mtkleinb83f6c32014-06-09 14:18:02 -0700654 gMutex.assertHeld();
commit-bot@chromium.org60c8d242014-05-27 16:28:43 +0000655 if (NULL == gScaledImageCache) {
656#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
657 gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create));
658#else
659 gScaledImageCache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT));
660#endif
661 atexit(cleanup_gScaledImageCache);
662 }
commit-bot@chromium.org709ca752014-01-24 22:38:39 +0000663 return gScaledImageCache;
reed@google.com602a1d72013-07-23 19:13:54 +0000664}
665
commit-bot@chromium.org75854792013-10-29 19:55:00 +0000666
667SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(
668 uint32_t pixelGenerationID,
669 int32_t width,
670 int32_t height,
671 SkBitmap* scaled) {
672 SkAutoMutexAcquire am(gMutex);
673 return get_cache()->findAndLock(pixelGenerationID, width, height, scaled);
674}
675
676SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(
677 uint32_t pixelGenerationID,
678 int32_t width,
679 int32_t height,
680 const SkBitmap& scaled) {
681 SkAutoMutexAcquire am(gMutex);
682 return get_cache()->addAndLock(pixelGenerationID, width, height, scaled);
683}
684
685
reed@google.com602a1d72013-07-23 19:13:54 +0000686SkScaledImageCache::ID* SkScaledImageCache::FindAndLock(const SkBitmap& orig,
687 SkScalar scaleX,
688 SkScalar scaleY,
689 SkBitmap* scaled) {
690 SkAutoMutexAcquire am(gMutex);
691 return get_cache()->findAndLock(orig, scaleX, scaleY, scaled);
692}
693
reed@google.comd94697c2013-07-24 14:31:33 +0000694SkScaledImageCache::ID* SkScaledImageCache::FindAndLockMip(const SkBitmap& orig,
695 SkMipMap const ** mip) {
696 SkAutoMutexAcquire am(gMutex);
697 return get_cache()->findAndLockMip(orig, mip);
698}
699
reed@google.com602a1d72013-07-23 19:13:54 +0000700SkScaledImageCache::ID* SkScaledImageCache::AddAndLock(const SkBitmap& orig,
701 SkScalar scaleX,
702 SkScalar scaleY,
703 const SkBitmap& scaled) {
704 SkAutoMutexAcquire am(gMutex);
705 return get_cache()->addAndLock(orig, scaleX, scaleY, scaled);
706}
707
reed@google.comd94697c2013-07-24 14:31:33 +0000708SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig,
709 const SkMipMap* mip) {
710 SkAutoMutexAcquire am(gMutex);
711 return get_cache()->addAndLockMip(orig, mip);
712}
713
reed@google.com602a1d72013-07-23 19:13:54 +0000714void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) {
715 SkAutoMutexAcquire am(gMutex);
reed@google.comfa7fd802013-12-12 21:37:25 +0000716 get_cache()->unlock(id);
717
718// get_cache()->dump();
reed@google.com602a1d72013-07-23 19:13:54 +0000719}
720
halcanary805ef152014-07-17 06:58:01 -0700721size_t SkScaledImageCache::GetTotalBytesUsed() {
reed@google.com602a1d72013-07-23 19:13:54 +0000722 SkAutoMutexAcquire am(gMutex);
halcanary805ef152014-07-17 06:58:01 -0700723 return get_cache()->getTotalBytesUsed();
reed@google.com602a1d72013-07-23 19:13:54 +0000724}
725
halcanary805ef152014-07-17 06:58:01 -0700726size_t SkScaledImageCache::GetTotalByteLimit() {
reed@google.com602a1d72013-07-23 19:13:54 +0000727 SkAutoMutexAcquire am(gMutex);
halcanary805ef152014-07-17 06:58:01 -0700728 return get_cache()->getTotalByteLimit();
reed@google.com602a1d72013-07-23 19:13:54 +0000729}
730
halcanary805ef152014-07-17 06:58:01 -0700731size_t SkScaledImageCache::SetTotalByteLimit(size_t newLimit) {
reed@google.com602a1d72013-07-23 19:13:54 +0000732 SkAutoMutexAcquire am(gMutex);
halcanary805ef152014-07-17 06:58:01 -0700733 return get_cache()->setTotalByteLimit(newLimit);
reed@google.com602a1d72013-07-23 19:13:54 +0000734}
735
reed@google.come4eb1222013-12-09 22:29:30 +0000736SkBitmap::Allocator* SkScaledImageCache::GetAllocator() {
737 SkAutoMutexAcquire am(gMutex);
738 return get_cache()->allocator();
739}
740
reed@google.comfa7fd802013-12-12 21:37:25 +0000741void SkScaledImageCache::Dump() {
742 SkAutoMutexAcquire am(gMutex);
743 get_cache()->dump();
744}
745
halcanary805ef152014-07-17 06:58:01 -0700746size_t SkScaledImageCache::SetSingleAllocationByteLimit(size_t size) {
747 SkAutoMutexAcquire am(gMutex);
748 return get_cache()->setSingleAllocationByteLimit(size);
749}
750
751size_t SkScaledImageCache::GetSingleAllocationByteLimit() {
752 SkAutoMutexAcquire am(gMutex);
753 return get_cache()->getSingleAllocationByteLimit();
754}
755
reed@google.com602a1d72013-07-23 19:13:54 +0000756///////////////////////////////////////////////////////////////////////////////
757
758#include "SkGraphics.h"
759
halcanary805ef152014-07-17 06:58:01 -0700760size_t SkGraphics::GetImageCacheTotalBytesUsed() {
761 return SkScaledImageCache::GetTotalBytesUsed();
reed@google.com602a1d72013-07-23 19:13:54 +0000762}
763
halcanary805ef152014-07-17 06:58:01 -0700764size_t SkGraphics::GetImageCacheTotalByteLimit() {
765 return SkScaledImageCache::GetTotalByteLimit();
reed@google.com602a1d72013-07-23 19:13:54 +0000766}
767
halcanary805ef152014-07-17 06:58:01 -0700768size_t SkGraphics::SetImageCacheTotalByteLimit(size_t newLimit) {
769 return SkScaledImageCache::SetTotalByteLimit(newLimit);
reed@google.com602a1d72013-07-23 19:13:54 +0000770}
halcanary805ef152014-07-17 06:58:01 -0700771
772size_t SkGraphics::GetImageCacheSingleAllocationByteLimit() {
773 return SkScaledImageCache::GetSingleAllocationByteLimit();
774}
775
776size_t SkGraphics::SetImageCacheSingleAllocationByteLimit(size_t newLimit) {
777 return SkScaledImageCache::SetSingleAllocationByteLimit(newLimit);
778}
779