blob: 6beb8a479bf59497b9ead4802aca899252ea58d6 [file] [log] [blame]
scroggo@google.comf8d7d272013-02-22 21:38:35 +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
8#include "SkLruImageCache.h"
9
10static intptr_t NextGenerationID() {
11 static intptr_t gNextID;
12 do {
13 gNextID++;
14 } while (SkImageCache::UNINITIALIZED_ID == gNextID);
15 return gNextID;
16}
17
18class CachedPixels : public SkNoncopyable {
19
20public:
21 CachedPixels(size_t length)
22 : fLength(length)
23 , fID(NextGenerationID())
24 , fLocked(false) {
25 fAddr = sk_malloc_throw(length);
26 }
27
28 ~CachedPixels() {
29 sk_free(fAddr);
30 }
31
32 void* getData() { return fAddr; }
33
34 intptr_t getID() const { return fID; }
35
36 size_t getLength() const { return fLength; }
37
38 void lock() { SkASSERT(!fLocked); fLocked = true; }
39
40 void unlock() { SkASSERT(fLocked); fLocked = false; }
41
42 bool isLocked() const { return fLocked; }
43
44private:
45 void* fAddr;
46 size_t fLength;
47 const intptr_t fID;
48 bool fLocked;
49 SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels);
50};
51
52////////////////////////////////////////////////////////////////////////////////////
53
54SkLruImageCache::SkLruImageCache(size_t budget)
55 : fRamBudget(budget)
56 , fRamUsed(0) {}
57
58SkLruImageCache::~SkLruImageCache() {
59 // Don't worry about updating pointers. All will be deleted.
60 Iter iter;
61 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
62 while (pixels != NULL) {
63 CachedPixels* prev = iter.prev();
64 SkASSERT(!pixels->isLocked());
robertphillips@google.comffb91da2013-02-25 01:38:01 +000065#ifdef SK_DEBUG
scroggo@google.comf8d7d272013-02-22 21:38:35 +000066 fRamUsed -= pixels->getLength();
67#endif
68 SkDELETE(pixels);
69 pixels = prev;
70 }
robertphillips@google.comffb91da2013-02-25 01:38:01 +000071#ifdef SK_DEBUG
scroggo@google.comf8d7d272013-02-22 21:38:35 +000072 SkASSERT(fRamUsed == 0);
73#endif
74}
75
76#ifdef SK_DEBUG
77SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
78 SkAutoMutexAcquire ac(&fMutex);
79 CachedPixels* pixels = this->findByID(ID);
80 if (NULL == pixels) {
81 return SkImageCache::kThrownAway_CacheStatus;
82 }
83 if (pixels->isLocked()) {
84 return SkImageCache::kPinned_CacheStatus;
85 }
86 return SkImageCache::kUnpinned_CacheStatus;
87}
88#endif
89
scroggo@google.coma560d00b2013-03-04 21:32:32 +000090size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) {
91 size_t oldLimit = fRamBudget;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000092 SkAutoMutexAcquire ac(&fMutex);
scroggo@google.coma560d00b2013-03-04 21:32:32 +000093 fRamBudget = newLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000094 this->purgeIfNeeded();
scroggo@google.coma560d00b2013-03-04 21:32:32 +000095 return oldLimit;
scroggo@google.comf8d7d272013-02-22 21:38:35 +000096}
97
98void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
99 SkAutoMutexAcquire ac(&fMutex);
100 CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
101 if (ID != NULL) {
102 *ID = pixels->getID();
103 }
104 pixels->lock();
105 fRamUsed += bytes;
106 fLRU.addToHead(pixels);
107 this->purgeIfNeeded();
108 return pixels->getData();
109}
110
111void* SkLruImageCache::pinCache(intptr_t ID) {
112 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
113 SkAutoMutexAcquire ac(&fMutex);
114 CachedPixels* pixels = this->findByID(ID);
115 if (NULL == pixels) {
116 return NULL;
117 }
118 if (pixels != fLRU.head()) {
119 fLRU.remove(pixels);
120 fLRU.addToHead(pixels);
121 }
122 pixels->lock();
123 return pixels->getData();
124}
125
126void SkLruImageCache::releaseCache(intptr_t ID) {
127 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
128 SkAutoMutexAcquire ac(&fMutex);
129 CachedPixels* pixels = this->findByID(ID);
130 SkASSERT(pixels != NULL);
131 pixels->unlock();
132 this->purgeIfNeeded();
133}
134
135void SkLruImageCache::throwAwayCache(intptr_t ID) {
136 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
137 SkAutoMutexAcquire ac(&fMutex);
138 CachedPixels* pixels = this->findByID(ID);
139 if (pixels != NULL) {
140 if (pixels->isLocked()) {
141 pixels->unlock();
142 }
143 this->removePixels(pixels);
144 }
145}
146
147void SkLruImageCache::removePixels(CachedPixels* pixels) {
148 // Mutex is already locked.
149 SkASSERT(!pixels->isLocked());
150 const size_t size = pixels->getLength();
151 SkASSERT(size <= fRamUsed);
152 fLRU.remove(pixels);
153 SkDELETE(pixels);
154 fRamUsed -= size;
155}
156
157CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
158 // Mutex is already locked.
159 Iter iter;
160 // Start from the head, most recently used.
161 CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
162 while (pixels != NULL) {
163 if (pixels->getID() == ID) {
164 return pixels;
165 }
166 pixels = iter.next();
167 }
168 return NULL;
169}
170
171void SkLruImageCache::purgeIfNeeded() {
172 // Mutex is already locked.
scroggo@google.coma560d00b2013-03-04 21:32:32 +0000173 if (fRamBudget > 0) {
174 this->purgeTilAtOrBelow(fRamBudget);
175 }
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000176}
177
178void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
179 // Mutex is already locked.
180 if (fRamUsed > limit) {
181 Iter iter;
182 // Start from the tail, least recently used.
183 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
184 while (pixels != NULL && fRamUsed > limit) {
185 CachedPixels* prev = iter.prev();
186 if (!pixels->isLocked()) {
187 this->removePixels(pixels);
188 }
189 pixels = prev;
190 }
191 }
192}