Have destroy call freePrefetchedLayers

Bug: 17208461

 There's a potential race condition between HardwareRenderer.destroy()
 being called (which calls destroyCanvasAndSurface()) and the renderer
 being finalized (which is what calls freePrefetchedLayers), during which
 time it's possible we get a TRIM_MEMORY_COMPLETE and destroy the EGL
 context.

 Fix this race condition by moving stopDrawing() and freePrefetchedLayers()
 into destroyCanvasAndSurface() where they should have been in the first
 place.

 Also, if we hit the assertion failure, dump the current state of
 Caches to try and provide more context for the failure.

Change-Id: Ife0ba3562041e8b08e87e3e13640472b3004eed6
This commit is contained in:
John Reck
2014-09-03 07:39:53 -07:00
parent 6e31e0f3d1
commit 17035b0211
12 changed files with 40 additions and 19 deletions

View File

@@ -126,7 +126,7 @@ public class ThreadedRenderer extends HardwareRenderer {
void destroy() {
mInitialized = false;
updateEnabledState(null);
nDestroyCanvasAndSurface(mNativeProxy);
nDestroy(mNativeProxy);
}
private void updateEnabledState(Surface surface) {
@@ -488,7 +488,7 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native void nSetOpaque(long nativeProxy, boolean opaque);
private static native int nSyncAndDrawFrame(long nativeProxy,
long frameTimeNanos, long recordDuration, float density);
private static native void nDestroyCanvasAndSurface(long nativeProxy);
private static native void nDestroy(long nativeProxy);
private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
private static native void nInvokeFunctor(long functor, boolean waitForCompletion);

View File

@@ -291,10 +291,10 @@ static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject c
return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density);
}
static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
jlong proxyPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
proxy->destroyCanvasAndSurface();
proxy->destroy();
}
static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz,
@@ -430,7 +430,7 @@ static JNINativeMethod gMethods[] = {
{ "nSetup", "(JIIFFFFII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
{ "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
{ "nDestroy", "(J)V", (void*) android_view_ThreadedRenderer_destroy },
{ "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
{ "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
{ "nCreateDisplayListLayer", "(JII)J", (void*) android_view_ThreadedRenderer_createDisplayListLayer },

View File

@@ -24,6 +24,7 @@
#include "Properties.h"
#include "LayerRenderer.h"
#include "ShadowTessellator.h"
#include "RenderState.h"
namespace android {
@@ -49,7 +50,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
Caches::Caches(): Singleton<Caches>(),
mExtensions(Extensions::getInstance()), mInitialized(false) {
mExtensions(Extensions::getInstance()), mInitialized(false), mRenderState(NULL) {
init();
initFont();
initConstraints();
@@ -267,8 +268,11 @@ void Caches::dumpMemoryUsage(String8 &log) {
log.appendFormat("Current memory usage / total memory usage (bytes):\n");
log.appendFormat(" TextureCache %8d / %8d\n",
textureCache.getSize(), textureCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8d\n",
layerCache.getSize(), layerCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8d (numLayers = %zu)\n",
layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount());
log.appendFormat(" Garbage layers %8zu\n", mLayerGarbage.size());
log.appendFormat(" Active layers %8zu\n",
mRenderState ? mRenderState->mActiveLayers.size() : 0);
log.appendFormat(" RenderBufferCache %8d / %8d\n",
renderBufferCache.getSize(), renderBufferCache.getMaxSize());
log.appendFormat(" GradientCache %8d / %8d\n",

View File

@@ -107,6 +107,7 @@ struct CacheLogger {
///////////////////////////////////////////////////////////////////////////////
class RenderNode;
class RenderState;
class ANDROID_API Caches: public Singleton<Caches> {
Caches();
@@ -132,6 +133,8 @@ public:
*/
bool initProperties();
void setRenderState(RenderState* renderState) { mRenderState = renderState; }
/**
* Flush the cache.
*
@@ -431,6 +434,8 @@ private:
GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
OverdrawColorSet mOverdrawDebugColorSet;
RenderState* mRenderState;
}; // class Caches
}; // namespace uirenderer

View File

@@ -49,6 +49,10 @@ LayerCache::~LayerCache() {
// Size management
///////////////////////////////////////////////////////////////////////////////
size_t LayerCache::getCount() {
return mCache.size();
}
uint32_t LayerCache::getSize() {
return mSize;
}

View File

@@ -87,6 +87,8 @@ public:
*/
uint32_t getSize();
size_t getCount();
/**
* Prints out the content of the cache.
*/

View File

@@ -32,10 +32,14 @@ void RenderState::onGLContextCreated() {
// This is delayed because the first access of Caches makes GL calls
mCaches = &Caches::getInstance();
mCaches->init();
mCaches->setRenderState(this);
}
void RenderState::onGLContextDestroyed() {
LOG_ALWAYS_FATAL_IF(!mActiveLayers.empty(), "layers have survived gl context destruction");
if (CC_UNLIKELY(!mActiveLayers.empty())) {
mCaches->dumpMemoryUsage();
LOG_ALWAYS_FATAL("layers have survived gl context destruction");
}
}
void RenderState::setViewport(GLsizei width, GLsizei height) {

View File

@@ -59,6 +59,7 @@ public:
private:
friend class renderthread::RenderThread;
friend class Caches;
void interruptForFunctorInvoke();
void resumeFromFunctorInvoke();

View File

@@ -51,13 +51,14 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
}
CanvasContext::~CanvasContext() {
destroyCanvasAndSurface();
mRenderThread.removeFrameCallback(this);
destroy();
delete mAnimationContext;
freePrefetechedLayers();
}
void CanvasContext::destroyCanvasAndSurface() {
void CanvasContext::destroy() {
stopDrawing();
freePrefetechedLayers();
destroyHardwareResources();
if (mCanvas) {
delete mCanvas;
mCanvas = 0;

View File

@@ -66,7 +66,7 @@ public:
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
void prepareTree(TreeInfo& info);
void draw();
void destroyCanvasAndSurface();
void destroy();
// IFrameCallback, Chroreographer-driven frame callback entry point
virtual void doFrame();

View File

@@ -194,13 +194,13 @@ int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDuration
return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
}
CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
args->context->destroyCanvasAndSurface();
CREATE_BRIDGE1(destroy, CanvasContext* context) {
args->context->destroy();
return NULL;
}
void RenderProxy::destroyCanvasAndSurface() {
SETUP_TASK(destroyCanvasAndSurface);
void RenderProxy::destroy() {
SETUP_TASK(destroy);
args->context = mContext;
// destroyCanvasAndSurface() needs a fence as when it returns the
// underlying BufferQueue is going to be released from under

View File

@@ -73,7 +73,7 @@ public:
ANDROID_API void setOpaque(bool opaque);
ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
float density);
ANDROID_API void destroyCanvasAndSurface();
ANDROID_API void destroy();
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);