Fix issue with bitmap uploading

Bug: 13912749

Change-Id: Ic23fa1d280118dc93dc2716a4a24cc0bbbdca595
This commit is contained in:
John Reck
2014-04-11 19:15:05 -07:00
parent db8b130a19
commit 860d155f86
14 changed files with 131 additions and 66 deletions

View File

@@ -146,8 +146,8 @@ static void android_view_GLRenderer_prepareTree(JNIEnv* env, jobject clazz,
jlong renderNodePtr) {
using namespace android::uirenderer;
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
TreeInfo info = {0};
renderNode->prepareTree(info);
TreeInfo ignoredInfo;
renderNode->prepareTree(ignoredInfo);
}
static void android_view_GLRenderer_invokeFunctor(JNIEnv* env, jobject clazz,

View File

@@ -127,8 +127,8 @@ static void android_view_HardwareLayer_updateRenderLayer(JNIEnv* env, jobject cl
static jboolean android_view_HardwareLayer_flushChanges(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
bool ignoredHasFunctors;
return layer->apply(&ignoredHasFunctors);
TreeInfo ignoredInfo;
return layer->apply(ignoredInfo);
}
static jlong android_view_HardwareLayer_getLayer(JNIEnv* env, jobject clazz,

View File

@@ -63,7 +63,7 @@ void DeferredLayerUpdater::setDisplayList(RenderNode* displayList,
}
}
bool DeferredLayerUpdater::apply(bool* hasFunctors) {
bool DeferredLayerUpdater::apply(TreeInfo& info) {
bool success = true;
// These properties are applied the same to both layer types
mLayer->setColorFilter(mColorFilter);
@@ -74,11 +74,7 @@ bool DeferredLayerUpdater::apply(bool* hasFunctors) {
success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight);
}
mLayer->setBlend(mBlend);
TreeInfo info = {0};
mDisplayList->prepareTree(info);
if (info.hasFunctors) {
*hasFunctors = true;
}
mLayer->updateDeferred(mDisplayList.get(),
mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom);
mDirtyRect.setEmpty();

View File

@@ -77,7 +77,7 @@ public:
ANDROID_API void setPaint(const SkPaint* paint);
ANDROID_API bool apply(bool* hasFunctors);
ANDROID_API bool apply(TreeInfo& info);
ANDROID_API Layer* backingLayer() {
return mLayer;

View File

@@ -286,12 +286,6 @@ public:
int getFlags() const { return mFlags; }
private:
SaveOp() {}
DisplayListOp* reinit(int flags) {
mFlags = flags;
return this;
}
int mFlags;
};
@@ -318,12 +312,6 @@ public:
virtual const char* name() { return "RestoreToCount"; }
private:
RestoreToCountOp() {}
DisplayListOp* reinit(int count) {
mCount = count;
return this;
}
int mCount;
};
@@ -514,7 +502,6 @@ public:
}
protected:
ClipOp() {}
virtual bool isRect() { return false; }
SkRegion::Op mOp;
@@ -539,13 +526,6 @@ protected:
virtual bool isRect() { return true; }
private:
ClipRectOp() {}
DisplayListOp* reinit(float left, float top, float right, float bottom, SkRegion::Op op) {
mOp = op;
mArea.set(left, top, right, bottom);
return this;
}
Rect mArea;
};

View File

