| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "OpenGLRenderer" |
| |
| #include <SkPixelRef.h> |
| #include "ResourceCache.h" |
| #include "Caches.h" |
| |
| namespace android { |
| |
| #ifdef USE_OPENGL_RENDERER |
| using namespace uirenderer; |
| ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache); |
| #endif |
| |
| namespace uirenderer { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Resource cache |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void ResourceCache::logCache() { |
| ALOGD("ResourceCache: cacheReport:"); |
| for (size_t i = 0; i < mCache->size(); ++i) { |
| ResourceReference* ref = mCache->valueAt(i); |
| ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", |
| i, mCache->keyAt(i), mCache->valueAt(i)); |
| ALOGD(" ResourceCache: mCache(%zu): refCount, recycled, destroyed, type = %d, %d, %d, %d", |
| i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType); |
| } |
| } |
| |
| ResourceCache::ResourceCache() { |
| Mutex::Autolock _l(mLock); |
| mCache = new KeyedVector<const void*, ResourceReference*>(); |
| } |
| |
| ResourceCache::~ResourceCache() { |
| Mutex::Autolock _l(mLock); |
| delete mCache; |
| } |
| |
| void ResourceCache::lock() { |
| mLock.lock(); |
| } |
| |
| void ResourceCache::unlock() { |
| mLock.unlock(); |
| } |
| |
| void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) { |
| Mutex::Autolock _l(mLock); |
| incrementRefcountLocked(resource, resourceType); |
| } |
| |
| void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) { |
| bitmapResource->pixelRef()->globalRef(); |
| SkSafeRef(bitmapResource->getColorTable()); |
| incrementRefcount((void*) bitmapResource, kBitmap); |
| } |
| |
| void ResourceCache::incrementRefcount(const SkPath* pathResource) { |
| incrementRefcount((void*) pathResource, kPath); |
| } |
| |
| void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { |
| incrementRefcount((void*) patchResource, kNinePatch); |
| } |
| |
| void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) { |
| ssize_t index = mCache->indexOfKey(resource); |
| ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; |
| if (ref == NULL || mCache->size() == 0) { |
| ref = new ResourceReference(resourceType); |
| mCache->add(resource, ref); |
| } |
| ref->refCount++; |
| } |
| |
| void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) { |
| bitmapResource->pixelRef()->globalRef(); |
| SkSafeRef(bitmapResource->getColorTable()); |
| incrementRefcountLocked((void*) bitmapResource, kBitmap); |
| } |
| |
| void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) { |
| incrementRefcountLocked((void*) pathResource, kPath); |
| } |
| |
| void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) { |
| incrementRefcountLocked((void*) patchResource, kNinePatch); |
| } |
| |
| void ResourceCache::decrementRefcount(void* resource) { |
| Mutex::Autolock _l(mLock); |
| decrementRefcountLocked(resource); |
| } |
| |
| void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) { |
| bitmapResource->pixelRef()->globalUnref(); |
| SkSafeUnref(bitmapResource->getColorTable()); |
| decrementRefcount((void*) bitmapResource); |
| } |
| |
| void ResourceCache::decrementRefcount(const SkPath* pathResource) { |
| decrementRefcount((void*) pathResource); |
| } |
| |
| void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { |
| decrementRefcount((void*) patchResource); |
| } |
| |
| void ResourceCache::decrementRefcountLocked(void* resource) { |
| ssize_t index = mCache->indexOfKey(resource); |
| ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; |
| if (ref == NULL) { |
| // Should not get here - shouldn't get a call to decrement if we're not yet tracking it |
| return; |
| } |
| ref->refCount--; |
| if (ref->refCount == 0) { |
| deleteResourceReferenceLocked(resource, ref); |
| } |
| } |
| |
| void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) { |
| bitmapResource->pixelRef()->globalUnref(); |
| SkSafeUnref(bitmapResource->getColorTable()); |
| decrementRefcountLocked((void*) bitmapResource); |
| } |
| |
| void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) { |
| decrementRefcountLocked((void*) pathResource); |
| } |
| |
| void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { |
| decrementRefcountLocked((void*) patchResource); |
| } |
| |
| void ResourceCache::destructor(SkPath* resource) { |
| Mutex::Autolock _l(mLock); |
| destructorLocked(resource); |
| } |
| |
| void ResourceCache::destructorLocked(SkPath* resource) { |
| ssize_t index = mCache->indexOfKey(resource); |
| ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; |
| if (ref == NULL) { |
| // If we're not tracking this resource, just delete it |
| if (Caches::hasInstance()) { |
| Caches::getInstance().pathCache.removeDeferred(resource); |
| } else { |
| delete resource; |
| } |
| return; |
| } |
| ref->destroyed = true; |
| if (ref->refCount == 0) { |
| deleteResourceReferenceLocked(resource, ref); |
| } |
| } |
| |
| void ResourceCache::destructor(const SkBitmap* resource) { |
| Mutex::Autolock _l(mLock); |
| destructorLocked(resource); |
| } |
| |
| void ResourceCache::destructorLocked(const SkBitmap* resource) { |
| ssize_t index = mCache->indexOfKey(resource); |
| ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; |
| if (ref == NULL) { |
| // If we're not tracking this resource, just delete it |
| if (Caches::hasInstance()) { |
| Caches::getInstance().textureCache.releaseTexture(resource); |
| } |
| delete resource; |
| return; |
| } |
| ref->destroyed = true; |
| if (ref->refCount == 0) { |
| deleteResourceReferenceLocked(resource, ref); |
| } |
| } |
| |
| void ResourceCache::destructor(Res_png_9patch* resource) { |
| Mutex::Autolock _l(mLock); |
| destructorLocked(resource); |
| } |
| |
| void ResourceCache::destructorLocked(Res_png_9patch* resource) { |
| ssize_t index = mCache->indexOfKey(resource); |
| ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; |
| if (ref == NULL) { |
| // If we're not tracking this resource, just delete it |
| if (Caches::hasInstance()) { |
| Caches::getInstance().patchCache.removeDeferred(resource); |
| } else { |
| // A Res_png_9patch is actually an array of byte that's larger |
| // than sizeof(Res_png_9patch). It must be freed as an array. |
| delete[] (int8_t*) resource; |
| } |
| return; |
| } |
| ref->destroyed = true; |
| if (ref->refCount == 0) { |
| deleteResourceReferenceLocked(resource, ref); |
| } |
| } |
| |
| /** |
| * Return value indicates whether resource was actually recycled, which happens when RefCnt |
| * reaches 0. |
| */ |
| bool ResourceCache::recycle(SkBitmap* resource) { |
| Mutex::Autolock _l(mLock); |
| return recycleLocked(resource); |
| } |
| |
| /** |
| * Return value indicates whether resource was actually recycled, which happens when RefCnt |
| * reaches 0. |
| */ |
| bool ResourceCache::recycleLocked(SkBitmap* resource) { |
| ssize_t index = mCache->indexOfKey(resource); |
| if (index < 0) { |
| if (Caches::hasInstance()) { |
| Caches::getInstance().textureCache.releaseTexture(resource); |
| } |
| // not tracking this resource; just recycle the pixel data |
| resource->setPixels(NULL, NULL); |
| return true; |
| } |
| ResourceReference* ref = mCache->valueAt(index); |
| if (ref == NULL) { |
| // Should not get here - shouldn't get a call to recycle if we're not yet tracking it |
| return true; |
| } |
| ref->recycled = true; |
| if (ref->refCount == 0) { |
| deleteResourceReferenceLocked(resource, ref); |
| return true; |
| } |
| // Still referring to resource, don't recycle yet |
| return false; |
| } |
| |
| /** |
| * This method should only be called while the mLock mutex is held (that mutex is grabbed |
| * by the various destructor() and recycle() methods which call this method). |
| */ |
| void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { |
| if (ref->recycled && ref->resourceType == kBitmap) { |
| SkBitmap* bitmap = (SkBitmap*) resource; |
| if (Caches::hasInstance()) { |
| Caches::getInstance().textureCache.releaseTexture(bitmap); |
| } |
| bitmap->setPixels(NULL, NULL); |
| } |
| if (ref->destroyed) { |
| switch (ref->resourceType) { |
| case kBitmap: { |
| SkBitmap* bitmap = (SkBitmap*) resource; |
| if (Caches::hasInstance()) { |
| Caches::getInstance().textureCache.releaseTexture(bitmap); |
| } |
| delete bitmap; |
| } |
| break; |
| case kPath: { |
| SkPath* path = (SkPath*) resource; |
| if (Caches::hasInstance()) { |
| Caches::getInstance().pathCache.removeDeferred(path); |
| } else { |
| delete path; |
| } |
| } |
| break; |
| case kNinePatch: { |
| if (Caches::hasInstance()) { |
| Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource); |
| } else { |
| // A Res_png_9patch is actually an array of byte that's larger |
| // than sizeof(Res_png_9patch). It must be freed as an array. |
| int8_t* patch = (int8_t*) resource; |
| delete[] patch; |
| } |
| } |
| break; |
| } |
| } |
| mCache->removeItem(resource); |
| delete ref; |
| } |
| |
| }; // namespace uirenderer |
| }; // namespace android |