From 6a2d17f71342f981c9df1dc5beff33e30eb3ae2b Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Sun, 30 Sep 2012 12:14:13 -0700 Subject: [PATCH] Fix texture corruption When memory gets low on a device, activities flush everything they can. Hardware-accelerated activites, such as Launcher, flush GL resources and destroy the GL context. However, some resources were still hanging around, due to deferred destruction policies (we don't delete layers until the DisplayLists they are in are finalized, to ensure we don't deref deleted objects). This meant that we were referring to obsolete GL data in these objects. in particular, it meant that we might come around later, after a new GL context was created, and delete a texture object that was incorrect. We use the layer's "texture id" to refer to the texture underlying the layer. But if there's a new GL context, then this texture ID is no longer valid, and we may be deleting the texture that a different object (layer, icon, whatever) is referring to, because the driver may return that same ID under the new GL context. The fix is to more aggressively delete things that we know will not be used again when the GL context is destroyed. In particular, we delete all resources being used by all DisplayLists at GL context destruction time. Issue #7195815 Textures corruption on all devices, in many apps Change-Id: I52d2d208173690dbb794a83402d38f14ea4c6c22 --- core/java/android/view/DisplayList.java | 7 +++ core/java/android/view/GLES20DisplayList.java | 8 +++ core/java/android/view/HardwareRenderer.java | 6 ++ core/java/android/view/View.java | 3 + core/jni/android/graphics/Canvas.cpp | 3 + core/jni/android_view_GLES20DisplayList.cpp | 6 ++ libs/hwui/Caches.cpp | 4 +- libs/hwui/DisplayListRenderer.cpp | 63 ++++++++++--------- libs/hwui/DisplayListRenderer.h | 3 +- 9 files changed, 69 insertions(+), 34 deletions(-) diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index a42e15677c39d..5e34a36bb52da 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -332,4 +332,11 @@ public abstract class DisplayList { * @see View#offsetTopAndBottom(int) */ public abstract void offsetTopBottom(int offset); + + /** + * Reset native resources. This is called when cleaning up the state of DisplayLists + * during destruction of hardware resources, to ensure that we do not hold onto + * obsolete resources after related resources are gone. + */ + public abstract void reset(); } diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index 3bdd5c054c3c1..e9bd0c43976de 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -86,6 +86,13 @@ class GLES20DisplayList extends DisplayList { } } + @Override + public void reset() { + if (hasNativeDisplayList()) { + nReset(mFinalizer.mNativeDisplayList); + } + } + @Override public boolean isValid() { return mValid; @@ -294,6 +301,7 @@ class GLES20DisplayList extends DisplayList { } } + private static native void nReset(int displayList); private static native void nOffsetTopBottom(int displayList, int offset); private static native void nOffsetLeftRight(int displayList, int offset); private static native void nSetLeftTopRightBottom(int displayList, int left, int top, diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 28763b3c949d9..99987bfd27932 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1508,6 +1508,9 @@ public abstract class HardwareRenderer { @Override void destroyLayers(View view) { if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) { + if (mCanvas != null) { + mCanvas.clearLayerUpdates(); + } destroyHardwareLayer(view); GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); } @@ -1556,6 +1559,9 @@ public abstract class HardwareRenderer { safelyRun(new Runnable() { @Override public void run() { + if (mCanvas != null) { + mCanvas.clearLayerUpdates(); + } destroyResources(view); GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6eca8fd93ca7a..4b6f4ead0e34d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12394,6 +12394,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mHardwareLayer.destroy(); mHardwareLayer = null; + if (mDisplayList != null) { + mDisplayList.reset(); + } invalidate(true); invalidateParentCaches(); } diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 5c276024fcf2c..2a02f7c82eba8 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -931,6 +931,9 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l SkIRect ir; bool result = canvas->getClipBounds(&r, SkCanvas::kBW_EdgeType); + if (!result) { + r.setEmpty(); + } r.round(&ir); (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); return result; diff --git a/core/jni/android_view_GLES20DisplayList.cpp b/core/jni/android_view_GLES20DisplayList.cpp index b307a2f9d8f90..c5f52df5ad4e5 100644 --- a/core/jni/android_view_GLES20DisplayList.cpp +++ b/core/jni/android_view_GLES20DisplayList.cpp @@ -36,6 +36,11 @@ using namespace uirenderer; */ #ifdef USE_OPENGL_RENDERER +static void android_view_GLES20DisplayList_reset(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + displayList->reset(); +} + // ---------------------------------------------------------------------------- // DisplayList view properties // ---------------------------------------------------------------------------- @@ -185,6 +190,7 @@ const char* const kClassPathName = "android/view/GLES20DisplayList"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER + { "nReset", "(I)V", (void*) android_view_GLES20DisplayList_reset }, { "nSetCaching", "(IZ)V", (void*) android_view_GLES20DisplayList_setCaching }, { "nSetStaticMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setStaticMatrix }, { "nSetAnimationMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setAnimationMatrix }, diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index e44329404190b..7853ae4168b98 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -268,8 +268,6 @@ void Caches::deleteDisplayListDeferred(DisplayList* displayList) { void Caches::flush(FlushMode mode) { FLUSH_LOGD("Flushing caches (mode %d)", mode); - clearGarbage(); - switch (mode) { case kFlushMode_Full: textureCache.clear(); @@ -293,6 +291,8 @@ void Caches::flush(FlushMode mode) { layerCache.clear(); break; } + + clearGarbage(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index f84c84735daf3..589d5c2eb9855 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -105,35 +105,6 @@ DisplayList::~DisplayList() { clearResources(); } -void DisplayList::initProperties() { - mLeft = 0; - mTop = 0; - mRight = 0; - mBottom = 0; - mClipChildren = true; - mAlpha = 1; - mMultipliedAlpha = 255; - mHasOverlappingRendering = true; - mTranslationX = 0; - mTranslationY = 0; - mRotation = 0; - mRotationX = 0; - mRotationY= 0; - mScaleX = 1; - mScaleY = 1; - mPivotX = 0; - mPivotY = 0; - mCameraDistance = 0; - mMatrixDirty = false; - mMatrixFlags = 0; - mPrevWidth = -1; - mPrevHeight = -1; - mWidth = 0; - mHeight = 0; - mPivotExplicitlySet = false; - mCaching = false; -} - void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { if (displayList) { DISPLAY_LIST_LOGD("Deferring display list destruction"); @@ -215,8 +186,12 @@ void DisplayList::clearResources() { mLayers.clear(); } +void DisplayList::reset() { + clearResources(); + init(); +} + void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) { - const SkWriter32& writer = recorder.writeStream(); if (reusing) { // re-using display list - clear out previous allocations @@ -224,8 +199,8 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde } init(); - initProperties(); + const SkWriter32& writer = recorder.writeStream(); if (writer.size() == 0) { return; } @@ -303,6 +278,32 @@ void DisplayList::init() { mSize = 0; mIsRenderable = true; mFunctorCount = 0; + mLeft = 0; + mTop = 0; + mRight = 0; + mBottom = 0; + mClipChildren = true; + mAlpha = 1; + mMultipliedAlpha = 255; + mHasOverlappingRendering = true; + mTranslationX = 0; + mTranslationY = 0; + mRotation = 0; + mRotationX = 0; + mRotationY= 0; + mScaleX = 1; + mScaleY = 1; + mPivotX = 0; + mPivotY = 0; + mCameraDistance = 0; + mMatrixDirty = false; + mMatrixFlags = 0; + mPrevWidth = -1; + mPrevHeight = -1; + mWidth = 0; + mHeight = 0; + mPivotExplicitlySet = false; + mCaching = false; } size_t DisplayList::getSize() { diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 39061f4c6c718..2610055ecd064 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -137,6 +137,8 @@ public: void output(OpenGLRenderer& renderer, uint32_t level = 0); + ANDROID_API void reset(); + void setRenderable(bool renderable) { mIsRenderable = renderable; } @@ -399,7 +401,6 @@ public: private: void init(); - void initProperties(); void clearResources();