OLD | NEW |
| (Empty) |
1 #include "SkTextureCache.h" | |
2 | |
3 //#define TRACE_HASH_HITS | |
4 //#define TRACE_TEXTURE_CACHE_PURGE | |
5 | |
6 SkTextureCache::Entry::Entry(const SkBitmap& bitmap) | |
7 : fName(0), fKey(bitmap), fPrev(NULL), fNext(NULL) { | |
8 | |
9 fMemSize = SkGL::ComputeTextureMemorySize(bitmap); | |
10 fLockCount = 0; | |
11 } | |
12 | |
13 SkTextureCache::Entry::~Entry() { | |
14 if (fName != 0) { | |
15 glDeleteTextures(1, &fName); | |
16 } | |
17 } | |
18 | |
19 /////////////////////////////////////////////////////////////////////////////// | |
20 | |
21 SkTextureCache::SkTextureCache(size_t countMax, size_t sizeMax) | |
22 : fHead(NULL), fTail(NULL), | |
23 fTexCountMax(countMax), fTexSizeMax(sizeMax), | |
24 fTexCount(0), fTexSize(0) { | |
25 | |
26 bzero(fHash, sizeof(fHash)); | |
27 this->validate(); | |
28 } | |
29 | |
30 SkTextureCache::~SkTextureCache() { | |
31 #ifdef SK_DEBUG | |
32 Entry* entry = fHead; | |
33 while (entry) { | |
34 SkASSERT(entry->lockCount() == 0); | |
35 entry = entry->fNext; | |
36 } | |
37 #endif | |
38 this->validate(); | |
39 } | |
40 | |
41 void SkTextureCache::deleteAllCaches(bool texturesAreValid) { | |
42 this->validate(); | |
43 | |
44 Entry* entry = fHead; | |
45 while (entry) { | |
46 Entry* next = entry->fNext; | |
47 if (!texturesAreValid) { | |
48 entry->abandonTexture(); | |
49 } | |
50 SkDELETE(entry); | |
51 entry = next; | |
52 } | |
53 | |
54 fSorted.reset(); | |
55 bzero(fHash, sizeof(fHash)); | |
56 | |
57 fTexCount = 0; | |
58 fTexSize = 0; | |
59 | |
60 fTail = fHead = NULL; | |
61 | |
62 this->validate(); | |
63 } | |
64 | |
65 /////////////////////////////////////////////////////////////////////////////// | |
66 | |
67 int SkTextureCache::findInSorted(const Key& key) const { | |
68 int count = fSorted.count(); | |
69 if (count == 0) { | |
70 return ~0; | |
71 } | |
72 | |
73 Entry** sorted = fSorted.begin(); | |
74 int lo = 0; | |
75 int hi = count - 1; | |
76 while (lo < hi) { | |
77 int mid = (hi + lo) >> 1; | |
78 if (sorted[mid]->getKey() < key) { | |
79 lo = mid + 1; | |
80 } else { | |
81 hi = mid; | |
82 } | |
83 } | |
84 | |
85 // hi is now our best guess | |
86 const Entry* entry = sorted[hi]; | |
87 if (entry->getKey() == key) { | |
88 return hi; | |
89 } | |
90 | |
91 // return where to insert it | |
92 if (entry->getKey() < key) { | |
93 hi += 1; | |
94 } | |
95 return ~hi; // we twiddle to indicate not-found | |
96 } | |
97 | |
98 #ifdef TRACE_HASH_HITS | |
99 static int gHashHits; | |
100 static int gSortedHits; | |
101 #endif | |
102 | |
103 SkTextureCache::Entry* SkTextureCache::find(const Key& key, int* insert) const { | |
104 int count = fSorted.count(); | |
105 if (count == 0) { | |
106 *insert = 0; | |
107 return NULL; | |
108 } | |
109 | |
110 // check the hash first | |
111 int hashIndex = key.getHashIndex(); | |
112 Entry* entry = fHash[hashIndex]; | |
113 if (NULL != entry && entry->getKey() == key) { | |
114 #ifdef TRACE_HASH_HITS | |
115 gHashHits += 1; | |
116 #endif | |
117 return entry; | |
118 } | |
119 | |
120 int index = this->findInSorted(key); | |
121 if (index >= 0) { | |
122 #ifdef TRACE_HASH_HITS | |
123 gSortedHits += 1; | |
124 #endif | |
125 entry = fSorted[index]; | |
126 fHash[hashIndex] = entry; | |
127 return entry; | |
128 } | |
129 | |
130 // ~index is where to insert the entry | |
131 *insert = ~index; | |
132 return NULL; | |
133 } | |
134 | |
135 SkTextureCache::Entry* SkTextureCache::lock(const SkBitmap& bitmap) { | |
136 this->validate(); | |
137 | |
138 // call this before we call find(), so we don't reorder after find() and | |
139 // invalidate our index | |
140 this->purgeIfNecessary(SkGL::ComputeTextureMemorySize(bitmap)); | |
141 | |
142 Key key(bitmap); | |
143 int index; | |
144 Entry* entry = this->find(key, &index); | |
145 | |
146 if (NULL == entry) { | |
147 entry = SkNEW_ARGS(Entry, (bitmap)); | |
148 | |
149 entry->fName = SkGL::BindNewTexture(bitmap, &entry->fTexSize); | |
150 if (0 == entry->fName) { | |
151 SkDELETE(entry); | |
152 return NULL; | |
153 } | |
154 fHash[key.getHashIndex()] = entry; | |
155 *fSorted.insert(index) = entry; | |
156 | |
157 fTexCount += 1; | |
158 fTexSize += entry->memSize(); | |
159 } else { | |
160 // detach from our llist | |
161 Entry* prev = entry->fPrev; | |
162 Entry* next = entry->fNext; | |
163 if (prev) { | |
164 prev->fNext = next; | |
165 } else { | |
166 SkASSERT(fHead == entry); | |
167 fHead = next; | |
168 } | |
169 if (next) { | |
170 next->fPrev = prev; | |
171 } else { | |
172 SkASSERT(fTail == entry); | |
173 fTail = prev; | |
174 } | |
175 // now bind the texture | |
176 glBindTexture(GL_TEXTURE_2D, entry->fName); | |
177 } | |
178 | |
179 // add to head of llist for LRU | |
180 entry->fPrev = NULL; | |
181 entry->fNext = fHead; | |
182 if (NULL != fHead) { | |
183 SkASSERT(NULL == fHead->fPrev); | |
184 fHead->fPrev = entry; | |
185 } | |
186 fHead = entry; | |
187 if (NULL == fTail) { | |
188 fTail = entry; | |
189 } | |
190 | |
191 this->validate(); | |
192 entry->lock(); | |
193 | |
194 #ifdef TRACE_HASH_HITS | |
195 SkDebugf("---- texture cache hash=%d sorted=%d\n", gHashHits, gSortedHits); | |
196 #endif | |
197 return entry; | |
198 } | |
199 | |
200 void SkTextureCache::unlock(Entry* entry) { | |
201 this->validate(); | |
202 | |
203 #ifdef SK_DEBUG | |
204 SkASSERT(entry); | |
205 int index = this->findInSorted(entry->getKey()); | |
206 SkASSERT(fSorted[index] == entry); | |
207 #endif | |
208 | |
209 SkASSERT(entry->fLockCount > 0); | |
210 entry->unlock(); | |
211 } | |
212 | |
213 void SkTextureCache::purgeIfNecessary(size_t extraSize) { | |
214 this->validate(); | |
215 | |
216 size_t countMax = fTexCountMax; | |
217 size_t sizeMax = fTexSizeMax; | |
218 | |
219 // take extraSize into account, but watch for underflow of size_t | |
220 if (extraSize > sizeMax) { | |
221 sizeMax = 0; | |
222 } else { | |
223 sizeMax -= extraSize; | |
224 } | |
225 | |
226 Entry* entry = fTail; | |
227 while (entry) { | |
228 if (fTexCount <= countMax && fTexSize <= sizeMax) { | |
229 break; | |
230 } | |
231 | |
232 Entry* prev = entry->fPrev; | |
233 // don't purge an entry that is locked | |
234 if (entry->isLocked()) { | |
235 entry = prev; | |
236 continue; | |
237 } | |
238 | |
239 fTexCount -= 1; | |
240 fTexSize -= entry->memSize(); | |
241 | |
242 // remove from our sorted and hash arrays | |
243 int index = this->findInSorted(entry->getKey()); | |
244 SkASSERT(index >= 0); | |
245 fSorted.remove(index); | |
246 index = entry->getKey().getHashIndex(); | |
247 if (entry == fHash[index]) { | |
248 fHash[index] = NULL; | |
249 } | |
250 | |
251 // now detach it from our llist | |
252 Entry* next = entry->fNext; | |
253 if (prev) { | |
254 prev->fNext = next; | |
255 } else { | |
256 fHead = next; | |
257 } | |
258 if (next) { | |
259 next->fPrev = prev; | |
260 } else { | |
261 fTail = prev; | |
262 } | |
263 | |
264 // now delete it | |
265 #ifdef TRACE_TEXTURE_CACHE_PURGE | |
266 SkDebugf("---- purge texture cache %d size=%d\n", | |
267 entry->name(), entry->memSize()); | |
268 #endif | |
269 SkDELETE(entry); | |
270 | |
271 // keep going | |
272 entry = prev; | |
273 } | |
274 | |
275 this->validate(); | |
276 } | |
277 | |
278 void SkTextureCache::setMaxCount(size_t count) { | |
279 if (fTexCountMax != count) { | |
280 fTexCountMax = count; | |
281 this->purgeIfNecessary(0); | |
282 } | |
283 } | |
284 | |
285 void SkTextureCache::setMaxSize(size_t size) { | |
286 if (fTexSizeMax != size) { | |
287 fTexSizeMax = size; | |
288 this->purgeIfNecessary(0); | |
289 } | |
290 } | |
291 | |
292 /////////////////////////////////////////////////////////////////////////////// | |
293 | |
294 #ifdef SK_DEBUG | |
295 void SkTextureCache::validate() const { | |
296 if (0 == fTexCount) { | |
297 SkASSERT(0 == fTexSize); | |
298 SkASSERT(NULL == fHead); | |
299 SkASSERT(NULL == fTail); | |
300 return; | |
301 } | |
302 | |
303 SkASSERT(fTexSize); // do we allow a zero-sized texture? | |
304 SkASSERT(fHead); | |
305 SkASSERT(fTail); | |
306 | |
307 SkASSERT(NULL == fHead->fPrev); | |
308 SkASSERT(NULL == fTail->fNext); | |
309 if (1 == fTexCount) { | |
310 SkASSERT(fHead == fTail); | |
311 } | |
312 | |
313 const Entry* entry = fHead; | |
314 size_t count = 0; | |
315 size_t size = 0; | |
316 size_t i; | |
317 | |
318 while (entry != NULL) { | |
319 SkASSERT(count < fTexCount); | |
320 SkASSERT(size < fTexSize); | |
321 size += entry->memSize(); | |
322 count += 1; | |
323 if (NULL == entry->fNext) { | |
324 SkASSERT(fTail == entry); | |
325 } | |
326 entry = entry->fNext; | |
327 } | |
328 SkASSERT(count == fTexCount); | |
329 SkASSERT(size == fTexSize); | |
330 | |
331 count = 0; | |
332 size = 0; | |
333 entry = fTail; | |
334 while (entry != NULL) { | |
335 SkASSERT(count < fTexCount); | |
336 SkASSERT(size < fTexSize); | |
337 size += entry->memSize(); | |
338 count += 1; | |
339 if (NULL == entry->fPrev) { | |
340 SkASSERT(fHead == entry); | |
341 } | |
342 entry = entry->fPrev; | |
343 } | |
344 SkASSERT(count == fTexCount); | |
345 SkASSERT(size == fTexSize); | |
346 | |
347 SkASSERT(count == (size_t)fSorted.count()); | |
348 for (i = 1; i < count; i++) { | |
349 SkASSERT(fSorted[i-1]->getKey() < fSorted[i]->getKey()); | |
350 } | |
351 | |
352 for (i = 0; i < kHashCount; i++) { | |
353 if (fHash[i]) { | |
354 size_t index = fHash[i]->getKey().getHashIndex(); | |
355 SkASSERT(index == i); | |
356 index = fSorted.find(fHash[i]); | |
357 SkASSERT((size_t)index < count); | |
358 } | |
359 } | |
360 } | |
361 #endif | |
362 | |
363 | |
OLD | NEW |