aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/lazy/SkLruImageCache.cpp
diff options
context:
space:
mode:
authorGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-02-22 21:38:35 +0000
committerGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-02-22 21:38:35 +0000
commitf8d7d2731318cdf510ab68e6b3f5ec68ab22c8e2 (patch)
treeb62a29fabe0b0af3f8f17ffbe8d607508c1d2c00 /src/lazy/SkLruImageCache.cpp
parent5c90e291425b2788f47679266d9584845ceefc2e (diff)
Create SkLazyPixelRef which performs lazy decoding.
The new pixel ref behaves similarly to SkImageRef, with some key differences: It does not depend on the images project. It requires an SkImageCache, which handles allocation and caching of the pixel memory. It takes a function signature for decoding which decodes into already allocated pixel memory rather than into an SkBitmap. Add two implementations of SkImageCache: SkLruImageCache and SkAshmemImageCache. Replace SkSerializationHelpers::DecodeBitmap with SkPicture::InstallPixelRefProc, and update sites that referenced it. SkBitmapFactory now sets the pixel ref to a new object of the new class SkLazyPixelRef, provided it has an SkImageCache for caching. Provide an option to do lazy decodes in render_pictures and bench_pictures. SkPicture: Eliminate the default parameters in the constructor. If a proc for decoding bitmaps is installed, use it to decode any encoded data in subpictures. When parsing deserializing subpictures, check for success. When serializing subpictures, pass the picture's bitmap encoder to the subpicture's call to serialize. Update BitmapFactoryTest to test its new behavior. BUG=https://code.google.com/p/skia/issues/detail?id=1008 BUG=https://code.google.com/p/skia/issues/detail?id=1009 Review URL: https://codereview.appspot.com/7060052 git-svn-id: http://skia.googlecode.com/svn/trunk@7835 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/lazy/SkLruImageCache.cpp')
-rw-r--r--src/lazy/SkLruImageCache.cpp189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/lazy/SkLruImageCache.cpp b/src/lazy/SkLruImageCache.cpp
new file mode 100644
index 0000000000..e8a3ed0a3d
--- /dev/null
+++ b/src/lazy/SkLruImageCache.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkLruImageCache.h"
+
+static intptr_t NextGenerationID() {
+ static intptr_t gNextID;
+ do {
+ gNextID++;
+ } while (SkImageCache::UNINITIALIZED_ID == gNextID);
+ return gNextID;
+}
+
+class CachedPixels : public SkNoncopyable {
+
+public:
+ CachedPixels(size_t length)
+ : fLength(length)
+ , fID(NextGenerationID())
+ , fLocked(false) {
+ fAddr = sk_malloc_throw(length);
+ }
+
+ ~CachedPixels() {
+ sk_free(fAddr);
+ }
+
+ void* getData() { return fAddr; }
+
+ intptr_t getID() const { return fID; }
+
+ size_t getLength() const { return fLength; }
+
+ void lock() { SkASSERT(!fLocked); fLocked = true; }
+
+ void unlock() { SkASSERT(fLocked); fLocked = false; }
+
+ bool isLocked() const { return fLocked; }
+
+private:
+ void* fAddr;
+ size_t fLength;
+ const intptr_t fID;
+ bool fLocked;
+ SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels);
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+SkLruImageCache::SkLruImageCache(size_t budget)
+ : fRamBudget(budget)
+ , fRamUsed(0) {}
+
+SkLruImageCache::~SkLruImageCache() {
+ // Don't worry about updating pointers. All will be deleted.
+ Iter iter;
+ CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
+ while (pixels != NULL) {
+ CachedPixels* prev = iter.prev();
+ SkASSERT(!pixels->isLocked());
+#if SK_DEBUG
+ fRamUsed -= pixels->getLength();
+#endif
+ SkDELETE(pixels);
+ pixels = prev;
+ }
+#if SK_DEBUG
+ SkASSERT(fRamUsed == 0);
+#endif
+}
+
+#ifdef SK_DEBUG
+SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
+ SkAutoMutexAcquire ac(&fMutex);
+ CachedPixels* pixels = this->findByID(ID);
+ if (NULL == pixels) {
+ return SkImageCache::kThrownAway_CacheStatus;
+ }
+ if (pixels->isLocked()) {
+ return SkImageCache::kPinned_CacheStatus;
+ }
+ return SkImageCache::kUnpinned_CacheStatus;
+}
+#endif
+
+void SkLruImageCache::setBudget(size_t newBudget) {
+ SkAutoMutexAcquire ac(&fMutex);
+ fRamBudget = newBudget;
+ this->purgeIfNeeded();
+}
+
+void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
+ SkAutoMutexAcquire ac(&fMutex);
+ CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
+ if (ID != NULL) {
+ *ID = pixels->getID();
+ }
+ pixels->lock();
+ fRamUsed += bytes;
+ fLRU.addToHead(pixels);
+ this->purgeIfNeeded();
+ return pixels->getData();
+}
+
+void* SkLruImageCache::pinCache(intptr_t ID) {
+ SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
+ SkAutoMutexAcquire ac(&fMutex);
+ CachedPixels* pixels = this->findByID(ID);
+ if (NULL == pixels) {
+ return NULL;
+ }
+ if (pixels != fLRU.head()) {
+ fLRU.remove(pixels);
+ fLRU.addToHead(pixels);
+ }
+ pixels->lock();
+ return pixels->getData();
+}
+
+void SkLruImageCache::releaseCache(intptr_t ID) {
+ SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
+ SkAutoMutexAcquire ac(&fMutex);
+ CachedPixels* pixels = this->findByID(ID);
+ SkASSERT(pixels != NULL);
+ pixels->unlock();
+ this->purgeIfNeeded();
+}
+
+void SkLruImageCache::throwAwayCache(intptr_t ID) {
+ SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
+ SkAutoMutexAcquire ac(&fMutex);
+ CachedPixels* pixels = this->findByID(ID);
+ if (pixels != NULL) {
+ if (pixels->isLocked()) {
+ pixels->unlock();
+ }
+ this->removePixels(pixels);
+ }
+}
+
+void SkLruImageCache::removePixels(CachedPixels* pixels) {
+ // Mutex is already locked.
+ SkASSERT(!pixels->isLocked());
+ const size_t size = pixels->getLength();
+ SkASSERT(size <= fRamUsed);
+ fLRU.remove(pixels);
+ SkDELETE(pixels);
+ fRamUsed -= size;
+}
+
+CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
+ // Mutex is already locked.
+ Iter iter;
+ // Start from the head, most recently used.
+ CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
+ while (pixels != NULL) {
+ if (pixels->getID() == ID) {
+ return pixels;
+ }
+ pixels = iter.next();
+ }
+ return NULL;
+}
+
+void SkLruImageCache::purgeIfNeeded() {
+ // Mutex is already locked.
+ this->purgeTilAtOrBelow(fRamBudget);
+}
+
+void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
+ // Mutex is already locked.
+ if (fRamUsed > limit) {
+ Iter iter;
+ // Start from the tail, least recently used.
+ CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
+ while (pixels != NULL && fRamUsed > limit) {
+ CachedPixels* prev = iter.prev();
+ if (!pixels->isLocked()) {
+ this->removePixels(pixels);
+ }
+ pixels = prev;
+ }
+ }
+}
+