|
1 |
|
2 /* |
|
3 * Copyright 2012 Google Inc. |
|
4 * |
|
5 * Use of this source code is governed by a BSD-style license that can be |
|
6 * found in the LICENSE file. |
|
7 */ |
|
8 |
|
9 #include "SkBitmapHeap.h" |
|
10 |
|
11 #include "SkBitmap.h" |
|
12 #include "SkReadBuffer.h" |
|
13 #include "SkWriteBuffer.h" |
|
14 #include "SkTSearch.h" |
|
15 |
|
16 SkBitmapHeapEntry::SkBitmapHeapEntry() |
|
17 : fSlot(-1) |
|
18 , fRefCount(0) |
|
19 , fBytesAllocated(0) { |
|
20 } |
|
21 |
|
22 SkBitmapHeapEntry::~SkBitmapHeapEntry() { |
|
23 SkASSERT(0 == fRefCount); |
|
24 } |
|
25 |
|
26 void SkBitmapHeapEntry::addReferences(int count) { |
|
27 if (0 == fRefCount) { |
|
28 // If there are no current owners then the heap manager |
|
29 // will be the only one able to modify it, so it does not |
|
30 // need to be an atomic operation. |
|
31 fRefCount = count; |
|
32 } else { |
|
33 sk_atomic_add(&fRefCount, count); |
|
34 } |
|
35 } |
|
36 |
|
37 /////////////////////////////////////////////////////////////////////////////// |
|
38 |
|
39 static bool operator<(const SkIPoint& a, const SkIPoint& b) { |
|
40 return *(const int64_t*)&a < *(const int64_t*)&b; |
|
41 } |
|
42 |
|
43 static bool operator>(const SkIPoint& a, const SkIPoint& b) { |
|
44 return *(const int64_t*)&a > *(const int64_t*)&b; |
|
45 } |
|
46 |
|
47 bool SkBitmapHeap::LookupEntry::Less(const SkBitmapHeap::LookupEntry& a, |
|
48 const SkBitmapHeap::LookupEntry& b) { |
|
49 if (a.fGenerationId < b.fGenerationId) { |
|
50 return true; |
|
51 } else if (a.fGenerationId > b.fGenerationId) { |
|
52 return false; |
|
53 } else if (a.fPixelOrigin < b.fPixelOrigin) { |
|
54 return true; |
|
55 } else if (a.fPixelOrigin > b.fPixelOrigin) { |
|
56 return false; |
|
57 } else if (a.fWidth < b.fWidth) { |
|
58 return true; |
|
59 } else if (a.fWidth > b.fWidth) { |
|
60 return false; |
|
61 } else if (a.fHeight < b.fHeight) { |
|
62 return true; |
|
63 } |
|
64 return false; |
|
65 } |
|
66 |
|
67 /////////////////////////////////////////////////////////////////////////////// |
|
68 |
|
69 SkBitmapHeap::SkBitmapHeap(int32_t preferredSize, int32_t ownerCount) |
|
70 : INHERITED() |
|
71 , fExternalStorage(NULL) |
|
72 , fMostRecentlyUsed(NULL) |
|
73 , fLeastRecentlyUsed(NULL) |
|
74 , fPreferredCount(preferredSize) |
|
75 , fOwnerCount(ownerCount) |
|
76 , fBytesAllocated(0) |
|
77 , fDeferAddingOwners(false) { |
|
78 } |
|
79 |
|
80 SkBitmapHeap::SkBitmapHeap(ExternalStorage* storage, int32_t preferredSize) |
|
81 : INHERITED() |
|
82 , fExternalStorage(storage) |
|
83 , fMostRecentlyUsed(NULL) |
|
84 , fLeastRecentlyUsed(NULL) |
|
85 , fPreferredCount(preferredSize) |
|
86 , fOwnerCount(IGNORE_OWNERS) |
|
87 , fBytesAllocated(0) |
|
88 , fDeferAddingOwners(false) { |
|
89 SkSafeRef(storage); |
|
90 } |
|
91 |
|
92 SkBitmapHeap::~SkBitmapHeap() { |
|
93 SkDEBUGCODE( |
|
94 for (int i = 0; i < fStorage.count(); i++) { |
|
95 bool unused = false; |
|
96 for (int j = 0; j < fUnusedSlots.count(); j++) { |
|
97 if (fUnusedSlots[j] == fStorage[i]->fSlot) { |
|
98 unused = true; |
|
99 break; |
|
100 } |
|
101 } |
|
102 if (!unused) { |
|
103 fBytesAllocated -= fStorage[i]->fBytesAllocated; |
|
104 } |
|
105 } |
|
106 fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry)); |
|
107 ) |
|
108 SkASSERT(0 == fBytesAllocated); |
|
109 fStorage.deleteAll(); |
|
110 SkSafeUnref(fExternalStorage); |
|
111 fLookupTable.deleteAll(); |
|
112 } |
|
113 |
|
114 SkTRefArray<SkBitmap>* SkBitmapHeap::extractBitmaps() const { |
|
115 const int size = fStorage.count(); |
|
116 SkTRefArray<SkBitmap>* array = NULL; |
|
117 if (size > 0) { |
|
118 array = SkTRefArray<SkBitmap>::Create(size); |
|
119 for (int i = 0; i < size; i++) { |
|
120 // make a shallow copy of the bitmap |
|
121 array->writableAt(i) = fStorage[i]->fBitmap; |
|
122 } |
|
123 } |
|
124 return array; |
|
125 } |
|
126 |
|
127 void SkBitmapHeap::removeFromLRU(SkBitmapHeap::LookupEntry* entry) { |
|
128 if (fMostRecentlyUsed == entry) { |
|
129 fMostRecentlyUsed = entry->fLessRecentlyUsed; |
|
130 if (NULL == fMostRecentlyUsed) { |
|
131 SkASSERT(fLeastRecentlyUsed == entry); |
|
132 fLeastRecentlyUsed = NULL; |
|
133 } else { |
|
134 fMostRecentlyUsed->fMoreRecentlyUsed = NULL; |
|
135 } |
|
136 } else { |
|
137 // Remove entry from its prior place, and make sure to cover the hole. |
|
138 if (fLeastRecentlyUsed == entry) { |
|
139 SkASSERT(entry->fMoreRecentlyUsed != NULL); |
|
140 fLeastRecentlyUsed = entry->fMoreRecentlyUsed; |
|
141 } |
|
142 // Since we have already considered the case where entry is the most recently used, it must |
|
143 // have a more recently used at this point. |
|
144 SkASSERT(entry->fMoreRecentlyUsed != NULL); |
|
145 entry->fMoreRecentlyUsed->fLessRecentlyUsed = entry->fLessRecentlyUsed; |
|
146 |
|
147 if (entry->fLessRecentlyUsed != NULL) { |
|
148 SkASSERT(fLeastRecentlyUsed != entry); |
|
149 entry->fLessRecentlyUsed->fMoreRecentlyUsed = entry->fMoreRecentlyUsed; |
|
150 } |
|
151 } |
|
152 entry->fMoreRecentlyUsed = NULL; |
|
153 } |
|
154 |
|
155 void SkBitmapHeap::appendToLRU(SkBitmapHeap::LookupEntry* entry) { |
|
156 if (fMostRecentlyUsed != NULL) { |
|
157 SkASSERT(NULL == fMostRecentlyUsed->fMoreRecentlyUsed); |
|
158 fMostRecentlyUsed->fMoreRecentlyUsed = entry; |
|
159 entry->fLessRecentlyUsed = fMostRecentlyUsed; |
|
160 } |
|
161 fMostRecentlyUsed = entry; |
|
162 if (NULL == fLeastRecentlyUsed) { |
|
163 fLeastRecentlyUsed = entry; |
|
164 } |
|
165 } |
|
166 |
|
167 // iterate through our LRU cache and try to find an entry to evict |
|
168 SkBitmapHeap::LookupEntry* SkBitmapHeap::findEntryToReplace(const SkBitmap& replacement) { |
|
169 SkASSERT(fPreferredCount != UNLIMITED_SIZE); |
|
170 SkASSERT(fStorage.count() >= fPreferredCount); |
|
171 |
|
172 SkBitmapHeap::LookupEntry* iter = fLeastRecentlyUsed; |
|
173 while (iter != NULL) { |
|
174 SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot]; |
|
175 if (heapEntry->fRefCount > 0) { |
|
176 // If the least recently used bitmap has not been unreferenced |
|
177 // by its owner, then according to our LRU specifications a more |
|
178 // recently used one can not have used all its references yet either. |
|
179 return NULL; |
|
180 } |
|
181 if (replacement.getGenerationID() == iter->fGenerationId) { |
|
182 // Do not replace a bitmap with a new one using the same |
|
183 // pixel ref. Instead look for a different one that will |
|
184 // potentially free up more space. |
|
185 iter = iter->fMoreRecentlyUsed; |
|
186 } else { |
|
187 return iter; |
|
188 } |
|
189 } |
|
190 return NULL; |
|
191 } |
|
192 |
|
193 size_t SkBitmapHeap::freeMemoryIfPossible(size_t bytesToFree) { |
|
194 if (UNLIMITED_SIZE == fPreferredCount) { |
|
195 return 0; |
|
196 } |
|
197 LookupEntry* iter = fLeastRecentlyUsed; |
|
198 size_t origBytesAllocated = fBytesAllocated; |
|
199 // Purge starting from LRU until a non-evictable bitmap is found or until |
|
200 // everything is evicted. |
|
201 while (iter != NULL) { |
|
202 SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot]; |
|
203 if (heapEntry->fRefCount > 0) { |
|
204 break; |
|
205 } |
|
206 LookupEntry* next = iter->fMoreRecentlyUsed; |
|
207 this->removeEntryFromLookupTable(iter); |
|
208 // Free the pixel memory. removeEntryFromLookupTable already reduced |
|
209 // fBytesAllocated properly. |
|
210 heapEntry->fBitmap.reset(); |
|
211 // Add to list of unused slots which can be reused in the future. |
|
212 fUnusedSlots.push(heapEntry->fSlot); |
|
213 iter = next; |
|
214 if (origBytesAllocated - fBytesAllocated >= bytesToFree) { |
|
215 break; |
|
216 } |
|
217 } |
|
218 |
|
219 if (fLeastRecentlyUsed != iter) { |
|
220 // There was at least one eviction. |
|
221 fLeastRecentlyUsed = iter; |
|
222 if (NULL == fLeastRecentlyUsed) { |
|
223 // Everything was evicted |
|
224 fMostRecentlyUsed = NULL; |
|
225 fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry)); |
|
226 fStorage.deleteAll(); |
|
227 fUnusedSlots.reset(); |
|
228 SkASSERT(0 == fBytesAllocated); |
|
229 } else { |
|
230 fLeastRecentlyUsed->fLessRecentlyUsed = NULL; |
|
231 } |
|
232 } |
|
233 |
|
234 return origBytesAllocated - fBytesAllocated; |
|
235 } |
|
236 |
|
237 int SkBitmapHeap::findInLookupTable(const LookupEntry& indexEntry, SkBitmapHeapEntry** entry) { |
|
238 int index = SkTSearch<const LookupEntry, LookupEntry::Less>( |
|
239 (const LookupEntry**)fLookupTable.begin(), |
|
240 fLookupTable.count(), |
|
241 &indexEntry, sizeof(void*)); |
|
242 |
|
243 if (index < 0) { |
|
244 // insert ourselves into the bitmapIndex |
|
245 index = ~index; |
|
246 *fLookupTable.insert(index) = SkNEW_ARGS(LookupEntry, (indexEntry)); |
|
247 } else if (entry != NULL) { |
|
248 // populate the entry if needed |
|
249 *entry = fStorage[fLookupTable[index]->fStorageSlot]; |
|
250 } |
|
251 |
|
252 return index; |
|
253 } |
|
254 |
|
255 bool SkBitmapHeap::copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap) { |
|
256 SkASSERT(!fExternalStorage); |
|
257 |
|
258 // If the bitmap is mutable, we need to do a deep copy, since the |
|
259 // caller may modify it afterwards. |
|
260 if (originalBitmap.isImmutable()) { |
|
261 copiedBitmap = originalBitmap; |
|
262 // TODO if we have the pixel ref in the heap we could pass it here to avoid a potential deep copy |
|
263 // else if (sharedPixelRef != NULL) { |
|
264 // copiedBitmap = orig; |
|
265 // copiedBitmap.setPixelRef(sharedPixelRef, originalBitmap.pixelRefOffset()); |
|
266 } else if (originalBitmap.empty()) { |
|
267 copiedBitmap.reset(); |
|
268 } else if (!originalBitmap.deepCopyTo(&copiedBitmap)) { |
|
269 return false; |
|
270 } |
|
271 copiedBitmap.setImmutable(); |
|
272 return true; |
|
273 } |
|
274 |
|
275 int SkBitmapHeap::removeEntryFromLookupTable(LookupEntry* entry) { |
|
276 // remove the bitmap index for the deleted entry |
|
277 SkDEBUGCODE(int count = fLookupTable.count();) |
|
278 int index = this->findInLookupTable(*entry, NULL); |
|
279 // Verify that findInLookupTable found an existing entry rather than adding |
|
280 // a new entry to the lookup table. |
|
281 SkASSERT(count == fLookupTable.count()); |
|
282 fBytesAllocated -= fStorage[entry->fStorageSlot]->fBytesAllocated; |
|
283 SkDELETE(fLookupTable[index]); |
|
284 fLookupTable.remove(index); |
|
285 return index; |
|
286 } |
|
287 |
|
288 int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) { |
|
289 SkBitmapHeapEntry* entry = NULL; |
|
290 int searchIndex = this->findInLookupTable(LookupEntry(originalBitmap), &entry); |
|
291 |
|
292 if (entry) { |
|
293 // Already had a copy of the bitmap in the heap. |
|
294 if (fOwnerCount != IGNORE_OWNERS) { |
|
295 if (fDeferAddingOwners) { |
|
296 *fDeferredEntries.append() = entry->fSlot; |
|
297 } else { |
|
298 entry->addReferences(fOwnerCount); |
|
299 } |
|
300 } |
|
301 if (fPreferredCount != UNLIMITED_SIZE) { |
|
302 LookupEntry* lookupEntry = fLookupTable[searchIndex]; |
|
303 if (lookupEntry != fMostRecentlyUsed) { |
|
304 this->removeFromLRU(lookupEntry); |
|
305 this->appendToLRU(lookupEntry); |
|
306 } |
|
307 } |
|
308 return entry->fSlot; |
|
309 } |
|
310 |
|
311 // decide if we need to evict an existing heap entry or create a new one |
|
312 if (fPreferredCount != UNLIMITED_SIZE && fStorage.count() >= fPreferredCount) { |
|
313 // iterate through our LRU cache and try to find an entry to evict |
|
314 LookupEntry* lookupEntry = this->findEntryToReplace(originalBitmap); |
|
315 if (lookupEntry != NULL) { |
|
316 // we found an entry to evict |
|
317 entry = fStorage[lookupEntry->fStorageSlot]; |
|
318 // Remove it from the LRU. The new entry will be added to the LRU later. |
|
319 this->removeFromLRU(lookupEntry); |
|
320 int index = this->removeEntryFromLookupTable(lookupEntry); |
|
321 |
|
322 // update the current search index now that we have removed one |
|
323 if (index < searchIndex) { |
|
324 searchIndex--; |
|
325 } |
|
326 } |
|
327 } |
|
328 |
|
329 // if we didn't have an entry yet we need to create one |
|
330 if (!entry) { |
|
331 if (fPreferredCount != UNLIMITED_SIZE && fUnusedSlots.count() > 0) { |
|
332 int slot; |
|
333 fUnusedSlots.pop(&slot); |
|
334 entry = fStorage[slot]; |
|
335 } else { |
|
336 entry = SkNEW(SkBitmapHeapEntry); |
|
337 fStorage.append(1, &entry); |
|
338 entry->fSlot = fStorage.count() - 1; |
|
339 fBytesAllocated += sizeof(SkBitmapHeapEntry); |
|
340 } |
|
341 } |
|
342 |
|
343 // create a copy of the bitmap |
|
344 bool copySucceeded; |
|
345 if (fExternalStorage) { |
|
346 copySucceeded = fExternalStorage->insert(originalBitmap, entry->fSlot); |
|
347 } else { |
|
348 copySucceeded = copyBitmap(originalBitmap, entry->fBitmap); |
|
349 } |
|
350 |
|
351 // if the copy failed then we must abort |
|
352 if (!copySucceeded) { |
|
353 // delete the index |
|
354 SkDELETE(fLookupTable[searchIndex]); |
|
355 fLookupTable.remove(searchIndex); |
|
356 // If entry is the last slot in storage, it is safe to delete it. |
|
357 if (fStorage.count() - 1 == entry->fSlot) { |
|
358 // free the slot |
|
359 fStorage.remove(entry->fSlot); |
|
360 fBytesAllocated -= sizeof(SkBitmapHeapEntry); |
|
361 SkDELETE(entry); |
|
362 } else { |
|
363 fUnusedSlots.push(entry->fSlot); |
|
364 } |
|
365 return INVALID_SLOT; |
|
366 } |
|
367 |
|
368 // update the index with the appropriate slot in the heap |
|
369 fLookupTable[searchIndex]->fStorageSlot = entry->fSlot; |
|
370 |
|
371 // compute the space taken by this entry |
|
372 // TODO if there is a shared pixel ref don't count it |
|
373 // If the SkBitmap does not share an SkPixelRef with an SkBitmap already |
|
374 // in the SharedHeap, also include the size of its pixels. |
|
375 entry->fBytesAllocated = originalBitmap.getSize(); |
|
376 |
|
377 // add the bytes from this entry to the total count |
|
378 fBytesAllocated += entry->fBytesAllocated; |
|
379 |
|
380 if (fOwnerCount != IGNORE_OWNERS) { |
|
381 if (fDeferAddingOwners) { |
|
382 *fDeferredEntries.append() = entry->fSlot; |
|
383 } else { |
|
384 entry->addReferences(fOwnerCount); |
|
385 } |
|
386 } |
|
387 if (fPreferredCount != UNLIMITED_SIZE) { |
|
388 this->appendToLRU(fLookupTable[searchIndex]); |
|
389 } |
|
390 return entry->fSlot; |
|
391 } |
|
392 |
|
393 void SkBitmapHeap::deferAddingOwners() { |
|
394 fDeferAddingOwners = true; |
|
395 } |
|
396 |
|
397 void SkBitmapHeap::endAddingOwnersDeferral(bool add) { |
|
398 if (add) { |
|
399 for (int i = 0; i < fDeferredEntries.count(); i++) { |
|
400 SkASSERT(fOwnerCount != IGNORE_OWNERS); |
|
401 SkBitmapHeapEntry* heapEntry = this->getEntry(fDeferredEntries[i]); |
|
402 SkASSERT(heapEntry != NULL); |
|
403 heapEntry->addReferences(fOwnerCount); |
|
404 } |
|
405 } |
|
406 fDeferAddingOwners = false; |
|
407 fDeferredEntries.reset(); |
|
408 } |