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