@@ -109,7 +109,7 @@ void RenderNode::pushStagingChanges(TreeInfo& info) {
mNeedsDisplayListDataSync = false;
// Do a push pass on the old tree to handle freeing DisplayListData
// that are no longer used
TreeInfo oldTreeInfo = {0};
TreeInfo oldTreeInfo;
prepareSubTree(oldTreeInfo, mDisplayListData);
// TODO: The damage for the old tree should be accounted for
delete mDisplayListData;
@@ -120,8 +120,15 @@ void RenderNode::pushStagingChanges(TreeInfo& info) {
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
if (subtree) {
if (!info.hasFunctors) {
info.hasFunctors = subtree->functorCount;
TextureCache& cache = Caches::getInstance().textureCache;
info.hasFunctors |= subtree->functorCount;
// 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]);
}
for (size_t i = 0; i < subtree->children().size(); i++) {
RenderNode* childNode = subtree->children()[i]->mDisplayList;

View File

@@ -66,7 +66,13 @@ class RestoreToCountOp;
class DrawDisplayListOp;
struct TreeInfo {
TreeInfo()
: hasFunctors(false)
, prepareTextures(false)
{}
bool hasFunctors;
bool prepareTextures;
// TODO: Damage calculations? Flag to skip staging pushes for RT animations?
};

View File

@@ -25,14 +25,14 @@ namespace android {
namespace uirenderer {
Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false),
mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
}
Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false),
mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
mFirstFilter(true), mFirstWrap(true), mCaches(caches) {

View File

@@ -94,6 +94,12 @@ public:
*/
const UvMapper* uvMapper;
/**
* Whether or not the Texture is marked in use and thus not evictable for
* the current frame. This is reset at the start of a new frame.
*/
bool isInUse;
private:
/**
* Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE.

View File

@@ -121,29 +121,49 @@ void TextureCache::operator()(const SkBitmap*&, Texture*& texture) {
// Caching
///////////////////////////////////////////////////////////////////////////////
Texture* TextureCache::get(const SkBitmap* bitmap) {
void TextureCache::resetMarkInUse() {
LruCache<const SkBitmap*, Texture*>::Iterator iter(mCache);
while (iter.next()) {
iter.value()->isInUse = false;
}
}
bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) {
if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
return false;
}
return true;
}
// Returns a prepared Texture* that either is already in the cache or can fit
// in the cache (and is thus added to the cache)
Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
Texture* texture = mCache.get(bitmap);
if (!texture) {
if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
if (!canMakeTextureFromBitmap(bitmap)) {
return NULL;
}
const uint32_t size = bitmap->rowBytes() * bitmap->height();
bool canCache = size < mMaxSize;
// Don't even try to cache a bitmap that's bigger than the cache
if (size < mMaxSize) {
while (mSize + size > mMaxSize) {
while (canCache && mSize + size > mMaxSize) {
Texture* oldest = mCache.peekOldestValue();
if (oldest && !oldest->isInUse) {
mCache.removeOldest();
} else {
canCache = false;
}
}
texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
if (canCache) {
texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
if (size < mMaxSize) {
mSize += size;
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
bitmap, texture->id, size, mSize);
@@ -151,16 +171,42 @@ Texture* TextureCache::get(const SkBitmap* bitmap) {
ALOGD("Texture created, size = %d", size);
}
mCache.put(bitmap, texture);
} else {
texture->cleanup = true;
}
} else if (bitmap->getGenerationID() != texture->generation) {
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
// TODO: Re-adjust the cache size if the bitmap's dimensions have changed
generateTexture(bitmap, texture, true);
}
return texture;
}
bool TextureCache::prefetchAndMarkInUse(const SkBitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap);
if (texture) {
texture->isInUse = true;
}
return texture;
}
Texture* TextureCache::get(const SkBitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap);
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
return NULL;
}
const uint32_t size = bitmap->rowBytes() * bitmap->height();
texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
texture->cleanup = true;
}
return texture;
}
Texture* TextureCache::getTransient(const SkBitmap* bitmap) {
Texture* texture = new Texture();
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();

View File

@@ -61,6 +61,18 @@ public:
*/
void operator()(const SkBitmap*& bitmap, Texture*& texture);
/**
* Resets all Textures to not be marked as in use
*/
void resetMarkInUse();
/**
* Attempts to precache the SkBitmap. Returns true if a Texture was successfully
* acquired for the bitmap, false otherwise. If a Texture was acquired it is
* marked as in use.
*/
bool prefetchAndMarkInUse(const SkBitmap* bitmap);
/**
* Returns the texture associated with the specified bitmap. If the texture
* cannot be found in the cache, a new texture is generated.
@@ -116,6 +128,11 @@ public:
void setFlushRate(float flushRate);
private:
bool canMakeTextureFromBitmap(const SkBitmap* bitmap);
Texture* getCachedTexture(const SkBitmap* bitmap);
/**
* Generates the texture from a bitmap into the specified texture structure.
*

View File

@@ -382,13 +382,18 @@ void CanvasContext::setup(int width, int height) {
mCanvas->setViewport(width, height);
}
void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
bool* hasFunctors) {
LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot process layer updates without a canvas!");
void CanvasContext::makeCurrent() {
mGlobalContext->makeCurrent(mEglSurface);
}
void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
TreeInfo& info) {
LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot process layer updates without a canvas!");
makeCurrent();
for (size_t i = 0; i < layerUpdaters->size(); i++) {
DeferredLayerUpdater* update = layerUpdaters->itemAt(i);
LOG_ALWAYS_FATAL_IF(!update->apply(hasFunctors), "Failed to update layer!");
bool success = update->apply(info);
LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
if (update->backingLayer()->deferredUpdateScheduled) {
mCanvas->pushLayerUpdate(update->backingLayer());
}
@@ -444,8 +449,8 @@ void CanvasContext::invokeFunctor(Functor* functor) {
bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
requireGlContext();
bool hasFunctors;
layer->apply(&hasFunctors);
TreeInfo info;
layer->apply(info);
return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
}

View File

@@ -23,6 +23,7 @@
#include <utils/Functor.h>
#include <utils/Vector.h>
#include "../RenderNode.h"
#include "RenderTask.h"
#define FUNCTOR_PROCESS_DELAY 4
@@ -31,8 +32,6 @@ namespace android {
namespace uirenderer {
class DeferredLayerUpdater;
class RenderNode;
class DisplayListData;
class OpenGLRenderer;
class Rect;
class Layer;
@@ -54,7 +53,8 @@ public:
void updateSurface(EGLNativeWindowType window);
void pauseSurface(EGLNativeWindowType window);
void setup(int width, int height);
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, bool* hasFunctors);
void makeCurrent();
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
void drawDisplayList(RenderNode* displayList, Rect* dirty);
void destroyCanvas();

View File

@@ -85,7 +85,6 @@ void DrawFrameTask::postAndWait(RenderThread* renderThread) {
void DrawFrameTask::run() {
ATRACE_NAME("DrawFrame");
// canUnblockUiThread is temporary until WebView has a solution for syncing frame state
bool canUnblockUiThread = syncFrameState();
// Grab a copy of everything we need
@@ -105,17 +104,20 @@ void DrawFrameTask::run() {
}
}
static void prepareTreeInfo(TreeInfo& info) {
info.prepareTextures = true;
}
bool DrawFrameTask::syncFrameState() {
ATRACE_CALL();
bool hasFunctors = false;
mContext->processLayerUpdates(&mLayers, &hasFunctors);
TreeInfo info = {0};
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
TreeInfo info;
prepareTreeInfo(info);
mContext->processLayerUpdates(&mLayers, info);
mRenderNode->prepareTree(info);
hasFunctors |= info.hasFunctors;
return !hasFunctors;
// If prepareTextures is false, we ran out of texture cache space
return !info.hasFunctors && info.prepareTextures;
}
void DrawFrameTask::unblockUiThread() {