ResourceCache.cpp revision 0e89e2b7bcb2c035e8cee77f93120e7c5617f8d2
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include <SkPixelRef.h>
20#include "ResourceCache.h"
21#include "Caches.h"
22
23namespace android {
24namespace uirenderer {
25
26///////////////////////////////////////////////////////////////////////////////
27// Resource cache
28///////////////////////////////////////////////////////////////////////////////
29
30void ResourceCache::logCache() {
31    ALOGD("ResourceCache: cacheReport:");
32    for (size_t i = 0; i < mCache->size(); ++i) {
33        ResourceReference* ref = mCache->valueAt(i);
34        ALOGD("  ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p",
35                i, mCache->keyAt(i), mCache->valueAt(i));
36        ALOGD("  ResourceCache: mCache(%zu): refCount, recycled, destroyed, type = %d, %d, %d, %d",
37                i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
38    }
39}
40
41ResourceCache::ResourceCache() {
42    Mutex::Autolock _l(mLock);
43    mCache = new KeyedVector<const void*, ResourceReference*>();
44}
45
46ResourceCache::~ResourceCache() {
47    Mutex::Autolock _l(mLock);
48    delete mCache;
49}
50
51void ResourceCache::lock() {
52    mLock.lock();
53}
54
55void ResourceCache::unlock() {
56    mLock.unlock();
57}
58
59void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
60    Mutex::Autolock _l(mLock);
61    incrementRefcountLocked(resource, resourceType);
62}
63
64void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) {
65    bitmapResource->pixelRef()->globalRef();
66    SkSafeRef(bitmapResource->getColorTable());
67    incrementRefcount((void*) bitmapResource, kBitmap);
68}
69
70void ResourceCache::incrementRefcount(const SkPath* pathResource) {
71    incrementRefcount((void*) pathResource, kPath);
72}
73
74void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
75    incrementRefcount((void*) patchResource, kNinePatch);
76}
77
78void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
79    ssize_t index = mCache->indexOfKey(resource);
80    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
81    if (ref == NULL || mCache->size() == 0) {
82        ref = new ResourceReference(resourceType);
83        mCache->add(resource, ref);
84    }
85    ref->refCount++;
86}
87
88void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) {
89    bitmapResource->pixelRef()->globalRef();
90    SkSafeRef(bitmapResource->getColorTable());
91    incrementRefcountLocked((void*) bitmapResource, kBitmap);
92}
93
94void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) {
95    incrementRefcountLocked((void*) pathResource, kPath);
96}
97
98void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) {
99    incrementRefcountLocked((void*) patchResource, kNinePatch);
100}
101
102void ResourceCache::decrementRefcount(void* resource) {
103    Mutex::Autolock _l(mLock);
104    decrementRefcountLocked(resource);
105}
106
107void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) {
108    bitmapResource->pixelRef()->globalUnref();
109    SkSafeUnref(bitmapResource->getColorTable());
110    decrementRefcount((void*) bitmapResource);
111}
112
113void ResourceCache::decrementRefcount(const SkPath* pathResource) {
114    decrementRefcount((void*) pathResource);
115}
116
117void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
118    decrementRefcount((void*) patchResource);
119}
120
121void ResourceCache::decrementRefcountLocked(void* resource) {
122    ssize_t index = mCache->indexOfKey(resource);
123    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
124    if (ref == NULL) {
125        // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
126        return;
127    }
128    ref->refCount--;
129    if (ref->refCount == 0) {
130        deleteResourceReferenceLocked(resource, ref);
131    }
132}
133
134void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
135    bitmapResource->pixelRef()->globalUnref();
136    SkSafeUnref(bitmapResource->getColorTable());
137    decrementRefcountLocked((void*) bitmapResource);
138}
139
140void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) {
141    decrementRefcountLocked((void*) pathResource);
142}
143
144void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
145    decrementRefcountLocked((void*) patchResource);
146}
147
148void ResourceCache::destructor(SkPath* resource) {
149    Mutex::Autolock _l(mLock);
150    destructorLocked(resource);
151}
152
153void ResourceCache::destructorLocked(SkPath* resource) {
154    ssize_t index = mCache->indexOfKey(resource);
155    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
156    if (ref == NULL) {
157        // If we're not tracking this resource, just delete it
158        if (Caches::hasInstance()) {
159            Caches::getInstance().pathCache.removeDeferred(resource);
160        } else {
161            delete resource;
162        }
163        return;
164    }
165    ref->destroyed = true;
166    if (ref->refCount == 0) {
167        deleteResourceReferenceLocked(resource, ref);
168    }
169}
170
171void ResourceCache::destructor(const SkBitmap* resource) {
172    Mutex::Autolock _l(mLock);
173    destructorLocked(resource);
174}
175
176void ResourceCache::destructorLocked(const SkBitmap* resource) {
177    ssize_t index = mCache->indexOfKey(resource);
178    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
179    if (ref == NULL) {
180        // If we're not tracking this resource, just delete it
181        if (Caches::hasInstance()) {
182            Caches::getInstance().textureCache.removeDeferred(resource);
183        } else {
184            delete resource;
185        }
186        return;
187    }
188    ref->destroyed = true;
189    if (ref->refCount == 0) {
190        deleteResourceReferenceLocked(resource, ref);
191    }
192}
193
194void ResourceCache::destructor(Res_png_9patch* resource) {
195    Mutex::Autolock _l(mLock);
196    destructorLocked(resource);
197}
198
199void ResourceCache::destructorLocked(Res_png_9patch* resource) {
200    ssize_t index = mCache->indexOfKey(resource);
201    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
202    if (ref == NULL) {
203        // If we're not tracking this resource, just delete it
204        if (Caches::hasInstance()) {
205            Caches::getInstance().patchCache.removeDeferred(resource);
206        } else {
207            // A Res_png_9patch is actually an array of byte that's larger
208            // than sizeof(Res_png_9patch). It must be freed as an array.
209            delete[] (int8_t*) resource;
210        }
211        return;
212    }
213    ref->destroyed = true;
214    if (ref->refCount == 0) {
215        deleteResourceReferenceLocked(resource, ref);
216    }
217}
218
219/**
220 * Return value indicates whether resource was actually recycled, which happens when RefCnt
221 * reaches 0.
222 */
223bool ResourceCache::recycle(SkBitmap* resource) {
224    Mutex::Autolock _l(mLock);
225    return recycleLocked(resource);
226}
227
228/**
229 * Return value indicates whether resource was actually recycled, which happens when RefCnt
230 * reaches 0.
231 */
232bool ResourceCache::recycleLocked(SkBitmap* resource) {
233    ssize_t index = mCache->indexOfKey(resource);
234    if (index < 0) {
235        // not tracking this resource; just recycle the pixel data
236        resource->setPixels(NULL, NULL);
237        return true;
238    }
239    ResourceReference* ref = mCache->valueAt(index);
240    if (ref == NULL) {
241        // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
242        return true;
243    }
244    ref->recycled = true;
245    if (ref->refCount == 0) {
246        deleteResourceReferenceLocked(resource, ref);
247        return true;
248    }
249    // Still referring to resource, don't recycle yet
250    return false;
251}
252
253/**
254 * This method should only be called while the mLock mutex is held (that mutex is grabbed
255 * by the various destructor() and recycle() methods which call this method).
256 */
257void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
258    if (ref->recycled && ref->resourceType == kBitmap) {
259        ((SkBitmap*) resource)->setPixels(NULL, NULL);
260    }
261    if (ref->destroyed) {
262        switch (ref->resourceType) {
263            case kBitmap: {
264                SkBitmap* bitmap = (SkBitmap*) resource;
265                if (Caches::hasInstance()) {
266                    Caches::getInstance().textureCache.removeDeferred(bitmap);
267                } else {
268                    delete bitmap;
269                }
270            }
271            break;
272            case kPath: {
273                SkPath* path = (SkPath*) resource;
274                if (Caches::hasInstance()) {
275                    Caches::getInstance().pathCache.removeDeferred(path);
276                } else {
277                    delete path;
278                }
279            }
280            break;
281            case kNinePatch: {
282                if (Caches::hasInstance()) {
283                    Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
284                } else {
285                    // A Res_png_9patch is actually an array of byte that's larger
286                    // than sizeof(Res_png_9patch). It must be freed as an array.
287                    int8_t* patch = (int8_t*) resource;
288                    delete[] patch;
289                }
290            }
291            break;
292        }
293    }
294    mCache->removeItem(resource);
295    delete ref;
296}
297
298}; // namespace uirenderer
299}; // namespace android
300