michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: #include "SkImageRefPool.h" michael@0: #include "SkImageRef.h" michael@0: #include "SkThread.h" michael@0: michael@0: SkImageRefPool::SkImageRefPool() { michael@0: fRAMBudget = 0; // means no explicit limit michael@0: fRAMUsed = 0; michael@0: fCount = 0; michael@0: fHead = fTail = NULL; michael@0: } michael@0: michael@0: SkImageRefPool::~SkImageRefPool() { michael@0: // SkASSERT(NULL == fHead); michael@0: } michael@0: michael@0: void SkImageRefPool::setRAMBudget(size_t size) { michael@0: if (fRAMBudget != size) { michael@0: fRAMBudget = size; michael@0: this->purgeIfNeeded(); michael@0: } michael@0: } michael@0: michael@0: void SkImageRefPool::justAddedPixels(SkImageRef* ref) { michael@0: #ifdef DUMP_IMAGEREF_LIFECYCLE michael@0: SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n", michael@0: ref->getURI(), michael@0: ref->fBitmap.width(), ref->fBitmap.height(), michael@0: ref->fBitmap.bytesPerPixel(), michael@0: ref->fBitmap.getSize(), (int)fRAMUsed); michael@0: #endif michael@0: fRAMUsed += ref->ramUsed(); michael@0: this->purgeIfNeeded(); michael@0: } michael@0: michael@0: void SkImageRefPool::canLosePixels(SkImageRef* ref) { michael@0: // the refs near fHead have recently been released (used) michael@0: // if we purge, we purge from the tail michael@0: this->detach(ref); michael@0: this->addToHead(ref); michael@0: this->purgeIfNeeded(); michael@0: } michael@0: michael@0: void SkImageRefPool::purgeIfNeeded() { michael@0: // do nothing if we have a zero-budget (i.e. unlimited) michael@0: if (fRAMBudget != 0) { michael@0: this->setRAMUsed(fRAMBudget); michael@0: } michael@0: } michael@0: michael@0: void SkImageRefPool::setRAMUsed(size_t limit) { michael@0: SkImageRef* ref = fTail; michael@0: michael@0: while (NULL != ref && fRAMUsed > limit) { michael@0: // only purge it if its pixels are unlocked michael@0: if (!ref->isLocked() && ref->fBitmap.getPixels()) { michael@0: size_t size = ref->ramUsed(); michael@0: SkASSERT(size <= fRAMUsed); michael@0: fRAMUsed -= size; michael@0: michael@0: #ifdef DUMP_IMAGEREF_LIFECYCLE michael@0: SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n", michael@0: ref->getURI(), michael@0: ref->fBitmap.width(), ref->fBitmap.height(), michael@0: ref->fBitmap.bytesPerPixel(), michael@0: (int)size, (int)fRAMUsed); michael@0: #endif michael@0: michael@0: // remember the bitmap config (don't call reset), michael@0: // just clear the pixel memory michael@0: ref->fBitmap.setPixels(NULL); michael@0: SkASSERT(NULL == ref->fBitmap.getPixels()); michael@0: } michael@0: ref = ref->fPrev; michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkImageRefPool::addToHead(SkImageRef* ref) { michael@0: ref->fNext = fHead; michael@0: ref->fPrev = NULL; michael@0: michael@0: if (fHead) { michael@0: SkASSERT(NULL == fHead->fPrev); michael@0: fHead->fPrev = ref; michael@0: } michael@0: fHead = ref; michael@0: michael@0: if (NULL == fTail) { michael@0: fTail = ref; michael@0: } michael@0: fCount += 1; michael@0: SkASSERT(computeCount() == fCount); michael@0: michael@0: fRAMUsed += ref->ramUsed(); michael@0: } michael@0: michael@0: void SkImageRefPool::addToTail(SkImageRef* ref) { michael@0: ref->fNext = NULL; michael@0: ref->fPrev = fTail; michael@0: michael@0: if (fTail) { michael@0: SkASSERT(NULL == fTail->fNext); michael@0: fTail->fNext = ref; michael@0: } michael@0: fTail = ref; michael@0: michael@0: if (NULL == fHead) { michael@0: fHead = ref; michael@0: } michael@0: fCount += 1; michael@0: SkASSERT(computeCount() == fCount); michael@0: michael@0: fRAMUsed += ref->ramUsed(); michael@0: } michael@0: michael@0: void SkImageRefPool::detach(SkImageRef* ref) { michael@0: SkASSERT(fCount > 0); michael@0: michael@0: if (fHead == ref) { michael@0: fHead = ref->fNext; michael@0: } michael@0: if (fTail == ref) { michael@0: fTail = ref->fPrev; michael@0: } michael@0: if (ref->fPrev) { michael@0: ref->fPrev->fNext = ref->fNext; michael@0: } michael@0: if (ref->fNext) { michael@0: ref->fNext->fPrev = ref->fPrev; michael@0: } michael@0: michael@0: ref->fNext = ref->fPrev = NULL; michael@0: michael@0: fCount -= 1; michael@0: SkASSERT(computeCount() == fCount); michael@0: michael@0: SkASSERT(fRAMUsed >= ref->ramUsed()); michael@0: fRAMUsed -= ref->ramUsed(); michael@0: } michael@0: michael@0: int SkImageRefPool::computeCount() const { michael@0: SkImageRef* ref = fHead; michael@0: int count = 0; michael@0: michael@0: while (ref != NULL) { michael@0: count += 1; michael@0: ref = ref->fNext; michael@0: } michael@0: michael@0: #ifdef SK_DEBUG michael@0: ref = fTail; michael@0: int count2 = 0; michael@0: michael@0: while (ref != NULL) { michael@0: count2 += 1; michael@0: ref = ref->fPrev; michael@0: } michael@0: SkASSERT(count2 == count); michael@0: #endif michael@0: michael@0: return count; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkStream.h" michael@0: michael@0: void SkImageRefPool::dump() const { michael@0: #if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE) michael@0: SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n", michael@0: (int)fRAMBudget, (int)fRAMUsed, fCount); michael@0: michael@0: SkImageRef* ref = fHead; michael@0: michael@0: while (ref != NULL) { michael@0: SkDebugf(" [%3d %3d %d] ram=%d data=%d locked=%d %s\n", ref->fBitmap.width(), michael@0: ref->fBitmap.height(), ref->fBitmap.config(), michael@0: ref->ramUsed(), (int)ref->fStream->getLength(), michael@0: ref->isLocked(), ref->getURI()); michael@0: michael@0: ref = ref->fNext; michael@0: } michael@0: #endif michael@0: }