Fix issue with bitmap uploading
Bug: 13912749 Change-Id: Ic23fa1d280118dc93dc2716a4a24cc0bbbdca595
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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?
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user