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