Update HWUI to store its own SkBitmap objects
This enables us to... 1) simplify the lifecycle/ownership between Java and HWUI 2) remove DisplayListRenderer::drawBitmapData and associated logic 3) track pixel lifecycle using standard SkPixelRef refcounting 4) Remove uncessary calls to ref/unref the bitmap's pixels and colorTable Change-Id: I3c95078da20995444f6388a029414280fd654318
This commit is contained in:
@@ -19,8 +19,6 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <ResourceCache.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Conversions to/from SkColor, for get/setPixels, and the create method, which
|
||||
// is basically like setPixels
|
||||
@@ -360,20 +358,11 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
|
||||
|
||||
static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
|
||||
if (android::uirenderer::ResourceCache::hasInstance()) {
|
||||
android::uirenderer::ResourceCache::getInstance().destructor(bitmap);
|
||||
} else {
|
||||
delete bitmap;
|
||||
}
|
||||
delete bitmap;
|
||||
}
|
||||
|
||||
static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
|
||||
if (android::uirenderer::ResourceCache::hasInstance()) {
|
||||
bool result;
|
||||
result = android::uirenderer::ResourceCache::getInstance().recycle(bitmap);
|
||||
return result ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
bitmap->setPixels(NULL, NULL);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "SkRegion.h"
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
#include <Caches.h>
|
||||
#include <TextureCache.h>
|
||||
|
||||
void doThrowNPE(JNIEnv* env) {
|
||||
jniThrowNullPointerException(env, NULL);
|
||||
}
|
||||
@@ -500,10 +503,28 @@ AndroidPixelRef::~AndroidPixelRef() {
|
||||
JNIEnv* env = vm2env(fVM);
|
||||
env->DeleteGlobalRef(fStorageObj);
|
||||
}
|
||||
|
||||
if (android::uirenderer::Caches::hasInstance()) {
|
||||
android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool computeAllocationSize(const SkImageInfo& info, size_t* size, size_t* rowBytes) {
|
||||
int32_t rowBytes32 = SkToS32(info.minRowBytes());
|
||||
int64_t bigSize = (int64_t)info.height() * rowBytes32;
|
||||
if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
|
||||
return false; // allocation will be too large
|
||||
}
|
||||
|
||||
*size = sk_64_asS32(bigSize);
|
||||
*rowBytes = rowBytes32;
|
||||
|
||||
SkASSERT(*size >= info.getSafeSize(*rowBytes));
|
||||
return true;
|
||||
}
|
||||
|
||||
jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable) {
|
||||
const SkImageInfo& info = bitmap->info();
|
||||
@@ -512,7 +533,11 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const size_t size = bitmap->getSize();
|
||||
size_t size, rowBytes;
|
||||
if (!computeAllocationSize(info, &size, &rowBytes)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,
|
||||
gVMRuntime_newNonMovableArray,
|
||||
gByte_class, size);
|
||||
@@ -525,8 +550,7 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
return NULL;
|
||||
}
|
||||
SkASSERT(addr);
|
||||
SkPixelRef* pr = new AndroidPixelRef(env, info, (void*) addr,
|
||||
bitmap->rowBytes(), arrayObj, ctable);
|
||||
SkPixelRef* pr = new AndroidPixelRef(env, info, (void*) addr, rowBytes, arrayObj, ctable);
|
||||
bitmap->setPixelRef(pr)->unref();
|
||||
// since we're already allocated, we lockPixels right away
|
||||
// HeapAllocator behaves this way too
|
||||
@@ -535,6 +559,56 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
return arrayObj;
|
||||
}
|
||||
|
||||
struct AndroidPixelRefContext {
|
||||
int32_t stableID;
|
||||
};
|
||||
|
||||
static void allocatePixelsReleaseProc(void* ptr, void* ctx) {
|
||||
AndroidPixelRefContext* context = (AndroidPixelRefContext*)ctx;
|
||||
if (android::uirenderer::Caches::hasInstance()) {
|
||||
android::uirenderer::Caches::getInstance().textureCache.releaseTexture(context->stableID);
|
||||
}
|
||||
|
||||
sk_free(ptr);
|
||||
delete context;
|
||||
}
|
||||
|
||||
bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
const SkImageInfo& info = bitmap->info();
|
||||
if (info.fColorType == kUnknown_SkColorType) {
|
||||
doThrowIAE(env, "unknown bitmap configuration");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t size, rowBytes;
|
||||
if (!computeAllocationSize(info, &size, &rowBytes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* addr = sk_malloc_flags(size, 0);
|
||||
if (NULL == addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AndroidPixelRefContext* context = new AndroidPixelRefContext;
|
||||
SkMallocPixelRef* pr = SkMallocPixelRef::NewWithProc(info, rowBytes, ctable, addr,
|
||||
&allocatePixelsReleaseProc, context);
|
||||
if (!pr) {
|
||||
delete context;
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the stableID in the context so that it can be used later in
|
||||
// allocatePixelsReleaseProc to remove the texture from the cache.
|
||||
context->stableID = pr->getStableID();
|
||||
|
||||
bitmap->setPixelRef(pr)->unref();
|
||||
// since we're already allocated, we can lockPixels right away
|
||||
bitmap->lockPixels();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env)
|
||||
|
||||
@@ -95,6 +95,14 @@ public:
|
||||
static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable);
|
||||
|
||||
/**
|
||||
* Given a bitmap we natively allocate a memory block to store the contents
|
||||
* of that bitmap. The memory is then attached to the bitmap via an
|
||||
* SkPixelRef, which ensures that upon deletion the appropriate caches
|
||||
* are notified.
|
||||
*/
|
||||
static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable);
|
||||
|
||||
/** Copy the colors in colors[] to the bitmap, convert to the correct
|
||||
format along the way.
|
||||
Whether to use premultiplied pixels is determined by dstBitmap's alphaType.
|
||||
|
||||
@@ -383,7 +383,8 @@ static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
|
||||
hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
|
||||
kPremul_SkAlphaType);
|
||||
SkBitmap bitmap;
|
||||
if (!bitmap.tryAllocPixels(info)) {
|
||||
bitmap.setInfo(info);
|
||||
if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +46,6 @@ void DisplayListData::cleanupResources() {
|
||||
resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
|
||||
const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
|
||||
resourceCache.decrementRefcountLocked(bitmap);
|
||||
resourceCache.destructorLocked(bitmap);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < patchResources.size(); i++) {
|
||||
resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
|
||||
}
|
||||
@@ -63,7 +57,6 @@ void DisplayListData::cleanupResources() {
|
||||
resourceCache.unlock();
|
||||
|
||||
bitmapResources.clear();
|
||||
ownedBitmapResources.clear();
|
||||
patchResources.clear();
|
||||
sourcePaths.clear();
|
||||
paints.clear();
|
||||
|
||||
@@ -134,7 +134,6 @@ public:
|
||||
int projectionReceiveIndex;
|
||||
|
||||
Vector<const SkBitmap*> bitmapResources;
|
||||
Vector<const SkBitmap*> ownedBitmapResources;
|
||||
Vector<const Res_png_9patch*> patchResources;
|
||||
|
||||
std::vector<std::unique_ptr<const SkPaint>> paints;
|
||||
|
||||
@@ -734,27 +734,6 @@ private:
|
||||
Rect mSrc;
|
||||
};
|
||||
|
||||
class DrawBitmapDataOp : public DrawBitmapOp {
|
||||
public:
|
||||
DrawBitmapDataOp(const SkBitmap* bitmap, const SkPaint* paint)
|
||||
: DrawBitmapOp(bitmap, paint) {}
|
||||
|
||||
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
|
||||
renderer.drawBitmapData(mBitmap, mPaint);
|
||||
}
|
||||
|
||||
virtual void output(int level, uint32_t logFlags) const override {
|
||||
OP_LOG("Draw bitmap %p", mBitmap);
|
||||
}
|
||||
|
||||
virtual const char* name() override { return "DrawBitmapData"; }
|
||||
|
||||
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
|
||||
const DeferredDisplayState& state) override {
|
||||
deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
|
||||
}
|
||||
};
|
||||
|
||||
class DrawBitmapMeshOp : public DrawBoundedOp {
|
||||
public:
|
||||
DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight,
|
||||
|
||||
@@ -266,13 +266,6 @@ void DisplayListRenderer::drawBitmap(const SkBitmap& bitmap, float srcLeft, floa
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
|
||||
bitmap = refBitmapData(bitmap);
|
||||
paint = refPaint(paint);
|
||||
|
||||
addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, paint));
|
||||
}
|
||||
|
||||
void DisplayListRenderer::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
|
||||
const float* vertices, const int* colors, const SkPaint* paint) {
|
||||
int vertexCount = (meshWidth + 1) * (meshHeight + 1);
|
||||
|
||||
@@ -101,8 +101,6 @@ public:
|
||||
|
||||
// Bitmap-based
|
||||
void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
|
||||
// TODO: move drawBitmapData() to Canvas.h
|
||||
void drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint);
|
||||
// TODO: move drawPatch() to Canvas.h
|
||||
void drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
|
||||
float left, float top, float right, float bottom, const SkPaint* paint);
|
||||
@@ -367,15 +365,9 @@ private:
|
||||
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
|
||||
// contents, and drawing again. The only fix would be to always copy it the first time,
|
||||
// which doesn't seem worth the extra cycles for this unlikely case.
|
||||
mDisplayListData->bitmapResources.add(bitmap);
|
||||
mResourceCache.incrementRefcount(bitmap);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
|
||||
mDisplayListData->ownedBitmapResources.add(bitmap);
|
||||
mResourceCache.incrementRefcount(bitmap);
|
||||
return bitmap;
|
||||
const SkBitmap* cachedBitmap = mResourceCache.insert(bitmap);
|
||||
mDisplayListData->bitmapResources.add(cachedBitmap);
|
||||
return cachedBitmap;
|
||||
}
|
||||
|
||||
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
|
||||
|
||||
@@ -2006,24 +2006,6 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
|
||||
mDirty = true;
|
||||
}
|
||||
|
||||
void OpenGLRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
|
||||
if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCaches.activeTexture(0);
|
||||
Texture* texture = mCaches.textureCache.getTransient(bitmap);
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
|
||||
drawAlphaBitmap(texture, 0, 0, paint);
|
||||
} else {
|
||||
drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint);
|
||||
}
|
||||
|
||||
mDirty = true;
|
||||
}
|
||||
|
||||
void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
|
||||
const float* vertices, const int* colors, const SkPaint* paint) {
|
||||
if (!vertices || mState.currentlyIgnored()) {
|
||||
|
||||
@@ -162,7 +162,6 @@ public:
|
||||
virtual void drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
|
||||
float srcRight, float srcBottom, float dstLeft, float dstTop,
|
||||
float dstRight, float dstBottom, const SkPaint* paint) override;
|
||||
virtual void drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) override;
|
||||
virtual void drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
|
||||
const float* vertices, const int* colors, const SkPaint* paint) override;
|
||||
void drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
|
||||
|
||||
@@ -338,11 +338,6 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
|
||||
if (subtree) {
|
||||
TextureCache& cache = Caches::getInstance().textureCache;
|
||||
info.out.hasFunctors |= subtree->functors.size();
|
||||
// TODO: Fix ownedBitmapResources to not require disabling prepareTextures
|
||||
// and thus falling out of async drawing path.
|
||||
if (subtree->ownedBitmapResources.size()) {
|
||||
info.prepareTextures = false;
|
||||
}
|
||||
for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
|
||||
info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,6 @@ public:
|
||||
virtual void drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
|
||||
float srcRight, float srcBottom, float dstLeft, float dstTop,
|
||||
float dstRight, float dstBottom, const SkPaint* paint) = 0;
|
||||
virtual void drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) = 0;
|
||||
virtual void drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
|
||||
const float* vertices, const int* colors, const SkPaint* paint) = 0;
|
||||
virtual void drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#define LOG_TAG "OpenGLRenderer"
|
||||
|
||||
#include <SkPixelRef.h>
|
||||
#include "ResourceCache.h"
|
||||
#include "Caches.h"
|
||||
|
||||
@@ -37,8 +36,8 @@ void ResourceCache::logCache() {
|
||||
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);
|
||||
ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d",
|
||||
i, ref->refCount, ref->destroyed, ref->resourceType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,15 +59,26 @@ void ResourceCache::unlock() {
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
const SkBitmap* ResourceCache::insert(const SkBitmap* bitmapResource) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
|
||||
BitmapKey bitmapKey(bitmapResource);
|
||||
ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
|
||||
if (index == NAME_NOT_FOUND) {
|
||||
SkBitmap* cachedBitmap = new SkBitmap(*bitmapResource);
|
||||
index = mBitmapCache.add(bitmapKey, cachedBitmap);
|
||||
return cachedBitmap;
|
||||
}
|
||||
|
||||
mBitmapCache.keyAt(index).mRefCount++;
|
||||
return mBitmapCache.valueAt(index);
|
||||
}
|
||||
|
||||
void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
incrementRefcountLocked(resource, resourceType);
|
||||
}
|
||||
|
||||
void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) {
|
||||
incrementRefcount((void*) bitmapResource, kBitmap);
|
||||
}
|
||||
|
||||
void ResourceCache::incrementRefcount(const SkPath* pathResource) {
|
||||
incrementRefcount((void*) pathResource, kPath);
|
||||
}
|
||||
@@ -87,25 +97,14 @@ void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourc
|
||||
ref->refCount++;
|
||||
}
|
||||
|
||||
void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) {
|
||||
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) {
|
||||
decrementRefcount((void*) bitmapResource);
|
||||
Mutex::Autolock _l(mLock);
|
||||
decrementRefcountLocked(bitmapResource);
|
||||
}
|
||||
|
||||
void ResourceCache::decrementRefcount(const SkPath* pathResource) {
|
||||
@@ -130,7 +129,20 @@ void ResourceCache::decrementRefcountLocked(void* resource) {
|
||||
}
|
||||
|
||||
void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
|
||||
decrementRefcountLocked((void*) bitmapResource);
|
||||
BitmapKey bitmapKey(bitmapResource);
|
||||
ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
|
||||
|
||||
LOG_ALWAYS_FATAL_IF(index == NAME_NOT_FOUND,
|
||||
"Decrementing the reference of an untracked Bitmap");
|
||||
|
||||
const BitmapKey& cacheEntry = mBitmapCache.keyAt(index);
|
||||
if (cacheEntry.mRefCount == 1) {
|
||||
// delete the bitmap and remove it from the cache
|
||||
delete mBitmapCache.valueAt(index);
|
||||
mBitmapCache.removeItemsAt(index);
|
||||
} else {
|
||||
cacheEntry.mRefCount--;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) {
|
||||
@@ -164,28 +176,6 @@ void ResourceCache::destructorLocked(SkPath* resource) {
|
||||
}
|
||||
}
|
||||
|
||||
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) : nullptr;
|
||||
if (ref == nullptr) {
|
||||
// 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);
|
||||
@@ -211,65 +201,13 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(nullptr, nullptr);
|
||||
return true;
|
||||
}
|
||||
ResourceReference* ref = mCache->valueAt(index);
|
||||
if (ref == nullptr) {
|
||||
// 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(nullptr, nullptr);
|
||||
}
|
||||
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()) {
|
||||
@@ -296,5 +234,38 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource
|
||||
delete ref;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Bitmap Key
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BitmapKey::operator=(const BitmapKey& other) {
|
||||
this->mRefCount = other.mRefCount;
|
||||
this->mBitmapDimensions = other.mBitmapDimensions;
|
||||
this->mPixelRefOrigin = other.mPixelRefOrigin;
|
||||
this->mPixelRefStableID = other.mPixelRefStableID;
|
||||
}
|
||||
|
||||
bool BitmapKey::operator==(const BitmapKey& other) const {
|
||||
return mPixelRefStableID == other.mPixelRefStableID &&
|
||||
mPixelRefOrigin == other.mPixelRefOrigin &&
|
||||
mBitmapDimensions == other.mBitmapDimensions;
|
||||
}
|
||||
|
||||
bool BitmapKey::operator<(const BitmapKey& other) const {
|
||||
if (mPixelRefStableID != other.mPixelRefStableID) {
|
||||
return mPixelRefStableID < other.mPixelRefStableID;
|
||||
}
|
||||
if (mPixelRefOrigin.x() != other.mPixelRefOrigin.x()) {
|
||||
return mPixelRefOrigin.x() < other.mPixelRefOrigin.x();
|
||||
}
|
||||
if (mPixelRefOrigin.y() != other.mPixelRefOrigin.y()) {
|
||||
return mPixelRefOrigin.y() < other.mPixelRefOrigin.y();
|
||||
}
|
||||
if (mBitmapDimensions.width() != other.mBitmapDimensions.width()) {
|
||||
return mBitmapDimensions.width() < other.mBitmapDimensions.width();
|
||||
}
|
||||
return mBitmapDimensions.height() < other.mBitmapDimensions.height();
|
||||
}
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#include <SkPath.h>
|
||||
#include <SkPixelRef.h>
|
||||
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/Singleton.h>
|
||||
@@ -36,7 +37,6 @@ class Layer;
|
||||
* Type of Resource being cached
|
||||
*/
|
||||
enum ResourceType {
|
||||
kBitmap,
|
||||
kNinePatch,
|
||||
kPath
|
||||
};
|
||||
@@ -45,15 +45,45 @@ class ResourceReference {
|
||||
public:
|
||||
|
||||
ResourceReference(ResourceType type) {
|
||||
refCount = 0; recycled = false; destroyed = false; resourceType = type;
|
||||
refCount = 0; destroyed = false; resourceType = type;
|
||||
}
|
||||
|
||||
int refCount;
|
||||
bool recycled;
|
||||
bool destroyed;
|
||||
ResourceType resourceType;
|
||||
};
|
||||
|
||||
class BitmapKey {
|
||||
public:
|
||||
BitmapKey(const SkBitmap* bitmap)
|
||||
: mRefCount(1)
|
||||
, mBitmapDimensions(bitmap->dimensions())
|
||||
, mPixelRefOrigin(bitmap->pixelRefOrigin())
|
||||
, mPixelRefStableID(bitmap->pixelRef()->getStableID()) { }
|
||||
|
||||
void operator=(const BitmapKey& other);
|
||||
bool operator==(const BitmapKey& other) const;
|
||||
bool operator<(const BitmapKey& other) const;
|
||||
|
||||
private:
|
||||
// This constructor is only used by the KeyedVector implementation
|
||||
BitmapKey()
|
||||
: mRefCount(-1)
|
||||
, mBitmapDimensions(SkISize::Make(0,0))
|
||||
, mPixelRefOrigin(SkIPoint::Make(0,0))
|
||||
, mPixelRefStableID(0) { }
|
||||
|
||||
// reference count of all HWUI object using this bitmap
|
||||
mutable int mRefCount;
|
||||
|
||||
SkISize mBitmapDimensions;
|
||||
SkIPoint mPixelRefOrigin;
|
||||
uint32_t mPixelRefStableID;
|
||||
|
||||
friend class ResourceCache;
|
||||
friend class android::key_value_pair_t<BitmapKey, SkBitmap*>;
|
||||
};
|
||||
|
||||
class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
|
||||
ResourceCache();
|
||||
~ResourceCache();
|
||||
@@ -69,13 +99,14 @@ public:
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
void incrementRefcount(const SkPath* resource);
|
||||
void incrementRefcount(const SkBitmap* resource);
|
||||
void incrementRefcount(const Res_png_9patch* resource);
|
||||
/**
|
||||
* The cache stores a copy of the provided resource or refs an existing resource
|
||||
* if the bitmap has previously been inserted and returns the cached copy.
|
||||
*/
|
||||
const SkBitmap* insert(const SkBitmap* resource);
|
||||
|
||||
void incrementRefcountLocked(const SkPath* resource);
|
||||
void incrementRefcountLocked(const SkBitmap* resource);
|
||||
void incrementRefcountLocked(const Res_png_9patch* resource);
|
||||
void incrementRefcount(const SkPath* resource);
|
||||
void incrementRefcount(const Res_png_9patch* resource);
|
||||
|
||||
void decrementRefcount(const SkBitmap* resource);
|
||||
void decrementRefcount(const SkPath* resource);
|
||||
@@ -86,16 +117,11 @@ public:
|
||||
void decrementRefcountLocked(const Res_png_9patch* resource);
|
||||
|
||||
void destructor(SkPath* resource);
|
||||
void destructor(const SkBitmap* resource);
|
||||
void destructor(Res_png_9patch* resource);
|
||||
|
||||
void destructorLocked(SkPath* resource);
|
||||
void destructorLocked(const SkBitmap* resource);
|
||||
void destructorLocked(Res_png_9patch* resource);
|
||||
|
||||
bool recycle(SkBitmap* resource);
|
||||
bool recycleLocked(SkBitmap* resource);
|
||||
|
||||
private:
|
||||
void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref);
|
||||
|
||||
@@ -115,6 +141,7 @@ private:
|
||||
mutable Mutex mLock;
|
||||
|
||||
KeyedVector<const void*, ResourceReference*>* mCache;
|
||||
KeyedVector<BitmapKey, SkBitmap*> mBitmapCache;
|
||||
};
|
||||
|
||||
}; // namespace uirenderer
|
||||
|
||||
@@ -233,11 +233,9 @@ Texture* TextureCache::getTransient(const SkBitmap* bitmap) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
void TextureCache::releaseTexture(const SkBitmap* bitmap) {
|
||||
if (!bitmap || !bitmap->pixelRef()) return;
|
||||
|
||||
void TextureCache::releaseTexture(uint32_t pixelRefStableID) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
mGarbage.push(bitmap->pixelRef()->getStableID());
|
||||
mGarbage.push(pixelRefStableID);
|
||||
}
|
||||
|
||||
void TextureCache::clearGarbage() {
|
||||
|
||||
@@ -88,10 +88,10 @@ public:
|
||||
Texture* getTransient(const SkBitmap* bitmap);
|
||||
|
||||
/**
|
||||
* Removes the texture associated with the specified bitmap. This is meant
|
||||
* Removes the texture associated with the specified pixelRef. This is meant
|
||||
* to be called from threads that are not the EGL context thread.
|
||||
*/
|
||||
void releaseTexture(const SkBitmap* bitmap);
|
||||
ANDROID_API void releaseTexture(uint32_t pixelRefStableID);
|
||||
/**
|
||||
* Process deferred removals.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user