ResourceCache.cpp revision d41c4d8c732095ae99c955b6b82f7306633004b1
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 {
24
25using namespace uirenderer;
26ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
27
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
31// Resource cache
32///////////////////////////////////////////////////////////////////////////////
33
34void ResourceCache::logCache() {
35    ALOGD("ResourceCache: cacheReport:");
36    for (size_t i = 0; i < mCache->size(); ++i) {
37        ResourceReference* ref = mCache->valueAt(i);
38        ALOGD("  ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p",
39                i, mCache->keyAt(i), mCache->valueAt(i));
40        ALOGD("  ResourceCache: mCache(%zu): refCount, recycled, destroyed, type = %d, %d, %d, %d",
41                i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
42    }
43}
44
45ResourceCache::ResourceCache() {
46    Mutex::Autolock _l(mLock);
47    mCache = new KeyedVector<const void*, ResourceReference*>();
48}
49
50ResourceCache::~ResourceCache() {
51    Mutex::Autolock _l(mLock);
52    delete mCache;
53}
54
55void ResourceCache::lock() {
56    mLock.lock();
57}
58
59void ResourceCache::unlock() {
60    mLock.unlock();
61}
62
63void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
64    Mutex::Autolock _l(mLock);
65    incrementRefcountLocked(resource, resourceType);
66}
67
68void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) {
69    incrementRefcount((void*) bitmapResource, kBitmap);
70}
71
72void ResourceCache::incrementRefcount(const SkPath* pathResource) {
73    incrementRefcount((void*) pathResource, kPath);
74}
75
76void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
77    incrementRefcount((void*) patchResource, kNinePatch);
78}
79
80void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
81    ssize_t index = mCache->indexOfKey(resource);
82    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
83    if (ref == nullptr || mCache->size() == 0) {
84        ref = new ResourceReference(resourceType);
85        mCache->add(resource, ref);
86    }
87    ref->refCount++;
88}
89
90void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) {
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    decrementRefcount((void*) bitmapResource);
109}
110
111void ResourceCache::decrementRefcount(const SkPath* pathResource) {
112    decrementRefcount((void*) pathResource);
113}
114
115void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
116    decrementRefcount((void*) patchResource);
117}
118
119void ResourceCache::decrementRefcountLocked(void* resource) {
120    ssize_t index = mCache->indexOfKey(resource);
121    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
122    if (ref == nullptr) {
123        // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
124        return;
125    }
126    ref->refCount--;
127    if (ref->refCount == 0) {
128        deleteResourceReferenceLocked(resource, ref);
129    }
130}
131
132void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
133    decrementRefcountLocked((void*) bitmapResource);
134}
135
136void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) {
137    decrementRefcountLocked((void*) pathResource);
138}
139
140void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
141    decrementRefcountLocked((void*) patchResource);
142}
143
144void ResourceCache::destructor(SkPath* resource) {
145    Mutex::Autolock _l(mLock);
146    destructorLocked(resource);
147}
148
149void ResourceCache::destructorLocked(SkPath* resource) {
150    ssize_t index = mCache->indexOfKey(resource);
151    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
152    if (ref == nullptr) {
153        // If we're not tracking this resource, just delete it
154        if (Caches::hasInstance()) {
155            Caches::getInstance().pathCache.removeDeferred(resource);
156        } else {
157            delete resource;
158        }
159        return;
160    }
161    ref->destroyed = true;
162    if (ref->refCount == 0) {
163        deleteResourceReferenceLocked(resource, ref);
164    }
165}
166
167void ResourceCache::destructor(const SkBitmap* resource) {
168    Mutex::Autolock _l(mLock);
169    destructorLocked(resource);
170}
171
172void ResourceCache::destructorLocked(const SkBitmap* resource) {
173    ssize_t index = mCache->indexOfKey(resource);
174    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
175    if (ref == nullptr) {
176        // If we're not tracking this resource, just delete it
177        if (Caches::hasInstance()) {
178            Caches::getInstance().textureCache.releaseTexture(resource);
179        }
180        delete resource;
181        return;
182    }
183    ref->destroyed = true;
184    if (ref->refCount == 0) {
185        deleteResourceReferenceLocked(resource, ref);
186    }
187}
188
189void ResourceCache::destructor(Res_png_9patch* resource) {
190    Mutex::Autolock _l(mLock);
191    destructorLocked(resource);
192}
193
194void ResourceCache::destructorLocked(Res_png_9patch* resource) {
195    ssize_t index = mCache->indexOfKey(resource);
196    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
197    if (ref == nullptr) {
198        // If we're not tracking this resource, just delete it
199        if (Caches::hasInstance()) {
200            Caches::getInstance().patchCache.removeDeferred(resource);
201        } else {
202            // A Res_png_9patch is actually an array of byte that's larger
203            // than sizeof(Res_png_9patch). It must be freed as an array.
204            delete[] (int8_t*) resource;
205        }
206        return;
207    }
208    ref->destroyed = true;
209    if (ref->refCount == 0) {
210        deleteResourceReferenceLocked(resource, ref);
211    }
212}
213
214/**
215 * Return value indicates whether resource was actually recycled, which happens when RefCnt
216 * reaches 0.
217 */
218bool ResourceCache::recycle(SkBitmap* resource) {
219    Mutex::Autolock _l(mLock);
220    return recycleLocked(resource);
221}
222
223/**
224 * Return value indicates whether resource was actually recycled, which happens when RefCnt
225 * reaches 0.
226 */
227bool ResourceCache::recycleLocked(SkBitmap* resource) {
228    ssize_t index = mCache->indexOfKey(resource);
229    if (index < 0) {
230        if (Caches::hasInstance()) {
231            Caches::getInstance().textureCache.releaseTexture(resource);
232        }
233        // not tracking this resource; just recycle the pixel data
234        resource->setPixels(nullptr, nullptr);
235        return true;
236    }
237    ResourceReference* ref = mCache->valueAt(index);
238    if (ref == nullptr) {
239        // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
240        return true;
241    }
242    ref->recycled = true;
243    if (ref->refCount == 0) {
244        deleteResourceReferenceLocked(resource, ref);
245        return true;
246    }
247    // Still referring to resource, don't recycle yet
248    return false;
249}
250
251/**
252 * This method should only be called while the mLock mutex is held (that mutex is grabbed
253 * by the various destructor() and recycle() methods which call this method).
254 */
255void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
256    if (ref->recycled && ref->resourceType == kBitmap) {
257        SkBitmap* bitmap = (SkBitmap*) resource;
258        if (Caches::hasInstance()) {
259            Caches::getInstance().textureCache.releaseTexture(bitmap);
260        }
261        bitmap->setPixels(nullptr, nullptr);
262    }
263    if (ref->destroyed) {
264        switch (ref->resourceType) {
265            case kBitmap: {
266                SkBitmap* bitmap = (SkBitmap*) resource;
267                if (Caches::hasInstance()) {
268                    Caches::getInstance().textureCache.releaseTexture(bitmap);
269                }
270                delete bitmap;
271            }
272            break;
273            case kPath: {
274                SkPath* path = (SkPath*) resource;
275                if (Caches::hasInstance()) {
276                    Caches::getInstance().pathCache.removeDeferred(path);
277                } else {
278                    delete path;
279                }
280            }
281            break;
282            case kNinePatch: {
283                if (Caches::hasInstance()) {
284                    Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
285                } else {
286                    // A Res_png_9patch is actually an array of byte that's larger
287                    // than sizeof(Res_png_9patch). It must be freed as an array.
288                    int8_t* patch = (int8_t*) resource;
289                    delete[] patch;
290                }
291            }
292            break;
293        }
294    }
295    mCache->removeItem(resource);
296    delete ref;
297}
298
299}; // namespace uirenderer
300}; // namespace android
301