1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SkImageRefPool.h"
9#include "SkImageRef.h"
10#include "SkThread.h"
11
12SkImageRefPool::SkImageRefPool() {
13    fRAMBudget = 0; // means no explicit limit
14    fRAMUsed = 0;
15    fCount = 0;
16    fHead = fTail = NULL;
17}
18
19SkImageRefPool::~SkImageRefPool() {
20    //    SkASSERT(NULL == fHead);
21}
22
23void SkImageRefPool::setRAMBudget(size_t size) {
24    if (fRAMBudget != size) {
25        fRAMBudget = size;
26        this->purgeIfNeeded();
27    }
28}
29
30void SkImageRefPool::justAddedPixels(SkImageRef* ref) {
31#ifdef DUMP_IMAGEREF_LIFECYCLE
32    SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n",
33             ref->getURI(),
34             ref->fBitmap.width(), ref->fBitmap.height(),
35             ref->fBitmap.bytesPerPixel(),
36             ref->fBitmap.getSize(), (int)fRAMUsed);
37#endif
38    fRAMUsed += ref->ramUsed();
39    this->purgeIfNeeded();
40}
41
42void SkImageRefPool::canLosePixels(SkImageRef* ref) {
43    // the refs near fHead have recently been released (used)
44    // if we purge, we purge from the tail
45    this->detach(ref);
46    this->addToHead(ref);
47    this->purgeIfNeeded();
48}
49
50void SkImageRefPool::purgeIfNeeded() {
51    // do nothing if we have a zero-budget (i.e. unlimited)
52    if (fRAMBudget != 0) {
53        this->setRAMUsed(fRAMBudget);
54    }
55}
56
57void SkImageRefPool::setRAMUsed(size_t limit) {
58    SkImageRef* ref = fTail;
59
60    while (NULL != ref && fRAMUsed > limit) {
61        // only purge it if its pixels are unlocked
62        if (!ref->isLocked() && ref->fBitmap.getPixels()) {
63            size_t size = ref->ramUsed();
64            SkASSERT(size <= fRAMUsed);
65            fRAMUsed -= size;
66
67#ifdef DUMP_IMAGEREF_LIFECYCLE
68            SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n",
69                     ref->getURI(),
70                     ref->fBitmap.width(), ref->fBitmap.height(),
71                     ref->fBitmap.bytesPerPixel(),
72                     (int)size, (int)fRAMUsed);
73#endif
74
75            // remember the bitmap config (don't call reset),
76            // just clear the pixel memory
77            ref->fBitmap.setPixels(NULL);
78            SkASSERT(NULL == ref->fBitmap.getPixels());
79        }
80        ref = ref->fPrev;
81    }
82}
83
84///////////////////////////////////////////////////////////////////////////////
85
86void SkImageRefPool::addToHead(SkImageRef* ref) {
87    ref->fNext = fHead;
88    ref->fPrev = NULL;
89
90    if (fHead) {
91        SkASSERT(NULL == fHead->fPrev);
92        fHead->fPrev = ref;
93    }
94    fHead = ref;
95
96    if (NULL == fTail) {
97        fTail = ref;
98    }
99    fCount += 1;
100    SkASSERT(computeCount() == fCount);
101
102    fRAMUsed += ref->ramUsed();
103}
104
105void SkImageRefPool::addToTail(SkImageRef* ref) {
106    ref->fNext = NULL;
107    ref->fPrev = fTail;
108
109    if (fTail) {
110        SkASSERT(NULL == fTail->fNext);
111        fTail->fNext = ref;
112    }
113    fTail = ref;
114
115    if (NULL == fHead) {
116        fHead = ref;
117    }
118    fCount += 1;
119    SkASSERT(computeCount() == fCount);
120
121    fRAMUsed += ref->ramUsed();
122}
123
124void SkImageRefPool::detach(SkImageRef* ref) {
125    SkASSERT(fCount > 0);
126
127    if (fHead == ref) {
128        fHead = ref->fNext;
129    }
130    if (fTail == ref) {
131        fTail = ref->fPrev;
132    }
133    if (ref->fPrev) {
134        ref->fPrev->fNext = ref->fNext;
135    }
136    if (ref->fNext) {
137        ref->fNext->fPrev = ref->fPrev;
138    }
139
140    ref->fNext = ref->fPrev = NULL;
141
142    fCount -= 1;
143    SkASSERT(computeCount() == fCount);
144
145    SkASSERT(fRAMUsed >= ref->ramUsed());
146    fRAMUsed -= ref->ramUsed();
147}
148
149int SkImageRefPool::computeCount() const {
150    SkImageRef* ref = fHead;
151    int count = 0;
152
153    while (ref != NULL) {
154        count += 1;
155        ref = ref->fNext;
156    }
157
158#ifdef SK_DEBUG
159    ref = fTail;
160    int count2 = 0;
161
162    while (ref != NULL) {
163        count2 += 1;
164        ref = ref->fPrev;
165    }
166    SkASSERT(count2 == count);
167#endif
168
169    return count;
170}
171
172///////////////////////////////////////////////////////////////////////////////
173
174#include "SkStream.h"
175
176void SkImageRefPool::dump() const {
177#if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE)
178    SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n",
179             (int)fRAMBudget, (int)fRAMUsed, fCount);
180
181    SkImageRef* ref = fHead;
182
183    while (ref != NULL) {
184        SkDebugf("  [%3d %3d %d] ram=%d data=%d locked=%d %s\n", ref->fBitmap.width(),
185                 ref->fBitmap.height(), ref->fBitmap.config(),
186                 ref->ramUsed(), (int)ref->fStream->getLength(),
187                 ref->isLocked(), ref->getURI());
188
189        ref = ref->fNext;
190    }
191#endif
192}
193