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#include <SkPixelRef.h>
18#include "ResourceCache.h"
19#include "Caches.h"
20
21namespace android {
22namespace uirenderer {
23
24///////////////////////////////////////////////////////////////////////////////
25// Resource cache
26///////////////////////////////////////////////////////////////////////////////
27
28void ResourceCache::logCache() {
29    ALOGD("ResourceCache: cacheReport:");
30    for (size_t i = 0; i < mCache->size(); ++i) {
31        ResourceReference* ref = mCache->valueAt(i);
32        ALOGD("  ResourceCache: mCache(%d): resource, ref = 0x%p, 0x%p",
33                i, mCache->keyAt(i), mCache->valueAt(i));
34        ALOGD("  ResourceCache: mCache(%d): refCount, recycled, destroyed, type = %d, %d, %d, %d",
35                i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
36    }
37}
38
39ResourceCache::ResourceCache() {
40    Mutex::Autolock _l(mLock);
41    mCache = new KeyedVector<void*, ResourceReference*>();
42}
43
44ResourceCache::~ResourceCache() {
45    Mutex::Autolock _l(mLock);
46    delete mCache;
47}
48
49void ResourceCache::lock() {
50    mLock.lock();
51}
52
53void ResourceCache::unlock() {
54    mLock.unlock();
55}
56
57void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
58    Mutex::Autolock _l(mLock);
59    incrementRefcountLocked(resource, resourceType);
60}
61
62void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
63    SkSafeRef(bitmapResource->pixelRef());
64    SkSafeRef(bitmapResource->getColorTable());
65    incrementRefcount((void*) bitmapResource, kBitmap);
66}
67
68void ResourceCache::incrementRefcount(SkPath* pathResource) {
69    incrementRefcount((void*) pathResource, kPath);
70}
71
72void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
73    SkSafeRef(shaderResource->getSkShader());
74    incrementRefcount((void*) shaderResource, kShader);
75}
76
77void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) {
78    SkSafeRef(filterResource->getSkColorFilter());
79    incrementRefcount((void*) filterResource, kColorFilter);
80}
81
82void ResourceCache::incrementRefcount(Layer* layerResource) {
83    incrementRefcount((void*) layerResource, kLayer);
84}
85
86void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
87    ssize_t index = mCache->indexOfKey(resource);
88    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
89    if (ref == NULL || mCache->size() == 0) {
90        ref = new ResourceReference(resourceType);
91        mCache->add(resource, ref);
92    }
93    ref->refCount++;
94}
95
96void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) {
97    SkSafeRef(bitmapResource->pixelRef());
98    SkSafeRef(bitmapResource->getColorTable());
99    incrementRefcountLocked((void*) bitmapResource, kBitmap);
100}
101
102void ResourceCache::incrementRefcountLocked(SkPath* pathResource) {
103    incrementRefcountLocked((void*) pathResource, kPath);
104}
105
106void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) {
107    SkSafeRef(shaderResource->getSkShader());
108    incrementRefcountLocked((void*) shaderResource, kShader);
109}
110
111void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) {
112    SkSafeRef(filterResource->getSkColorFilter());
113    incrementRefcountLocked((void*) filterResource, kColorFilter);
114}
115
116void ResourceCache::incrementRefcountLocked(Layer* layerResource) {
117    incrementRefcountLocked((void*) layerResource, kLayer);
118}
119
120void ResourceCache::decrementRefcount(void* resource) {
121    Mutex::Autolock _l(mLock);
122    decrementRefcountLocked(resource);
123}
124
125void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
126    SkSafeUnref(bitmapResource->pixelRef());
127    SkSafeUnref(bitmapResource->getColorTable());
128    decrementRefcount((void*) bitmapResource);
129}
130
131void ResourceCache::decrementRefcount(SkPath* pathResource) {
132    decrementRefcount((void*) pathResource);
133}
134
135void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
136    SkSafeUnref(shaderResource->getSkShader());
137    decrementRefcount((void*) shaderResource);
138}
139
140void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) {
141    SkSafeUnref(filterResource->getSkColorFilter());
142    decrementRefcount((void*) filterResource);
143}
144
145void ResourceCache::decrementRefcount(Layer* layerResource) {
146    decrementRefcount((void*) layerResource);
147}
148
149void ResourceCache::decrementRefcountLocked(void* resource) {
150    ssize_t index = mCache->indexOfKey(resource);
151    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
152    if (ref == NULL) {
153        // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
154        return;
155    }
156    ref->refCount--;
157    if (ref->refCount == 0) {
158        deleteResourceReferenceLocked(resource, ref);
159    }
160}
161
162void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) {
163    SkSafeUnref(bitmapResource->pixelRef());
164    SkSafeUnref(bitmapResource->getColorTable());
165    decrementRefcountLocked((void*) bitmapResource);
166}
167
168void ResourceCache::decrementRefcountLocked(SkPath* pathResource) {
169    decrementRefcountLocked((void*) pathResource);
170}
171
172void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) {
173    SkSafeUnref(shaderResource->getSkShader());
174    decrementRefcountLocked((void*) shaderResource);
175}
176
177void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) {
178    SkSafeUnref(filterResource->getSkColorFilter());
179    decrementRefcountLocked((void*) filterResource);
180}
181
182void ResourceCache::decrementRefcountLocked(Layer* layerResource) {
183    decrementRefcountLocked((void*) layerResource);
184}
185
186void ResourceCache::destructor(SkPath* resource) {
187    Mutex::Autolock _l(mLock);
188    destructorLocked(resource);
189}
190
191void ResourceCache::destructorLocked(SkPath* resource) {
192    ssize_t index = mCache->indexOfKey(resource);
193    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
194    if (ref == NULL) {
195        // If we're not tracking this resource, just delete it
196        if (Caches::hasInstance()) {
197            Caches::getInstance().pathCache.removeDeferred(resource);
198        }
199        delete resource;
200        return;
201    }
202    ref->destroyed = true;
203    if (ref->refCount == 0) {
204        deleteResourceReferenceLocked(resource, ref);
205    }
206}
207
208void ResourceCache::destructor(SkBitmap* resource) {
209    Mutex::Autolock _l(mLock);
210    destructorLocked(resource);
211}
212
213void ResourceCache::destructorLocked(SkBitmap* resource) {
214    ssize_t index = mCache->indexOfKey(resource);
215    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
216    if (ref == NULL) {
217        // If we're not tracking this resource, just delete it
218        if (Caches::hasInstance()) {
219            Caches::getInstance().textureCache.removeDeferred(resource);
220        }
221        delete resource;
222        return;
223    }
224    ref->destroyed = true;
225    if (ref->refCount == 0) {
226        deleteResourceReferenceLocked(resource, ref);
227    }
228}
229
230void ResourceCache::destructor(SkiaShader* resource) {
231    Mutex::Autolock _l(mLock);
232    destructorLocked(resource);
233}
234
235void ResourceCache::destructorLocked(SkiaShader* resource) {
236    ssize_t index = mCache->indexOfKey(resource);
237    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
238    if (ref == NULL) {
239        // If we're not tracking this resource, just delete it
240        delete resource;
241        return;
242    }
243    ref->destroyed = true;
244    if (ref->refCount == 0) {
245        deleteResourceReferenceLocked(resource, ref);
246    }
247}
248
249void ResourceCache::destructor(SkiaColorFilter* resource) {
250    Mutex::Autolock _l(mLock);
251    destructorLocked(resource);
252}
253
254void ResourceCache::destructorLocked(SkiaColorFilter* resource) {
255    ssize_t index = mCache->indexOfKey(resource);
256    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
257    if (ref == NULL) {
258        // If we're not tracking this resource, just delete it
259        delete resource;
260        return;
261    }
262    ref->destroyed = true;
263    if (ref->refCount == 0) {
264        deleteResourceReferenceLocked(resource, ref);
265    }
266}
267
268/**
269 * Return value indicates whether resource was actually recycled, which happens when RefCnt
270 * reaches 0.
271 */
272bool ResourceCache::recycle(SkBitmap* resource) {
273    Mutex::Autolock _l(mLock);
274    return recycleLocked(resource);
275}
276
277/**
278 * Return value indicates whether resource was actually recycled, which happens when RefCnt
279 * reaches 0.
280 */
281bool ResourceCache::recycleLocked(SkBitmap* resource) {
282    ssize_t index = mCache->indexOfKey(resource);
283    if (index < 0) {
284        // not tracking this resource; just recycle the pixel data
285        resource->setPixels(NULL, NULL);
286        return true;
287    }
288    ResourceReference* ref = mCache->valueAt(index);
289    if (ref == NULL) {
290        // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
291        return true;
292    }
293    ref->recycled = true;
294    if (ref->refCount == 0) {
295        deleteResourceReferenceLocked(resource, ref);
296        return true;
297    }
298    // Still referring to resource, don't recycle yet
299    return false;
300}
301
302/**
303 * This method should only be called while the mLock mutex is held (that mutex is grabbed
304 * by the various destructor() and recycle() methods which call this method).
305 */
306void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceReference* ref) {
307    if (ref->recycled && ref->resourceType == kBitmap) {
308        ((SkBitmap*) resource)->setPixels(NULL, NULL);
309    }
310    if (ref->destroyed || ref->resourceType == kLayer) {
311        switch (ref->resourceType) {
312            case kBitmap: {
313                SkBitmap* bitmap = (SkBitmap*) resource;
314                if (Caches::hasInstance()) {
315                    Caches::getInstance().textureCache.removeDeferred(bitmap);
316                }
317                delete bitmap;
318            }
319            break;
320            case kPath: {
321                SkPath* path = (SkPath*) resource;
322                if (Caches::hasInstance()) {
323                    Caches::getInstance().pathCache.removeDeferred(path);
324                }
325                delete path;
326            }
327            break;
328            case kShader: {
329                SkiaShader* shader = (SkiaShader*) resource;
330                delete shader;
331            }
332            break;
333            case kColorFilter: {
334                SkiaColorFilter* filter = (SkiaColorFilter*) resource;
335                delete filter;
336            }
337            break;
338            case kLayer: {
339                Layer* layer = (Layer*) resource;
340                Caches::getInstance().deleteLayerDeferred(layer);
341            }
342            break;
343        }
344    }
345    mCache->removeItem(resource);
346    delete ref;
347}
348
349}; // namespace uirenderer
350}; // namespace android
351