summaryrefslogtreecommitdiff
path: root/core/SkBitmapHeap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/SkBitmapHeap.cpp')
-rw-r--r--core/SkBitmapHeap.cpp402
1 files changed, 402 insertions, 0 deletions
diff --git a/core/SkBitmapHeap.cpp b/core/SkBitmapHeap.cpp
new file mode 100644
index 00000000..f3428db6
--- /dev/null
+++ b/core/SkBitmapHeap.cpp
@@ -0,0 +1,402 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapHeap.h"
+
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkTSearch.h"
+
+SK_DEFINE_INST_COUNT(SkBitmapHeapReader)
+SK_DEFINE_INST_COUNT(SkBitmapHeap::ExternalStorage)
+
+SkBitmapHeapEntry::SkBitmapHeapEntry()
+ : fSlot(-1)
+ , fRefCount(0)
+ , fBytesAllocated(0) {
+}
+
+SkBitmapHeapEntry::~SkBitmapHeapEntry() {
+ SkASSERT(0 == fRefCount);
+}
+
+void SkBitmapHeapEntry::addReferences(int count) {
+ if (0 == fRefCount) {
+ // If there are no current owners then the heap manager
+ // will be the only one able to modify it, so it does not
+ // need to be an atomic operation.
+ fRefCount = count;
+ } else {
+ sk_atomic_add(&fRefCount, count);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkBitmapHeap::LookupEntry::Less(const SkBitmapHeap::LookupEntry& a,
+ const SkBitmapHeap::LookupEntry& b) {
+ if (a.fGenerationId < b.fGenerationId) {
+ return true;
+ } else if (a.fGenerationId > b.fGenerationId) {
+ return false;
+ } else if (a.fPixelOffset < b.fPixelOffset) {
+ return true;
+ } else if (a.fPixelOffset > b.fPixelOffset) {
+ return false;
+ } else if (a.fWidth < b.fWidth) {
+ return true;
+ } else if (a.fWidth > b.fWidth) {
+ return false;
+ } else if (a.fHeight < b.fHeight) {
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapHeap::SkBitmapHeap(int32_t preferredSize, int32_t ownerCount)
+ : INHERITED()
+ , fExternalStorage(NULL)
+ , fMostRecentlyUsed(NULL)
+ , fLeastRecentlyUsed(NULL)
+ , fPreferredCount(preferredSize)
+ , fOwnerCount(ownerCount)
+ , fBytesAllocated(0)
+ , fDeferAddingOwners(false) {
+}
+
+SkBitmapHeap::SkBitmapHeap(ExternalStorage* storage, int32_t preferredSize)
+ : INHERITED()
+ , fExternalStorage(storage)
+ , fMostRecentlyUsed(NULL)
+ , fLeastRecentlyUsed(NULL)
+ , fPreferredCount(preferredSize)
+ , fOwnerCount(IGNORE_OWNERS)
+ , fBytesAllocated(0)
+ , fDeferAddingOwners(false) {
+ SkSafeRef(storage);
+}
+
+SkBitmapHeap::~SkBitmapHeap() {
+ SkDEBUGCODE(
+ for (int i = 0; i < fStorage.count(); i++) {
+ bool unused = false;
+ for (int j = 0; j < fUnusedSlots.count(); j++) {
+ if (fUnusedSlots[j] == fStorage[i]->fSlot) {
+ unused = true;
+ break;
+ }
+ }
+ if (!unused) {
+ fBytesAllocated -= fStorage[i]->fBytesAllocated;
+ }
+ }
+ fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry));
+ )
+ SkASSERT(0 == fBytesAllocated);
+ fStorage.deleteAll();
+ SkSafeUnref(fExternalStorage);
+ fLookupTable.deleteAll();
+}
+
+SkTRefArray<SkBitmap>* SkBitmapHeap::extractBitmaps() const {
+ const int size = fStorage.count();
+ SkTRefArray<SkBitmap>* array = NULL;
+ if (size > 0) {
+ array = SkTRefArray<SkBitmap>::Create(size);
+ for (int i = 0; i < size; i++) {
+ // make a shallow copy of the bitmap
+ array->writableAt(i) = fStorage[i]->fBitmap;
+ }
+ }
+ return array;
+}
+
+void SkBitmapHeap::removeFromLRU(SkBitmapHeap::LookupEntry* entry) {
+ if (fMostRecentlyUsed == entry) {
+ fMostRecentlyUsed = entry->fLessRecentlyUsed;
+ if (NULL == fMostRecentlyUsed) {
+ SkASSERT(fLeastRecentlyUsed == entry);
+ fLeastRecentlyUsed = NULL;
+ } else {
+ fMostRecentlyUsed->fMoreRecentlyUsed = NULL;
+ }
+ } else {
+ // Remove entry from its prior place, and make sure to cover the hole.
+ if (fLeastRecentlyUsed == entry) {
+ SkASSERT(entry->fMoreRecentlyUsed != NULL);
+ fLeastRecentlyUsed = entry->fMoreRecentlyUsed;
+ }
+ // Since we have already considered the case where entry is the most recently used, it must
+ // have a more recently used at this point.
+ SkASSERT(entry->fMoreRecentlyUsed != NULL);
+ entry->fMoreRecentlyUsed->fLessRecentlyUsed = entry->fLessRecentlyUsed;
+
+ if (entry->fLessRecentlyUsed != NULL) {
+ SkASSERT(fLeastRecentlyUsed != entry);
+ entry->fLessRecentlyUsed->fMoreRecentlyUsed = entry->fMoreRecentlyUsed;
+ }
+ }
+ entry->fMoreRecentlyUsed = NULL;
+}
+
+void SkBitmapHeap::appendToLRU(SkBitmapHeap::LookupEntry* entry) {
+ if (fMostRecentlyUsed != NULL) {
+ SkASSERT(NULL == fMostRecentlyUsed->fMoreRecentlyUsed);
+ fMostRecentlyUsed->fMoreRecentlyUsed = entry;
+ entry->fLessRecentlyUsed = fMostRecentlyUsed;
+ }
+ fMostRecentlyUsed = entry;
+ if (NULL == fLeastRecentlyUsed) {
+ fLeastRecentlyUsed = entry;
+ }
+}
+
+// iterate through our LRU cache and try to find an entry to evict
+SkBitmapHeap::LookupEntry* SkBitmapHeap::findEntryToReplace(const SkBitmap& replacement) {
+ SkASSERT(fPreferredCount != UNLIMITED_SIZE);
+ SkASSERT(fStorage.count() >= fPreferredCount);
+
+ SkBitmapHeap::LookupEntry* iter = fLeastRecentlyUsed;
+ while (iter != NULL) {
+ SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot];
+ if (heapEntry->fRefCount > 0) {
+ // If the least recently used bitmap has not been unreferenced
+ // by its owner, then according to our LRU specifications a more
+ // recently used one can not have used all its references yet either.
+ return NULL;
+ }
+ if (replacement.getGenerationID() == iter->fGenerationId) {
+ // Do not replace a bitmap with a new one using the same
+ // pixel ref. Instead look for a different one that will
+ // potentially free up more space.
+ iter = iter->fMoreRecentlyUsed;
+ } else {
+ return iter;
+ }
+ }
+ return NULL;
+}
+
+size_t SkBitmapHeap::freeMemoryIfPossible(size_t bytesToFree) {
+ if (UNLIMITED_SIZE == fPreferredCount) {
+ return 0;
+ }
+ LookupEntry* iter = fLeastRecentlyUsed;
+ size_t origBytesAllocated = fBytesAllocated;
+ // Purge starting from LRU until a non-evictable bitmap is found or until
+ // everything is evicted.
+ while (iter != NULL) {
+ SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot];
+ if (heapEntry->fRefCount > 0) {
+ break;
+ }
+ LookupEntry* next = iter->fMoreRecentlyUsed;
+ this->removeEntryFromLookupTable(iter);
+ // Free the pixel memory. removeEntryFromLookupTable already reduced
+ // fBytesAllocated properly.
+ heapEntry->fBitmap.reset();
+ // Add to list of unused slots which can be reused in the future.
+ fUnusedSlots.push(heapEntry->fSlot);
+ iter = next;
+ if (origBytesAllocated - fBytesAllocated >= bytesToFree) {
+ break;
+ }
+ }
+
+ if (fLeastRecentlyUsed != iter) {
+ // There was at least one eviction.
+ fLeastRecentlyUsed = iter;
+ if (NULL == fLeastRecentlyUsed) {
+ // Everything was evicted
+ fMostRecentlyUsed = NULL;
+ fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry));
+ fStorage.deleteAll();
+ fUnusedSlots.reset();
+ SkASSERT(0 == fBytesAllocated);
+ } else {
+ fLeastRecentlyUsed->fLessRecentlyUsed = NULL;
+ }
+ }
+
+ return origBytesAllocated - fBytesAllocated;
+}
+
+int SkBitmapHeap::findInLookupTable(const LookupEntry& indexEntry, SkBitmapHeapEntry** entry) {
+ int index = SkTSearch<const LookupEntry, LookupEntry::Less>(
+ (const LookupEntry**)fLookupTable.begin(),
+ fLookupTable.count(),
+ &indexEntry, sizeof(void*));
+
+ if (index < 0) {
+ // insert ourselves into the bitmapIndex
+ index = ~index;
+ *fLookupTable.insert(index) = SkNEW_ARGS(LookupEntry, (indexEntry));
+ } else if (entry != NULL) {
+ // populate the entry if needed
+ *entry = fStorage[fLookupTable[index]->fStorageSlot];
+ }
+
+ return index;
+}
+
+bool SkBitmapHeap::copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap) {
+ SkASSERT(!fExternalStorage);
+
+ // If the bitmap is mutable, we need to do a deep copy, since the
+ // caller may modify it afterwards.
+ if (originalBitmap.isImmutable()) {
+ copiedBitmap = originalBitmap;
+// TODO if we have the pixel ref in the heap we could pass it here to avoid a potential deep copy
+// else if (sharedPixelRef != NULL) {
+// copiedBitmap = orig;
+// copiedBitmap.setPixelRef(sharedPixelRef, originalBitmap.pixelRefOffset());
+ } else if (originalBitmap.empty()) {
+ copiedBitmap.reset();
+ } else if (!originalBitmap.deepCopyTo(&copiedBitmap, originalBitmap.getConfig())) {
+ return false;
+ }
+ copiedBitmap.setImmutable();
+ return true;
+}
+
+int SkBitmapHeap::removeEntryFromLookupTable(LookupEntry* entry) {
+ // remove the bitmap index for the deleted entry
+ SkDEBUGCODE(int count = fLookupTable.count();)
+ int index = this->findInLookupTable(*entry, NULL);
+ // Verify that findInLookupTable found an existing entry rather than adding
+ // a new entry to the lookup table.
+ SkASSERT(count == fLookupTable.count());
+ fBytesAllocated -= fStorage[entry->fStorageSlot]->fBytesAllocated;
+ SkDELETE(fLookupTable[index]);
+ fLookupTable.remove(index);
+ return index;
+}
+
+int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
+ SkBitmapHeapEntry* entry = NULL;
+ int searchIndex = this->findInLookupTable(LookupEntry(originalBitmap), &entry);
+
+ if (entry) {
+ // Already had a copy of the bitmap in the heap.
+ if (fOwnerCount != IGNORE_OWNERS) {
+ if (fDeferAddingOwners) {
+ *fDeferredEntries.append() = entry->fSlot;
+ } else {
+ entry->addReferences(fOwnerCount);
+ }
+ }
+ if (fPreferredCount != UNLIMITED_SIZE) {
+ LookupEntry* lookupEntry = fLookupTable[searchIndex];
+ if (lookupEntry != fMostRecentlyUsed) {
+ this->removeFromLRU(lookupEntry);
+ this->appendToLRU(lookupEntry);
+ }
+ }
+ return entry->fSlot;
+ }
+
+ // decide if we need to evict an existing heap entry or create a new one
+ if (fPreferredCount != UNLIMITED_SIZE && fStorage.count() >= fPreferredCount) {
+ // iterate through our LRU cache and try to find an entry to evict
+ LookupEntry* lookupEntry = this->findEntryToReplace(originalBitmap);
+ if (lookupEntry != NULL) {
+ // we found an entry to evict
+ entry = fStorage[lookupEntry->fStorageSlot];
+ // Remove it from the LRU. The new entry will be added to the LRU later.
+ this->removeFromLRU(lookupEntry);
+ int index = this->removeEntryFromLookupTable(lookupEntry);
+
+ // update the current search index now that we have removed one
+ if (index < searchIndex) {
+ searchIndex--;
+ }
+ }
+ }
+
+ // if we didn't have an entry yet we need to create one
+ if (!entry) {
+ if (fPreferredCount != UNLIMITED_SIZE && fUnusedSlots.count() > 0) {
+ int slot;
+ fUnusedSlots.pop(&slot);
+ entry = fStorage[slot];
+ } else {
+ entry = SkNEW(SkBitmapHeapEntry);
+ fStorage.append(1, &entry);
+ entry->fSlot = fStorage.count() - 1;
+ fBytesAllocated += sizeof(SkBitmapHeapEntry);
+ }
+ }
+
+ // create a copy of the bitmap
+ bool copySucceeded;
+ if (fExternalStorage) {
+ copySucceeded = fExternalStorage->insert(originalBitmap, entry->fSlot);
+ } else {
+ copySucceeded = copyBitmap(originalBitmap, entry->fBitmap);
+ }
+
+ // if the copy failed then we must abort
+ if (!copySucceeded) {
+ // delete the index
+ SkDELETE(fLookupTable[searchIndex]);
+ fLookupTable.remove(searchIndex);
+ // If entry is the last slot in storage, it is safe to delete it.
+ if (fStorage.count() - 1 == entry->fSlot) {
+ // free the slot
+ fStorage.remove(entry->fSlot);
+ fBytesAllocated -= sizeof(SkBitmapHeapEntry);
+ SkDELETE(entry);
+ } else {
+ fUnusedSlots.push(entry->fSlot);
+ }
+ return INVALID_SLOT;
+ }
+
+ // update the index with the appropriate slot in the heap
+ fLookupTable[searchIndex]->fStorageSlot = entry->fSlot;
+
+ // compute the space taken by this entry
+ // TODO if there is a shared pixel ref don't count it
+ // If the SkBitmap does not share an SkPixelRef with an SkBitmap already
+ // in the SharedHeap, also include the size of its pixels.
+ entry->fBytesAllocated = originalBitmap.getSize();
+
+ // add the bytes from this entry to the total count
+ fBytesAllocated += entry->fBytesAllocated;
+
+ if (fOwnerCount != IGNORE_OWNERS) {
+ if (fDeferAddingOwners) {
+ *fDeferredEntries.append() = entry->fSlot;
+ } else {
+ entry->addReferences(fOwnerCount);
+ }
+ }
+ if (fPreferredCount != UNLIMITED_SIZE) {
+ this->appendToLRU(fLookupTable[searchIndex]);
+ }
+ return entry->fSlot;
+}
+
+void SkBitmapHeap::deferAddingOwners() {
+ fDeferAddingOwners = true;
+}
+
+void SkBitmapHeap::endAddingOwnersDeferral(bool add) {
+ if (add) {
+ for (int i = 0; i < fDeferredEntries.count(); i++) {
+ SkASSERT(fOwnerCount != IGNORE_OWNERS);
+ SkBitmapHeapEntry* heapEntry = this->getEntry(fDeferredEntries[i]);
+ SkASSERT(heapEntry != NULL);
+ heapEntry->addReferences(fOwnerCount);
+ }
+ }
+ fDeferAddingOwners = false;
+ fDeferredEntries.reset();
+}