/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkBitmapHeap_DEFINED #define SkBitmapHeap_DEFINED #include "SkBitmap.h" #include "SkFlattenable.h" #include "SkRefCnt.h" #include "SkTDArray.h" #include "SkThread.h" /** * SkBitmapHeapEntry provides users of SkBitmapHeap (using internal storage) with a means to... * (1) get access a bitmap in the heap * (2) indicate they are done with bitmap by releasing their reference (if they were an owner). */ class SkBitmapHeapEntry : SkNoncopyable { public: ~SkBitmapHeapEntry(); int32_t getSlot() { return fSlot; } SkBitmap* getBitmap() { return &fBitmap; } void releaseRef() { sk_atomic_dec(&fRefCount); } private: SkBitmapHeapEntry(); void addReferences(int count); int32_t fSlot; int32_t fRefCount; SkBitmap fBitmap; // Keep track of the bytes allocated for this bitmap. When replacing the // bitmap or removing this HeapEntry we know how much memory has been // reclaimed. size_t fBytesAllocated; friend class SkBitmapHeap; friend class SkBitmapHeapTester; }; class SkBitmapHeapReader : public SkRefCnt { public: SK_DECLARE_INST_COUNT(SkBitmapHeapReader) SkBitmapHeapReader() : INHERITED() {} virtual SkBitmap* getBitmap(int32_t slot) const = 0; virtual void releaseRef(int32_t slot) = 0; private: typedef SkRefCnt INHERITED; }; /** * TODO: stores immutable bitmaps into a heap */ class SkBitmapHeap : public SkBitmapHeapReader { public: class ExternalStorage : public SkRefCnt { public: SK_DECLARE_INST_COUNT(ExternalStorage) virtual bool insert(const SkBitmap& bitmap, int32_t slot) = 0; private: typedef SkRefCnt INHERITED; }; static const int32_t UNLIMITED_SIZE = -1; static const int32_t IGNORE_OWNERS = -1; static const int32_t INVALID_SLOT = -1; /** * Constructs a heap that is responsible for allocating and managing its own storage. In the * case where we choose to allow the heap to grow indefinitely (i.e. UNLIMITED_SIZE) we * guarantee that once allocated in the heap a bitmap's index in the heap is immutable. * Otherwise we guarantee the bitmaps placement in the heap until its owner count goes to zero. * * @param preferredSize Specifies the preferred maximum number of bitmaps to store. This is * not a hard limit as it can grow larger if the number of bitmaps in the heap with active * owners exceeds this limit. * @param ownerCount The number of owners to assign to each inserted bitmap. NOTE: while a * bitmap in the heap has a least one owner it can't be removed. */ SkBitmapHeap(int32_t preferredSize = UNLIMITED_SIZE, int32_t ownerCount = IGNORE_OWNERS); /** * Constructs a heap that defers the responsibility of storing the bitmaps to an external * function. This is especially useful if the bitmaps will be used in a separate process as the * external storage can ensure the data is properly shuttled to the appropriate processes. * * Our LRU implementation assumes that inserts into the external storage are consumed in the * order that they are inserted (i.e. SkPipe). This ensures that we don't need to query the * external storage to see if a slot in the heap is eligible to be overwritten. * * @param externalStorage The class responsible for storing the bitmaps inserted into the heap * @param heapSize The maximum size of the heap. Because of the sequential limitation imposed * by our LRU implementation we can guarantee that the heap will never grow beyond this size. */ SkBitmapHeap(ExternalStorage* externalStorage, int32_t heapSize = UNLIMITED_SIZE); virtual ~SkBitmapHeap(); /** * Retrieves the bitmap from the specified slot in the heap * * @return The bitmap located at that slot or NULL if external storage is being used. */ SkBitmap* getBitmap(int32_t slot) const override { SkASSERT(fExternalStorage == NULL); SkBitmapHeapEntry* entry = getEntry(slot); if (entry) { return &entry->fBitmap; } return NULL; } /** * Retrieves the bitmap from the specified slot in the heap * * @return The bitmap located at that slot or NULL if external storage is being used. */ void releaseRef(int32_t slot) override { SkASSERT(fExternalStorage == NULL); if (fOwnerCount != IGNORE_OWNERS) { SkBitmapHeapEntry* entry = getEntry(slot); if (entry) { entry->releaseRef(); } } } /** * Inserts a bitmap into the heap. The stored version of bitmap is guaranteed to be immutable * and is not dependent on the lifecycle of the provided bitmap. * * @param bitmap the bitmap to be inserted into the heap * @return the slot in the heap where the bitmap is stored or INVALID_SLOT if the bitmap could * not be added to the heap. If it was added the slot will remain valid... * (1) indefinitely if no owner count has been specified. * (2) until all owners have called releaseRef on the appropriate SkBitmapHeapEntry* */ int32_t insert(const SkBitmap& bitmap); /** * Retrieves an entry from the heap at a given slot. * * @param slot the slot in the heap where a bitmap was stored. * @return a SkBitmapHeapEntry that wraps the bitmap or NULL if external storage is used. */ SkBitmapHeapEntry* getEntry(int32_t slot) const { SkASSERT(slot <= fStorage.count()); if (fExternalStorage != NULL) { return NULL; } return fStorage[slot]; } /** * Returns a count of the number of items currently in the heap */ int count() const { SkASSERT(fExternalStorage != NULL || fStorage.count() - fUnusedSlots.count() == fLookupTable.count()); return fLookupTable.count(); } /** * Returns the total number of bytes allocated by the bitmaps in the heap */ size_t bytesAllocated() const { return fBytesAllocated; } /** * Attempt to reduce the storage allocated. * @param bytesToFree minimum number of bytes that should be attempted to * be freed. * @return number of bytes actually freed. */ size_t freeMemoryIfPossible(size_t bytesToFree); /** * Defer any increments of owner counts until endAddingOwnersDeferral is called. So if an * existing SkBitmap is inserted into the SkBitmapHeap, its corresponding SkBitmapHeapEntry will * not have addReferences called on it, and the client does not need to make a corresponding * call to releaseRef. Only meaningful if this SkBitmapHeap was created with an owner count not * equal to IGNORE_OWNERS. */ void deferAddingOwners(); /** * Resume adding references when duplicate SkBitmaps are inserted. * @param add If true, add references to the SkBitmapHeapEntrys whose SkBitmaps were re-inserted * while deferring. */ void endAddingOwnersDeferral(bool add); private: struct LookupEntry { LookupEntry(const SkBitmap& bm) : fGenerationId(bm.getGenerationID()) , fPixelOrigin(bm.pixelRefOrigin()) , fWidth(bm.width()) , fHeight(bm.height()) , fMoreRecentlyUsed(NULL) , fLessRecentlyUsed(NULL){} const uint32_t fGenerationId; // SkPixelRef GenerationID. const SkIPoint fPixelOrigin; const uint32_t fWidth; const uint32_t fHeight; // TODO: Generalize the LRU caching mechanism LookupEntry* fMoreRecentlyUsed; LookupEntry* fLessRecentlyUsed; uint32_t fStorageSlot; // slot of corresponding bitmap in fStorage. /** * Compare two LookupEntry pointers for sorting and searching. */ static bool Less(const LookupEntry& a, const LookupEntry& b); }; /** * Remove the entry from the lookup table. Also deletes the entry pointed * to by the table. Therefore, if a pointer to that one was passed in, the * pointer should no longer be used, since the object to which it points has * been deleted. * @return The index in the lookup table of the entry before removal. */ int removeEntryFromLookupTable(LookupEntry*); /** * Searches for the bitmap in the lookup table and returns the bitmaps index within the table. * If the bitmap was not already in the table it is added. * * @param key The key to search the lookup table, created from a bitmap. * @param entry A pointer to a SkBitmapHeapEntry* that if non-null AND the bitmap is found * in the lookup table is populated with the entry from the heap storage. */ int findInLookupTable(const LookupEntry& key, SkBitmapHeapEntry** entry); LookupEntry* findEntryToReplace(const SkBitmap& replacement); bool copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap); /** * Remove a LookupEntry from the LRU, in preparation for either deleting or appending as most * recent. Points the LookupEntry's old neighbors at each other, and sets fLeastRecentlyUsed * (if there is still an entry left). Sets LookupEntry's fMoreRecentlyUsed to NULL and leaves * its fLessRecentlyUsed unmodified. */ void removeFromLRU(LookupEntry* entry); /** * Append a LookupEntry to the end of the LRU cache, marking it as the most * recently used. Assumes that the LookupEntry is already in fLookupTable, * but is not in the LRU cache. If it is in the cache, removeFromLRU should * be called first. */ void appendToLRU(LookupEntry*); // searchable index that maps to entries in the heap SkTDArray<LookupEntry*> fLookupTable; // heap storage SkTDArray<SkBitmapHeapEntry*> fStorage; // Used to mark slots in fStorage as deleted without actually deleting // the slot so as not to mess up the numbering. SkTDArray<int> fUnusedSlots; ExternalStorage* fExternalStorage; LookupEntry* fMostRecentlyUsed; LookupEntry* fLeastRecentlyUsed; const int32_t fPreferredCount; const int32_t fOwnerCount; size_t fBytesAllocated; bool fDeferAddingOwners; SkTDArray<int> fDeferredEntries; typedef SkBitmapHeapReader INHERITED; }; #endif // SkBitmapHeap_DEFINED