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