From 65b345fa22b878e141b8fd8ece9c208df00fa40f Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 27 Jul 2011 18:51:50 -0700 Subject: Reclaim more memory, more often. Yay. Change-Id: I04557ad575c307a55088549f48f0e9ad994b7275 --- core/java/android/app/ActivityThread.java | 120 ++++++++++++++------------ core/java/android/view/DisplayList.java | 7 ++ core/java/android/view/GLES20Canvas.java | 6 ++ core/java/android/view/GLES20DisplayList.java | 6 ++ core/java/android/view/HardwareRenderer.java | 4 +- core/java/android/view/View.java | 6 +- core/java/android/view/ViewDebug.java | 2 +- core/java/android/view/ViewRootImpl.java | 53 ++++++++++-- core/java/android/view/WindowManagerImpl.java | 58 ++++++++++++- core/jni/android_view_GLES20Canvas.cpp | 9 +- libs/hwui/DisplayListRenderer.cpp | 22 +++-- libs/hwui/DisplayListRenderer.h | 4 + libs/hwui/LayerCache.cpp | 1 + libs/hwui/LayerRenderer.cpp | 2 +- libs/hwui/Properties.h | 2 +- 15 files changed, 224 insertions(+), 78 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9bbbd6c76753..1e93f8835eaa 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -123,7 +123,6 @@ public final class ActivityThread { /** @hide */ public static final String TAG = "ActivityThread"; private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565; - private static final boolean DEBUG = false; static final boolean localLOGV = false; static final boolean DEBUG_MESSAGES = false; /** @hide */ @@ -163,7 +162,7 @@ public final class ActivityThread { = new ArrayList(); // set of instantiated backup agents, keyed by package name final HashMap mBackupAgents = new HashMap(); - static final ThreadLocal sThreadLocal = new ThreadLocal(); + static final ThreadLocal sThreadLocal = new ThreadLocal(); Instrumentation mInstrumentation; String mInstrumentationAppDir = null; String mInstrumentationAppPackage = null; @@ -410,9 +409,9 @@ public final class ActivityThread { CompatibilityInfo info; } - native private void dumpGraphicsInfo(FileDescriptor fd); + private native void dumpGraphicsInfo(FileDescriptor fd); - private final class ApplicationThread extends ApplicationThreadNative { + private class ApplicationThread extends ApplicationThreadNative { private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%21s %8d"; private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; @@ -734,13 +733,13 @@ public final class ActivityThread { FileOutputStream fout = new FileOutputStream(fd); PrintWriter pw = new PrintWriter(fout); try { - return dumpMemInfo(fd, pw, args); + return dumpMemInfo(pw, args); } finally { pw.flush(); } } - private Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, PrintWriter pw, String[] args) { + private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, String[] args) { long nativeMax = Debug.getNativeHeapSize() / 1024; long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; long nativeFree = Debug.getNativeHeapFreeSize() / 1024; @@ -754,7 +753,7 @@ public final class ActivityThread { long dalvikFree = runtime.freeMemory() / 1024; long dalvikAllocated = dalvikMax - dalvikFree; long viewInstanceCount = ViewDebug.getViewInstanceCount(); - long viewRootInstanceCount = ViewDebug.getViewAncestorInstanceCount(); + long viewRootInstanceCount = ViewDebug.getViewRootImplCount(); long appContextInstanceCount = Debug.countInstancesOfClass(ContextImpl.class); long activityInstanceCount = Debug.countInstancesOfClass(Activity.class); int globalAssetCount = AssetManager.getGlobalAssetCount(); @@ -868,7 +867,7 @@ public final class ActivityThread { int otherPrivateDirty = memInfo.otherPrivateDirty; for (int i=0; i intents) { final int N = intents.size(); for (int i=0; i results) { + private void deliverResults(ActivityClientRecord r, List results) { final int N = results.size(); for (int i=0; i providers) { final ArrayList results = new ArrayList(); @@ -3825,7 +3831,7 @@ public final class ActivityThread { } } - private final IContentProvider getExistingProvider(Context context, String name) { + private IContentProvider getExistingProvider(Context context, String name) { synchronized(mProviderMap) { final ProviderClientRecord pr = mProviderMap.get(name); if (pr != null) { @@ -3835,7 +3841,7 @@ public final class ActivityThread { } } - private final IContentProvider getProvider(Context context, String name) { + private IContentProvider getProvider(Context context, String name) { IContentProvider existing = getExistingProvider(context, name); if (existing != null) { return existing; @@ -4007,7 +4013,7 @@ public final class ActivityThread { } } - private final IContentProvider installProvider(Context context, + private IContentProvider installProvider(Context context, IContentProvider provider, ProviderInfo info, boolean noisy) { ContentProvider localProvider = null; if (provider == null) { @@ -4027,6 +4033,7 @@ public final class ActivityThread { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { + // Ignore } } if (c == null) { @@ -4086,7 +4093,7 @@ public final class ActivityThread { return provider; } - private final void attach(boolean system) { + private void attach(boolean system) { sThreadLocal.set(this); mSystemThread = system; if (!system) { @@ -4101,6 +4108,7 @@ public final class ActivityThread { try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { + // Ignore } } else { // Don't set application object here -- if the system crashes, @@ -4166,7 +4174,7 @@ public final class ActivityThread { } } - public static final void main(String[] args) { + public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index f4c0249ab5a9..8f4ece04b113 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -54,4 +54,11 @@ public abstract class DisplayList { * @return boolean true if the display list is able to be replayed, false otherwise. */ abstract boolean isValid(); + + /** + * Return the amount of memory used by this display list. + * + * @return The size of this display list in bytes + */ + abstract int getSize(); } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index ac0abc35125d..a7fe95d225fb 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -330,6 +330,12 @@ class GLES20Canvas extends HardwareCanvas { private static native void nDestroyDisplayList(int displayList); + static int getDisplayListSize(int displayList) { + return nGetDisplayListSize(displayList); + } + + private static native int nGetDisplayListSize(int displayList); + @Override public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) { return nDrawDisplayList(mRenderer, diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index 9e649cea63dc..4ca5e9844ffb 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -82,6 +82,12 @@ class GLES20DisplayList extends DisplayList { } } + @Override + int getSize() { + if (mFinalizer == null) return 0; + return GLES20Canvas.getDisplayListSize(mFinalizer.mNativeDisplayList); + } + private static class DisplayListFinalizer { final int mNativeDisplayList; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 5404e3a6acd3..4e4923b50bbe 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -934,7 +934,9 @@ public abstract class HardwareRenderer { } private void destroyHardwareLayer(View view) { - view.destroyLayer(); + if (view.destroyLayer()) { + view.invalidate(true); + } if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5b8a2019a0a9..bfa525ccd9c9 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2294,8 +2294,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit private Bitmap mDrawingCache; private Bitmap mUnscaledDrawingCache; - private DisplayList mDisplayList; private HardwareLayer mHardwareLayer; + DisplayList mDisplayList; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -9727,11 +9727,13 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit return mHardwareLayer; } - void destroyLayer() { + boolean destroyLayer() { if (mHardwareLayer != null) { mHardwareLayer.destroy(); mHardwareLayer = null; + return true; } + return false; } /** diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 4acf48c6b316..96e550e16c55 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -420,7 +420,7 @@ public class ViewDebug { * * @hide */ - public static long getViewAncestorInstanceCount() { + public static long getViewRootImplCount() { return Debug.countInstancesOfClass(ViewRootImpl.class); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 57bf17f36b92..cddf41e7cea8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -80,6 +80,7 @@ import com.android.internal.view.RootViewSurfaceTaker; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -235,7 +236,6 @@ public final class ViewRootImpl extends Handler implements ViewParent, final Configuration mLastConfiguration = new Configuration(); final Configuration mPendingConfiguration = new Configuration(); - class ResizedInfo { Rect coveredInsets; Rect visibleInsets; @@ -542,10 +542,26 @@ public final class ViewRootImpl extends Handler implements ViewParent, } private void destroyHardwareResources() { - if (mAttachInfo.mHardwareRenderer.isEnabled()) { - mAttachInfo.mHardwareRenderer.destroyLayers(mView); + if (mAttachInfo.mHardwareRenderer != null) { + if (mAttachInfo.mHardwareRenderer.isEnabled()) { + mAttachInfo.mHardwareRenderer.destroyLayers(mView); + } + mAttachInfo.mHardwareRenderer.destroy(false); + } + } + + void destroyHardwareLayers() { + if (mThread != Thread.currentThread()) { + if (mAttachInfo.mHardwareRenderer != null && + mAttachInfo.mHardwareRenderer.isEnabled()) { + HardwareRenderer.trimMemory(ComponentCallbacks.TRIM_MEMORY_MODERATE); + } + } else { + if (mAttachInfo.mHardwareRenderer != null && + mAttachInfo.mHardwareRenderer.isEnabled()) { + mAttachInfo.mHardwareRenderer.destroyLayers(mView); + } } - mAttachInfo.mHardwareRenderer.destroy(false); } private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { @@ -876,9 +892,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, attachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { - if (mAttachInfo.mHardwareRenderer != null) { - destroyHardwareResources(); - } + destroyHardwareResources(); } if (viewVisibility == View.GONE) { // After making a window gone, we will count it as being @@ -3492,6 +3506,31 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void debug() { mView.debug(); } + + public void dumpGfxInfo(PrintWriter pw, int[] info) { + if (mView != null) { + getGfxInfo(mView, info); + } else { + info[0] = info[1] = 0; + } + } + + private void getGfxInfo(View view, int[] info) { + DisplayList displayList = view.mDisplayList; + info[0]++; + if (displayList != null) { + info[1] += displayList.getSize(); + } + + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + + int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + getGfxInfo(group.getChildAt(i), info); + } + } + } public void die(boolean immediate) { if (immediate) { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index a451bb52204f..5ef4f3eb31e7 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -24,6 +24,9 @@ import android.util.AndroidRuntimeException; import android.util.Log; import android.view.inputmethod.InputMethodManager; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintWriter; import java.util.HashMap; final class WindowLeaked extends AndroidRuntimeException { @@ -392,7 +395,7 @@ public class WindowManagerImpl implements WindowManager { leak.setStackTrace(root.getLocation().getStackTrace()); Log.e("WindowManager", leak.getMessage(), leak); } - + removeViewLocked(i); i--; count--; @@ -410,6 +413,59 @@ public class WindowManagerImpl implements WindowManager { } } + /** + * @hide + */ + public void trimLocalMemory() { + synchronized (this) { + if (mViews == null) return; + int count = mViews.length; + for (int i = 0; i < count; i++) { + mRoots[i].destroyHardwareLayers(); + } + } + } + + /** + * @hide + */ + public void dumpGfxInfo(FileDescriptor fd) { + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new PrintWriter(fout); + try { + synchronized (this) { + if (mViews != null) { + pw.println("View hierarchy:"); + + final int count = mViews.length; + + int viewsCount = 0; + int displayListsSize = 0; + int[] info = new int[2]; + + for (int i = 0; i < count; i++) { + ViewRootImpl root = mRoots[i]; + root.dumpGfxInfo(pw, info); + + String name = root.getClass().getName() + '@' + + Integer.toHexString(hashCode()); + pw.printf(" %s: %d views, %.2f kB (display lists)\n", + name, info[0], info[1] / 1024.0f); + + viewsCount += info[0]; + displayListsSize += info[1]; + } + + pw.printf("\nTotal ViewRootImpl: %d\n", count); + pw.printf("Total Views: %d\n", viewsCount); + pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f); + } + } + } finally { + pw.flush(); + } + } + public void setStoppedState(IBinder token, boolean stopped) { synchronized (this) { if (mViews == null) diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index b06de9d1b91e..b3f2d514c504 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -580,6 +580,11 @@ static DisplayList* android_view_GLES20Canvas_getDisplayList(JNIEnv* env, return renderer->getDisplayList(displayList); } +static jint android_view_GLES20Canvas_getDisplayListSize(JNIEnv* env, + jobject clazz, DisplayList* displayList) { + return displayList->getSize(); +} + static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) { return new DisplayListRenderer; @@ -721,8 +726,7 @@ static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz // ---------------------------------------------------------------------------- static void -android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) -{ +android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) { #ifdef USE_OPENGL_RENDERER int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); android::uirenderer::DisplayList::outputLogBuffer(fd); @@ -814,6 +818,7 @@ static JNINativeMethod gMethods[] = { { "nGetDisplayList", "(II)I", (void*) android_view_GLES20Canvas_getDisplayList }, { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, + { "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize }, { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer }, { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z", diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 886c05c64c07..88cfc5a6b25e 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -74,12 +74,17 @@ void DisplayList::outputLogBuffer(int fd) { if (logBuffer.isEmpty()) { return; } - String8 cachesLog; - Caches::getInstance().dumpMemoryUsage(cachesLog); + FILE *file = fdopen(fd, "a"); - fprintf(file, "\nCaches:\n%s", cachesLog.string()); + fprintf(file, "\nRecent DisplayList operations\n"); logBuffer.outputCommands(file, OP_NAMES); + + String8 cachesLog; + Caches::getInstance().dumpMemoryUsage(cachesLog); + fprintf(file, "\nCaches:\n%s", cachesLog.string()); + fprintf(file, "\n"); + fflush(file); } @@ -143,10 +148,10 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde clearResources(); } - size_t size = writer.size(); - void* buffer = sk_malloc_throw(size); + mSize = writer.size(); + void* buffer = sk_malloc_throw(mSize); writer.flatten(buffer); - mReader.setMemory(buffer, size); + mReader.setMemory(buffer, mSize); Caches& caches = Caches::getInstance(); @@ -188,6 +193,11 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde } void DisplayList::init() { + mSize = 0; +} + +size_t DisplayList::getSize() { + return mSize; } /** diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index a3d346dd9bea..69e72a454408 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -105,6 +105,8 @@ public: void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); + size_t getSize(); + bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0); void output(OpenGLRenderer& renderer, uint32_t level = 0); @@ -203,6 +205,8 @@ private: Vector mShaders; mutable SkFlattenableReadBuffer mReader; + + size_t mSize; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 89c35da3f088..0af017787305 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -68,6 +68,7 @@ void LayerCache::setMaxSize(uint32_t maxSize) { void LayerCache::deleteLayer(Layer* layer) { if (layer) { + LAYER_LOGD("Destroying layer %dx%d", layer->getWidth(), layer->getHeight()); mSize -= layer->getWidth() * layer->getHeight() * 4; layer->deleteFbo(); layer->deleteTexture(); diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 81816f6a2b5d..7e8c7fdb8e03 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -301,7 +301,7 @@ void LayerRenderer::destroyLayer(Layer* layer) { delete layer; } else { LAYER_RENDERER_LOGD(" Cached!"); -#if DEBUG_LAYERS +#if DEBUG_LAYER_RENDERER Caches::getInstance().layerCache.dump(); #endif layer->region.clear(); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 47049e2f0751..923978f67d7a 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -68,7 +68,7 @@ enum DebugLevel { #define MB(s) s * 1024 * 1024 #define DEFAULT_TEXTURE_CACHE_SIZE 24.0f -#define DEFAULT_LAYER_CACHE_SIZE 24.0f +#define DEFAULT_LAYER_CACHE_SIZE 16.0f #define DEFAULT_PATH_CACHE_SIZE 4.0f #define DEFAULT_SHAPE_CACHE_SIZE 1.0f #define DEFAULT_PATCH_CACHE_SIZE 512 -- cgit v1.2.3-59-g8ed1b