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
13SkBitmapHeapEntry::SkBitmapHeapEntry()
14    : fSlot(-1)
15    , fRefCount(0)
16    , fBytesAllocated(0) {
17}
18
19SkBitmapHeapEntry::~SkBitmapHeapEntry() {
20    SkASSERT(0 == fRefCount);
21}
22
23void 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
36static bool operator<(const SkIPoint& a, const SkIPoint& b) {
37    return *(const int64_t*)&a < *(const int64_t*)&b;
38}
39
40static bool operator>(const SkIPoint& a, const SkIPoint& b) {
41    return *(const int64_t*)&a > *(const int64_t*)&b;
42}
43
44bool 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
66SkBitmapHeap::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
77SkBitmapHeap::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
89SkBitmapHeap::~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
111void 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
139void 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
152SkBitmapHeap::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
177size_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
221int 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
239bool 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
259int 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
272int32_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
377void SkBitmapHeap::deferAddingOwners() {
378    fDeferAddingOwners = true;
379}
380
381void 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