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