aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-12-09 22:29:30 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-12-09 22:29:30 +0000
commite4eb122a61d7c29f1dd979a41d90524fd249db3f (patch)
tree6897bcca0f1b470d435f70f54cdd8c04a2bc29c4 /src
parentf309dbcf2a8084afc44774a675c68756993acbc3 (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.cpp3
-rw-r--r--src/core/SkScaledImageCache.cpp167
-rw-r--r--src/core/SkScaledImageCache.h34
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