diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-12-09 22:29:30 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-12-09 22:29:30 +0000 |
commit | e4eb122a61d7c29f1dd979a41d90524fd249db3f (patch) | |
tree | 6897bcca0f1b470d435f70f54cdd8c04a2bc29c4 /src | |
parent | f309dbcf2a8084afc44774a675c68756993acbc3 (diff) |
support scaledimagecache instantiable using discardablememory
Add this to your build/gyp system to use discardable instead of malloc+budget
#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
R=halcanary@google.com
Review URL: https://codereview.chromium.org/105933003
git-svn-id: http://skia.googlecode.com/svn/trunk@12588 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkBitmapProcState.cpp | 3 | ||||
-rw-r--r-- | src/core/SkScaledImageCache.cpp | 167 | ||||
-rw-r--r-- | src/core/SkScaledImageCache.h | 34 |
3 files changed, 196 insertions, 8 deletions
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp index 59483d66a1..c93a2b5cd7 100644 --- a/src/core/SkBitmapProcState.cpp +++ b/src/core/SkBitmapProcState.cpp @@ -147,7 +147,8 @@ bool SkBitmapProcState::possiblyScaleImage() { SkBitmapScaler::RESIZE_BEST, dest_width, dest_height, - simd)) { + simd, + SkScaledImageCache::GetAllocator())) { // we failed to create fScaledBitmap, so just return and let // the scanline proc handle it. return false; diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp index eba20c4f90..25e29c2fb7 100644 --- a/src/core/SkScaledImageCache.cpp +++ b/src/core/SkScaledImageCache.cpp @@ -11,6 +11,13 @@ #include "SkPixelRef.h" #include "SkRect.h" +// This can be defined by the caller's build system +//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE + +#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT +# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024 +#endif + #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024) #endif @@ -164,7 +171,7 @@ static inline SkScaledImageCache::Rec* find_rec_in_list( } #endif -SkScaledImageCache::SkScaledImageCache(size_t byteLimit) { +void SkScaledImageCache::init() { fHead = NULL; fTail = NULL; #ifdef USE_HASH @@ -173,11 +180,133 @@ SkScaledImageCache::SkScaledImageCache(size_t byteLimit) { fHash = NULL; #endif fBytesUsed = 0; - fByteLimit = byteLimit; fCount = 0; + fAllocator = NULL; + + // One of these should be explicit set by the caller after we return. + fByteLimit = 0; + fDiscardableFactory = NULL; +} + +#include "SkDiscardableMemory.h" + +class SkOneShotDiscardablePixelRef : public SkPixelRef { +public: + // Ownership of the discardablememory is transfered to the pixelref + SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes); + ~SkOneShotDiscardablePixelRef(); + + SK_DECLARE_UNFLATTENABLE_OBJECT() + +protected: + virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE; + virtual void onUnlockPixels() SK_OVERRIDE; + virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE; + +private: + SkImageInfo fInfo; // remove when SkPixelRef gets this in baseclass + + SkDiscardableMemory* fDM; + size_t fRB; + bool fFirstTime; + + typedef SkPixelRef INHERITED; +}; + +SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info, + SkDiscardableMemory* dm, + size_t rowBytes) + : INHERITED(info) + , fDM(dm) + , fRB(rowBytes) +{ + fInfo = info; // remove this redundant field when SkPixelRef has info + + SkASSERT(dm->data()); + fFirstTime = true; +} + +SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() { + SkDELETE(fDM); +} + +void* SkOneShotDiscardablePixelRef::onLockPixels(SkColorTable** ctable) { + if (fFirstTime) { + // we're already locked + fFirstTime = false; + return fDM->data(); + } + return fDM->lock() ? fDM->data() : NULL; +} + +void SkOneShotDiscardablePixelRef::onUnlockPixels() { + SkASSERT(!fFirstTime); + fDM->unlock(); +} + +size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const { + return fInfo.fHeight * fRB; +} + +class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator { +public: + SkScaledImageCacheDiscardableAllocator( + SkScaledImageCache::DiscardableFactory factory) { + SkASSERT(factory); + fFactory = factory; + } + + virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE; + +private: + SkScaledImageCache::DiscardableFactory fFactory; +}; + +bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, + SkColorTable* ctable) { + size_t size = bitmap->getSize(); + if (0 == size) { + return false; + } + + SkDiscardableMemory* dm = fFactory(size); + if (NULL == dm) { + return false; + } + + // can relax when we have bitmap::asImageInfo + if (SkBitmap::kARGB_8888_Config != bitmap->config()) { + return false; + } + + SkImageInfo info = { + bitmap->width(), + bitmap->height(), + kPMColor_SkColorType, + bitmap->alphaType() + }; + + bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef, + (info, dm, bitmap->rowBytes())))->unref(); + bitmap->lockPixels(); + return bitmap->readyToDraw(); +} + +SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) { + this->init(); + fDiscardableFactory = factory; + + fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory)); +} + +SkScaledImageCache::SkScaledImageCache(size_t byteLimit) { + this->init(); + fByteLimit = byteLimit; } SkScaledImageCache::~SkScaledImageCache() { + SkSafeUnref(fAllocator); + Rec* rec = fHead; while (rec) { Rec* next = rec->fNext; @@ -367,30 +496,45 @@ void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) { } void SkScaledImageCache::purgeAsNeeded() { - size_t byteLimit = fByteLimit; - size_t bytesUsed = fBytesUsed; + size_t byteLimit; + int countLimit; + + if (fDiscardableFactory) { + countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT; + byteLimit = SK_MaxU32; // no limit based on bytes + } else { + countLimit = SK_MaxS32; // no limit based on count + byteLimit = fByteLimit; + } + size_t bytesUsed = fBytesUsed; + int countUsed = fCount; + Rec* rec = fTail; while (rec) { - if (bytesUsed < byteLimit) { + if (bytesUsed < byteLimit && countUsed < countLimit) { break; } + Rec* prev = rec->fPrev; if (0 == rec->fLockCount) { size_t used = rec->bytesUsed(); SkASSERT(used <= bytesUsed); - bytesUsed -= used; this->detach(rec); #ifdef USE_HASH fHash->remove(rec->fKey); #endif SkDELETE(rec); - fCount -= 1; + + bytesUsed -= used; + countUsed -= 1; } rec = prev; } + fBytesUsed = bytesUsed; + fCount = countUsed; } size_t SkScaledImageCache::setByteLimit(size_t newLimit) { @@ -513,7 +657,11 @@ void SkScaledImageCache::validate() const { SK_DECLARE_STATIC_MUTEX(gMutex); static void create_cache(SkScaledImageCache** cache) { +#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE + *cache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create)); +#else *cache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT)); +#endif } static SkScaledImageCache* get_cache() { @@ -592,6 +740,11 @@ size_t SkScaledImageCache::SetByteLimit(size_t newLimit) { return get_cache()->setByteLimit(newLimit); } +SkBitmap::Allocator* SkScaledImageCache::GetAllocator() { + SkAutoMutexAcquire am(gMutex); + return get_cache()->allocator(); +} + /////////////////////////////////////////////////////////////////////////////// #include "SkGraphics.h" diff --git a/src/core/SkScaledImageCache.h b/src/core/SkScaledImageCache.h index 44ef1f8a2c..311db325be 100644 --- a/src/core/SkScaledImageCache.h +++ b/src/core/SkScaledImageCache.h @@ -10,6 +10,7 @@ #include "SkBitmap.h" +class SkDiscardableMemory; class SkMipMap; /** @@ -26,6 +27,12 @@ class SkScaledImageCache { public: struct ID; + /** + * Returns a locked/pinned SkDiscardableMemory instance for the specified + * number of bytes, or NULL on failure. + */ + typedef SkDiscardableMemory* (*DiscardableFactory)(size_t bytes); + /* * The following static methods are thread-safe wrappers around a global * instance of this cache. @@ -57,9 +64,27 @@ public: static size_t GetByteLimit(); static size_t SetByteLimit(size_t newLimit); + static SkBitmap::Allocator* GetAllocator(); + /////////////////////////////////////////////////////////////////////////// + /** + * Construct the cache to call DiscardableFactory when it + * allocates memory for the pixels. In this mode, the cache has + * not explicit budget, and so methods like getBytesUsed() and + * getByteLimit() will return 0, and setByteLimit will ignore its argument + * and return 0. + */ + SkScaledImageCache(DiscardableFactory); + + /** + * Construct the cache, allocating memory with malloc, and respect the + * byteLimit, purging automatically when a new image is added to the cache + * that pushes the total bytesUsed over the limit. Note: The limit can be + * changed at runtime with setByteLimit. + */ SkScaledImageCache(size_t byteLimit); + ~SkScaledImageCache(); /** @@ -124,6 +149,8 @@ public: */ size_t setByteLimit(size_t newLimit); + SkBitmap::Allocator* allocator() const { return fAllocator; }; + public: struct Rec; struct Key; @@ -134,6 +161,10 @@ private: class Hash; Hash* fHash; + DiscardableFactory fDiscardableFactory; + // the allocator is NULL or one that matches discardables + SkBitmap::Allocator* fAllocator; + size_t fBytesUsed; size_t fByteLimit; int fCount; @@ -149,6 +180,9 @@ private: void moveToHead(Rec*); void addToHead(Rec*); void detach(Rec*); + + void init(); // called by constructors + #ifdef SK_DEBUG void validate() const; #else |