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 "SkLruImageCache.h" | |
9 | |
10 SK_DEFINE_INST_COUNT(SkImageCache) | |
11 SK_DEFINE_INST_COUNT(SkLruImageCache) | |
12 | |
13 static intptr_t NextGenerationID() { | |
14 static intptr_t gNextID; | |
15 do { | |
16 gNextID++; | |
17 } while (SkImageCache::UNINITIALIZED_ID == gNextID); | |
18 return gNextID; | |
19 } | |
20 | |
21 class CachedPixels : public SkNoncopyable { | |
22 | |
23 public: | |
24 CachedPixels(size_t length) | |
25 : fLength(length) | |
26 , fID(NextGenerationID()) | |
27 , fLocked(false) { | |
28 fAddr = sk_malloc_throw(length); | |
29 } | |
30 | |
31 ~CachedPixels() { | |
32 sk_free(fAddr); | |
33 } | |
34 | |
35 void* getData() { return fAddr; } | |
36 | |
37 intptr_t getID() const { return fID; } | |
38 | |
39 size_t getLength() const { return fLength; } | |
40 | |
41 void lock() { SkASSERT(!fLocked); fLocked = true; } | |
42 | |
43 void unlock() { SkASSERT(fLocked); fLocked = false; } | |
44 | |
45 bool isLocked() const { return fLocked; } | |
46 | |
47 private: | |
48 void* fAddr; | |
49 size_t fLength; | |
50 const intptr_t fID; | |
51 bool fLocked; | |
52 SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels); | |
53 }; | |
54 | |
55 ////////////////////////////////////////////////////////////////////////////////
//// | |
56 | |
57 SkLruImageCache::SkLruImageCache(size_t budget) | |
58 : fRamBudget(budget) | |
59 , fRamUsed(0) {} | |
60 | |
61 SkLruImageCache::~SkLruImageCache() { | |
62 // Don't worry about updating pointers. All will be deleted. | |
63 Iter iter; | |
64 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart); | |
65 while (pixels != NULL) { | |
66 CachedPixels* prev = iter.prev(); | |
67 SkASSERT(!pixels->isLocked()); | |
68 #ifdef SK_DEBUG | |
69 fRamUsed -= pixels->getLength(); | |
70 #endif | |
71 SkDELETE(pixels); | |
72 pixels = prev; | |
73 } | |
74 #ifdef SK_DEBUG | |
75 SkASSERT(fRamUsed == 0); | |
76 #endif | |
77 } | |
78 | |
79 #ifdef SK_DEBUG | |
80 SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const { | |
81 if (SkImageCache::UNINITIALIZED_ID == ID) { | |
82 return SkImageCache::kFreed_MemoryStatus; | |
83 } | |
84 SkAutoMutexAcquire ac(&fMutex); | |
85 CachedPixels* pixels = this->findByID(ID); | |
86 if (NULL == pixels) { | |
87 return SkImageCache::kFreed_MemoryStatus; | |
88 } | |
89 if (pixels->isLocked()) { | |
90 return SkImageCache::kPinned_MemoryStatus; | |
91 } | |
92 return SkImageCache::kUnpinned_MemoryStatus; | |
93 } | |
94 | |
95 void SkLruImageCache::purgeAllUnpinnedCaches() { | |
96 SkAutoMutexAcquire ac(&fMutex); | |
97 this->purgeTilAtOrBelow(0); | |
98 } | |
99 #endif | |
100 | |
101 size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) { | |
102 size_t oldLimit = fRamBudget; | |
103 SkAutoMutexAcquire ac(&fMutex); | |
104 fRamBudget = newLimit; | |
105 this->purgeIfNeeded(); | |
106 return oldLimit; | |
107 } | |
108 | |
109 void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) { | |
110 SkAutoMutexAcquire ac(&fMutex); | |
111 CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes)); | |
112 if (ID != NULL) { | |
113 *ID = pixels->getID(); | |
114 } | |
115 pixels->lock(); | |
116 fRamUsed += bytes; | |
117 fLRU.addToHead(pixels); | |
118 this->purgeIfNeeded(); | |
119 return pixels->getData(); | |
120 } | |
121 | |
122 void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) { | |
123 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); | |
124 SkAutoMutexAcquire ac(&fMutex); | |
125 CachedPixels* pixels = this->findByID(ID); | |
126 if (NULL == pixels) { | |
127 return NULL; | |
128 } | |
129 if (pixels != fLRU.head()) { | |
130 fLRU.remove(pixels); | |
131 fLRU.addToHead(pixels); | |
132 } | |
133 SkASSERT(status != NULL); | |
134 // This cache will never return pinned memory whose data has been overwritte
n. | |
135 *status = SkImageCache::kRetained_DataStatus; | |
136 pixels->lock(); | |
137 return pixels->getData(); | |
138 } | |
139 | |
140 void SkLruImageCache::releaseCache(intptr_t ID) { | |
141 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); | |
142 SkAutoMutexAcquire ac(&fMutex); | |
143 CachedPixels* pixels = this->findByID(ID); | |
144 SkASSERT(pixels != NULL); | |
145 pixels->unlock(); | |
146 this->purgeIfNeeded(); | |
147 } | |
148 | |
149 void SkLruImageCache::throwAwayCache(intptr_t ID) { | |
150 SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); | |
151 SkAutoMutexAcquire ac(&fMutex); | |
152 CachedPixels* pixels = this->findByID(ID); | |
153 if (pixels != NULL) { | |
154 if (pixels->isLocked()) { | |
155 pixels->unlock(); | |
156 } | |
157 this->removePixels(pixels); | |
158 } | |
159 } | |
160 | |
161 void SkLruImageCache::removePixels(CachedPixels* pixels) { | |
162 // Mutex is already locked. | |
163 SkASSERT(!pixels->isLocked()); | |
164 const size_t size = pixels->getLength(); | |
165 SkASSERT(size <= fRamUsed); | |
166 fLRU.remove(pixels); | |
167 SkDELETE(pixels); | |
168 fRamUsed -= size; | |
169 } | |
170 | |
171 CachedPixels* SkLruImageCache::findByID(intptr_t ID) const { | |
172 // Mutex is already locked. | |
173 Iter iter; | |
174 // Start from the head, most recently used. | |
175 CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart); | |
176 while (pixels != NULL) { | |
177 if (pixels->getID() == ID) { | |
178 return pixels; | |
179 } | |
180 pixels = iter.next(); | |
181 } | |
182 return NULL; | |
183 } | |
184 | |
185 void SkLruImageCache::purgeIfNeeded() { | |
186 // Mutex is already locked. | |
187 if (fRamBudget > 0) { | |
188 this->purgeTilAtOrBelow(fRamBudget); | |
189 } | |
190 } | |
191 | |
192 void SkLruImageCache::purgeTilAtOrBelow(size_t limit) { | |
193 // Mutex is already locked. | |
194 if (fRamUsed > limit) { | |
195 Iter iter; | |
196 // Start from the tail, least recently used. | |
197 CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart); | |
198 while (pixels != NULL && fRamUsed > limit) { | |
199 CachedPixels* prev = iter.prev(); | |
200 if (!pixels->isLocked()) { | |
201 this->removePixels(pixels); | |
202 } | |
203 pixels = prev; | |
204 } | |
205 } | |
206 } | |
OLD | NEW |