diff options
author | John Reck <jreck@google.com> | 2014-05-29 22:23:40 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-05-29 22:23:41 +0000 |
commit | aee470c0232afdac4256d13020fa0cf04f30395c (patch) | |
tree | 5fc37aedf16ce9aa5b6283d0c9b5665954f191f3 | |
parent | e2960b6fd2d31f8a729ce9d180bffc947c0d6464 (diff) | |
parent | fe5e7b7346a54537b980796ceeca66bfdbd05561 (diff) | |
download | frameworks_base-aee470c0232afdac4256d13020fa0cf04f30395c.zip frameworks_base-aee470c0232afdac4256d13020fa0cf04f30395c.tar.gz frameworks_base-aee470c0232afdac4256d13020fa0cf04f30395c.tar.bz2 |
Merge "Enable debug stuffs" into lmp-preview-dev
-rw-r--r-- | core/java/android/view/GLRenderer.java | 39 | ||||
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 116 | ||||
-rw-r--r-- | core/java/android/view/RenderNode.java | 8 | ||||
-rw-r--r-- | core/java/android/view/ThreadedRenderer.java | 52 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 2 | ||||
-rw-r--r-- | core/java/android/view/WindowManagerGlobal.java | 7 | ||||
-rw-r--r-- | core/jni/android_view_RenderNode.cpp | 7 | ||||
-rw-r--r-- | core/jni/android_view_ThreadedRenderer.cpp | 17 | ||||
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/DrawProfiler.cpp | 261 | ||||
-rw-r--r-- | libs/hwui/DrawProfiler.h | 96 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 30 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 11 | ||||
-rw-r--r-- | libs/hwui/RenderNode.h | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 13 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 5 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 9 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 5 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 25 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 6 |
20 files changed, 545 insertions, 166 deletions
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java index 6dd7c00..64a4c41 100644 --- a/core/java/android/view/GLRenderer.java +++ b/core/java/android/view/GLRenderer.java @@ -61,6 +61,7 @@ import android.view.Surface.OutOfResourcesException; import com.google.android.gles_jni.EGLImpl; +import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -106,7 +107,6 @@ public class GLRenderer extends HardwareRenderer { private static final String[] VISUALIZERS = { PROFILE_PROPERTY_VISUALIZE_BARS, - PROFILE_PROPERTY_VISUALIZE_LINES }; private static final String[] OVERDRAW = { @@ -674,7 +674,7 @@ public class GLRenderer extends HardwareRenderer { mProfilePaint = null; if (value) { - mDebugDataProvider = new DrawPerformanceDataProvider(graphType); + mDebugDataProvider = new GraphDataProvider(graphType); } else { mDebugDataProvider = null; } @@ -742,7 +742,7 @@ public class GLRenderer extends HardwareRenderer { } @Override - void dumpGfxInfo(PrintWriter pw) { + void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) { if (mProfileEnabled) { pw.printf("\n\tDraw\tProcess\tExecute\n"); @@ -763,11 +763,6 @@ public class GLRenderer extends HardwareRenderer { } } - @Override - long getFrameCount() { - return mFrameCount; - } - /** * Indicates whether this renderer instance can track and update dirty regions. */ @@ -1446,7 +1441,18 @@ public class GLRenderer extends HardwareRenderer { private static native void nPrepareTree(long displayListPtr); - class DrawPerformanceDataProvider extends GraphDataProvider { + class GraphDataProvider { + /** + * Draws the graph as bars. Frame elements are stacked on top of + * each other. + */ + public static final int GRAPH_TYPE_BARS = 0; + /** + * Draws the graph as lines. The number of series drawn corresponds + * to the number of elements. + */ + public static final int GRAPH_TYPE_LINES = 1; + private final int mGraphType; private int mVerticalUnit; @@ -1454,11 +1460,10 @@ public class GLRenderer extends HardwareRenderer { private int mHorizontalMargin; private int mThresholdStroke; - DrawPerformanceDataProvider(int graphType) { + public GraphDataProvider(int graphType) { mGraphType = graphType; } - @Override void prepare(DisplayMetrics metrics) { final float density = metrics.density; @@ -1468,64 +1473,52 @@ public class GLRenderer extends HardwareRenderer { mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); } - @Override int getGraphType() { return mGraphType; } - @Override int getVerticalUnitSize() { return mVerticalUnit; } - @Override int getHorizontalUnitSize() { return mHorizontalUnit; } - @Override int getHorizontaUnitMargin() { return mHorizontalMargin; } - @Override float[] getData() { return mProfileData; } - @Override float getThreshold() { return 16; } - @Override int getFrameCount() { return mProfileData.length / PROFILE_FRAME_DATA_COUNT; } - @Override int getElementCount() { return PROFILE_FRAME_DATA_COUNT; } - @Override int getCurrentFrame() { return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT; } - @Override void setupGraphPaint(Paint paint, int elementIndex) { paint.setColor(PROFILE_DRAW_COLORS[elementIndex]); if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); } - @Override void setupThresholdPaint(Paint paint) { paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR); paint.setStrokeWidth(mThresholdStroke); } - @Override void setupCurrentFramePaint(Paint paint) { paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR); if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 3c4d83f..60f8ee3 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -17,13 +17,13 @@ package android.view; import android.graphics.Bitmap; -import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.util.DisplayMetrics; import android.view.Surface.OutOfResourcesException; import java.io.File; +import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -61,11 +61,9 @@ public abstract class HardwareRenderer { * Possible values: * "true", to enable profiling * "visual_bars", to enable profiling and visualize the results on screen - * "visual_lines", to enable profiling and visualize the results on screen * "false", to disable profiling * * @see #PROFILE_PROPERTY_VISUALIZE_BARS - * @see #PROFILE_PROPERTY_VISUALIZE_LINES * * @hide */ @@ -80,14 +78,6 @@ public abstract class HardwareRenderer { public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars"; /** - * Value for {@link #PROFILE_PROPERTY}. When the property is set to this - * value, profiling data will be visualized on screen as a line chart. - * - * @hide - */ - public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines"; - - /** * System property used to specify the number of frames to be used * when doing hardware rendering profiling. * The default value of this property is #PROFILE_MAX_FRAMES. @@ -298,16 +288,8 @@ public abstract class HardwareRenderer { /** * Outputs extra debugging information in the specified file descriptor. - * @param pw */ - abstract void dumpGfxInfo(PrintWriter pw); - - /** - * Outputs the total number of frames rendered (used for fps calculations) - * - * @return the number of frames rendered - */ - abstract long getFrameCount(); + abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd); /** * Loads system properties used by the renderer. This method is invoked @@ -583,98 +565,4 @@ public abstract class HardwareRenderer { */ public void notifyFramePending() { } - - /** - * Describes a series of frames that should be drawn on screen as a graph. - * Each frame is composed of 1 or more elements. - */ - abstract class GraphDataProvider { - /** - * Draws the graph as bars. Frame elements are stacked on top of - * each other. - */ - public static final int GRAPH_TYPE_BARS = 0; - /** - * Draws the graph as lines. The number of series drawn corresponds - * to the number of elements. - */ - public static final int GRAPH_TYPE_LINES = 1; - - /** - * Returns the type of graph to render. - * - * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES} - */ - abstract int getGraphType(); - - /** - * This method is invoked before the graph is drawn. This method - * can be used to compute sizes, etc. - * - * @param metrics The display metrics - */ - abstract void prepare(DisplayMetrics metrics); - - /** - * @return The size in pixels of a vertical unit. - */ - abstract int getVerticalUnitSize(); - - /** - * @return The size in pixels of a horizontal unit. - */ - abstract int getHorizontalUnitSize(); - - /** - * @return The size in pixels of the margin between horizontal units. - */ - abstract int getHorizontaUnitMargin(); - - /** - * An optional threshold value. - * - * @return A value >= 0 to draw the threshold, a negative value - * to ignore it. - */ - abstract float getThreshold(); - - /** - * The data to draw in the graph. The number of elements in the - * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}. - * If a value is negative the following values will be ignored. - */ - abstract float[] getData(); - - /** - * Returns the number of frames to render in the graph. - */ - abstract int getFrameCount(); - - /** - * Returns the number of elements in each frame. This directly affects - * the number of series drawn in the graph. - */ - abstract int getElementCount(); - - /** - * Returns the current frame, if any. If the returned value is negative - * the current frame is ignored. - */ - abstract int getCurrentFrame(); - - /** - * Prepares the paint to draw the specified element (or series.) - */ - abstract void setupGraphPaint(Paint paint, int elementIndex); - - /** - * Prepares the paint to draw the threshold. - */ - abstract void setupThresholdPaint(Paint paint); - - /** - * Prepares the paint to draw the current frame indicator. - */ - abstract void setupCurrentFramePaint(Paint paint); - } } diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index cf125bc..e63829e 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -850,6 +850,13 @@ public class RenderNode { nOutput(mNativeRenderNode); } + /** + * Gets the size of the DisplayList for debug purposes. + */ + public int getDebugSize() { + return nGetDebugSize(mNativeRenderNode); + } + /////////////////////////////////////////////////////////////////////////// // Animations /////////////////////////////////////////////////////////////////////////// @@ -941,6 +948,7 @@ public class RenderNode { private static native float nGetPivotX(long renderNode); private static native float nGetPivotY(long renderNode); private static native void nOutput(long renderNode); + private static native int nGetDebugSize(long renderNode); /////////////////////////////////////////////////////////////////////////// // Animations diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 9c9a939..cac23a8 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -22,12 +22,14 @@ import android.graphics.SurfaceTexture; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.Trace; import android.util.Log; import android.util.TimeUtils; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; +import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -60,11 +62,16 @@ public class ThreadedRenderer extends HardwareRenderer { // Needs a ViewRoot invalidate private static final int SYNC_INVALIDATE_REQUIRED = 0x1; + private static final String[] VISUALIZERS = { + PROFILE_PROPERTY_VISUALIZE_BARS, + }; + private int mWidth, mHeight; private long mNativeProxy; private boolean mInitialized = false; private RenderNode mRootNode; private Choreographer mChoreographer; + private boolean mProfilingEnabled; ThreadedRenderer(boolean translucent) { AtlasInitializer.sInstance.init(); @@ -77,6 +84,8 @@ public class ThreadedRenderer extends HardwareRenderer { // Setup timing mChoreographer = Choreographer.getInstance(); nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos()); + + loadSystemProperties(); } @Override @@ -166,19 +175,33 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override - void dumpGfxInfo(PrintWriter pw) { - // TODO Auto-generated method stub + void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) { + pw.flush(); + nDumpProfileInfo(mNativeProxy, fd); } - @Override - long getFrameCount() { - // TODO Auto-generated method stub - return 0; + private static int search(String[] values, String value) { + for (int i = 0; i < values.length; i++) { + if (values[i].equals(value)) return i; + } + return -1; + } + + private static boolean checkIfProfilingRequested() { + String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY); + int graphType = search(VISUALIZERS, profiling); + return (graphType >= 0) || Boolean.parseBoolean(profiling); } @Override boolean loadSystemProperties() { - return nLoadSystemProperties(mNativeProxy); + boolean changed = nLoadSystemProperties(mNativeProxy); + boolean wantProfiling = checkIfProfilingRequested(); + if (wantProfiling != mProfilingEnabled) { + mProfilingEnabled = wantProfiling; + changed = true; + } + return changed; } private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) { @@ -210,14 +233,24 @@ public class ThreadedRenderer extends HardwareRenderer { long frameTimeNanos = mChoreographer.getFrameTimeNanos(); attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS; + long recordDuration = 0; + if (mProfilingEnabled) { + recordDuration = System.nanoTime(); + } + updateRootDisplayList(view, callbacks); + if (mProfilingEnabled) { + recordDuration = System.nanoTime() - recordDuration; + } + attachInfo.mIgnoreDirtyState = false; if (dirty == null) { dirty = NULL_RECT; } int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, + recordDuration, view.getResources().getDisplayMetrics().density, dirty.left, dirty.top, dirty.right, dirty.bottom); if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { attachInfo.mViewRootImpl.invalidate(); @@ -354,7 +387,8 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nSetup(long nativeProxy, int width, int height, float lightX, float lightY, float lightZ, float lightRadius); private static native void nSetOpaque(long nativeProxy, boolean opaque); - private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos, + private static native int nSyncAndDrawFrame(long nativeProxy, + long frameTimeNanos, long recordDuration, float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); private static native void nRunWithGlContext(long nativeProxy, Runnable runnable); private static native void nDestroyCanvasAndSurface(long nativeProxy); @@ -370,4 +404,6 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nFence(long nativeProxy); private static native void nNotifyFramePending(long nativeProxy); + + private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fc7bf0e..aa06d15 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5329,7 +5329,7 @@ public final class ViewRootImpl implements ViewParent, RenderNode renderNode = view.mRenderNode; info[0]++; if (renderNode != null) { - info[1] += 0; /* TODO: Memory used by RenderNodes (properties + DisplayLists) */ + info[1] += renderNode.getDebugSize(); } if (view instanceof ViewGroup) { diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 96c0ed2..b4779f4 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -430,7 +430,7 @@ public final class WindowManagerGlobal { HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer; if (renderer != null) { - renderer.dumpGfxInfo(pw); + renderer.dumpGfxInfo(pw, fd); } } @@ -447,11 +447,6 @@ public final class WindowManagerGlobal { String name = getWindowName(root); pw.printf(" %s\n %d views, %.2f kB of display lists", name, info[0], info[1] / 1024.0f); - HardwareRenderer renderer = - root.getView().mAttachInfo.mHardwareRenderer; - if (renderer != null) { - pw.printf(", %d frames rendered", renderer.getFrameCount()); - } pw.printf("\n\n"); viewsCount += info[0]; diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 867c1b1..26022e0 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -48,6 +48,12 @@ static void android_view_RenderNode_output(JNIEnv* env, renderNode->output(); } +static jint android_view_RenderNode_getDebugSize(JNIEnv* env, + jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->getDebugSize(); +} + static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring name) { RenderNode* renderNode = new RenderNode(); renderNode->incStrong(0); @@ -505,6 +511,7 @@ static JNINativeMethod gMethods[] = { { "nDestroyRenderNode", "(J)V", (void*) android_view_RenderNode_destroyRenderNode }, { "nSetDisplayListData", "(JJ)V", (void*) android_view_RenderNode_setDisplayListData }, { "nOutput", "(J)V", (void*) android_view_RenderNode_output }, + { "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize }, { "nSetCaching", "(JZ)V", (void*) android_view_RenderNode_setCaching }, { "nSetStaticMatrix", "(JJ)V", (void*) android_view_RenderNode_setStaticMatrix }, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 6f256f0..bd016fd 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -238,10 +238,11 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, } static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, - jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop, - jint dirtyRight, jint dirtyBottom) { + jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density, + jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - return proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); + return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density, + dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); } static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz, @@ -311,6 +312,13 @@ static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobjec proxy->notifyFramePending(); } +static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz, + jlong proxyPtr, jobject javaFileDescriptor) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); + proxy->dumpProfileInfo(fd); +} + #endif // ---------------------------------------------------------------------------- @@ -332,7 +340,7 @@ static JNINativeMethod gMethods[] = { { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface }, { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup }, { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, - { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, + { "nSyncAndDrawFrame", "(JJJFIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface }, { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext }, @@ -343,6 +351,7 @@ static JNINativeMethod gMethods[] = { { "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches }, { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, + { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, #endif }; diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 2cadf09..442f327 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -23,6 +23,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) DisplayListLogBuffer.cpp \ DisplayListRenderer.cpp \ Dither.cpp \ + DrawProfiler.cpp \ Extensions.cpp \ FboCache.cpp \ GradientCache.cpp \ diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp new file mode 100644 index 0000000..971a66e --- /dev/null +++ b/libs/hwui/DrawProfiler.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "DrawProfiler.h" + +#include <cutils/compiler.h> + +#include "OpenGLRenderer.h" +#include "Properties.h" + +#define DEFAULT_MAX_FRAMES 128 + +#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return + +#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f) + +#define PROFILE_DRAW_WIDTH 3 +#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 +#define PROFILE_DRAW_DP_PER_MS 7 + +// Number of floats we want to display from FrameTimingData +// If this is changed make sure to update the indexes below +#define NUM_ELEMENTS 4 + +#define RECORD_INDEX 0 +#define PREPARE_INDEX 1 +#define PLAYBACK_INDEX 2 +#define SWAPBUFFERS_INDEX 3 + +// Must be NUM_ELEMENTS in size +static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 }; +static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d; +static const SkColor THRESHOLD_COLOR = 0xff5faa4d; + +// We could get this from TimeLord and use the actual frame interval, but +// this is good enough +#define FRAME_THRESHOLD 16 + +namespace android { +namespace uirenderer { + +static int dpToPx(int dp, float density) { + return (int) (dp * density + 0.5f); +} + +DrawProfiler::DrawProfiler() + : mType(kNone) + , mDensity(0) + , mData(NULL) + , mDataSize(0) + , mCurrentFrame(-1) + , mPreviousTime(0) + , mVerticalUnit(0) + , mHorizontalUnit(0) + , mThresholdStroke(0) { + setDensity(1); +} + +DrawProfiler::~DrawProfiler() { + destroyData(); +} + +void DrawProfiler::setDensity(float density) { + if (CC_UNLIKELY(mDensity != density)) { + mDensity = density; + mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); + mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density); + mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); + } +} + +void DrawProfiler::startFrame(nsecs_t recordDurationNanos) { + RETURN_IF_DISABLED(); + mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos); + mPreviousTime = systemTime(CLOCK_MONOTONIC); +} + +void DrawProfiler::markPlaybackStart() { + RETURN_IF_DISABLED(); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); + mPreviousTime = now; +} + +void DrawProfiler::markPlaybackEnd() { + RETURN_IF_DISABLED(); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); + mPreviousTime = now; +} + +void DrawProfiler::finishFrame() { + RETURN_IF_DISABLED(); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); + mPreviousTime = now; + mCurrentFrame = (mCurrentFrame + 1) % mDataSize; +} + +void DrawProfiler::unionDirty(Rect* dirty) { + RETURN_IF_DISABLED(); + // Not worth worrying about minimizing the dirty region for debugging, so just + // dirty the entire viewport. + if (dirty) { + dirty->setEmpty(); + } +} + +void DrawProfiler::draw(OpenGLRenderer* canvas) { + if (CC_LIKELY(mType != kBars)) { + return; + } + + prepareShapes(canvas->getViewportHeight()); + drawGraph(canvas); + drawCurrentFrame(canvas); + drawThreshold(canvas); +} + +void DrawProfiler::createData() { + if (mData) return; + + mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES); + if (mDataSize <= 0) mDataSize = 1; + if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum + mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData)); + mRects = new float*[NUM_ELEMENTS]; + for (int i = 0; i < NUM_ELEMENTS; i++) { + // 4 floats per rect + mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float)); + } + mCurrentFrame = 0; +} + +void DrawProfiler::destroyData() { + delete mData; + mData = NULL; +} + +void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { + r.top = r.bottom - (data * mVerticalUnit); + shapeOutput[0] = r.left; + shapeOutput[1] = r.top; + shapeOutput[2] = r.right; + shapeOutput[3] = r.bottom; + r.bottom = r.top; +} + +void DrawProfiler::prepareShapes(const int baseline) { + Rect r; + r.right = mHorizontalUnit; + for (int i = 0; i < mDataSize; i++) { + const int shapeIndex = i * 4; + r.bottom = baseline; + addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex); + addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex); + addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex); + addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex); + r.translate(mHorizontalUnit, 0); + } +} + +void DrawProfiler::drawGraph(OpenGLRenderer* canvas) { + SkPaint paint; + for (int i = 0; i < NUM_ELEMENTS; i++) { + paint.setColor(ELEMENT_COLORS[i]); + canvas->drawRects(mRects[i], mDataSize * 4, &paint); + } +} + +void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) { + // This draws a solid rect over the entirety of the current frame's shape + // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1] + // which will therefore fully overlap the previously drawn rects + SkPaint paint; + paint.setColor(CURRENT_FRAME_COLOR); + const int i = mCurrentFrame * 4; + canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2], + mRects[0][i+3], &paint); +} + +void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { + SkPaint paint; + paint.setColor(THRESHOLD_COLOR); + paint.setStrokeWidth(mThresholdStroke); + + float pts[4]; + pts[0] = 0.0f; + pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); + pts[2] = canvas->getViewportWidth(); + canvas->drawLines(pts, 4, &paint); +} + +DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() { + ProfileType type = kNone; + char buf[PROPERTY_VALUE_MAX] = {'\0',}; + if (property_get(PROPERTY_PROFILE, buf, "") > 0) { + if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) { + type = kBars; + } else if (!strcmp(buf, "true")) { + type = kConsole; + } + } + return type; +} + +bool DrawProfiler::loadSystemProperties() { + ProfileType newType = loadRequestedProfileType(); + if (newType != mType) { + mType = newType; + if (mType == kNone) { + destroyData(); + } else { + createData(); + } + return true; + } + return false; +} + +void DrawProfiler::dumpData(int fd) { + RETURN_IF_DISABLED(); + + // This method logs the last N frames (where N is <= mDataSize) since the + // last call to dumpData(). In other words if there's a dumpData(), draw frame, + // dumpData(), the last dumpData() should only log 1 frame. + + const FrameTimingData emptyData = {0, 0, 0, 0}; + + FILE *file = fdopen(fd, "a"); + fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); + + for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) { + int i = (mCurrentFrame + frameOffset) % mDataSize; + if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) { + continue; + } + fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", + mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers); + } + // reset the buffer + memset(mData, 0, sizeof(FrameTimingData) * mDataSize); + mCurrentFrame = 0; + + fflush(file); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h new file mode 100644 index 0000000..c1aa1c6 --- /dev/null +++ b/libs/hwui/DrawProfiler.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DRAWPROFILER_H +#define DRAWPROFILER_H + +#include <utils/Timers.h> +#include "Rect.h" + +namespace android { +namespace uirenderer { + +class OpenGLRenderer; + +class DrawProfiler { +public: + DrawProfiler(); + ~DrawProfiler(); + + bool loadSystemProperties(); + void setDensity(float density); + + void startFrame(nsecs_t recordDurationNanos = 0); + void markPlaybackStart(); + void markPlaybackEnd(); + void finishFrame(); + + void unionDirty(Rect* dirty); + void draw(OpenGLRenderer* canvas); + + void dumpData(int fd); + +private: + enum ProfileType { + kNone, + kConsole, + kBars, + }; + + typedef struct { + float record; + float prepare; + float playback; + float swapBuffers; + } FrameTimingData; + + void createData(); + void destroyData(); + + void addRect(Rect& r, float data, float* shapeOutput); + void prepareShapes(const int baseline); + void drawGraph(OpenGLRenderer* canvas); + void drawCurrentFrame(OpenGLRenderer* canvas); + void drawThreshold(OpenGLRenderer* canvas); + + ProfileType loadRequestedProfileType(); + + ProfileType mType; + float mDensity; + + FrameTimingData* mData; + int mDataSize; + + int mCurrentFrame; + nsecs_t mPreviousTime; + + int mVerticalUnit; + int mHorizontalUnit; + int mThresholdStroke; + + /* + * mRects represents an array of rect shapes, divided into NUM_ELEMENTS + * groups such that each group is drawn with the same paint. + * For example mRects[0] is the array of rect floats suitable for + * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record + * information. + */ + float** mRects; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DRAWPROFILER_H */ diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 20b8f2f..12241b8 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -89,6 +89,36 @@ enum DebugLevel { #define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling" /** + * System property used to enable or disable hardware rendering profiling. + * The default value of this property is assumed to be false. + * + * When profiling is enabled, the adb shell dumpsys gfxinfo command will + * output extra information about the time taken to execute by the last + * frames. + * + * Possible values: + * "true", to enable profiling + * "visual_bars", to enable profiling and visualize the results on screen + * "false", to disable profiling + */ +#define PROPERTY_PROFILE "debug.hwui.profile" +#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars" + +/** + * System property used to specify the number of frames to be used + * when doing hardware rendering profiling. + * The default value of this property is #PROFILE_MAX_FRAMES. + * + * When profiling is enabled, the adb shell dumpsys gfxinfo command will + * output extra information about the time taken to execute by the last + * frames. + * + * Possible values: + * "60", to set the limit of frames to 60 + */ +#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes" + +/** * Used to enable/disable non-rectangular clipping debugging. * * The accepted values are: diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index df74f31..d964efc 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -93,6 +93,17 @@ void RenderNode::output(uint32_t level) { ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); } +int RenderNode::getDebugSize() { + int size = sizeof(RenderNode); + if (mStagingDisplayListData) { + size += mStagingDisplayListData->allocator.usedSize(); + } + if (mDisplayListData && mDisplayListData != mStagingDisplayListData) { + size += mDisplayListData->allocator.usedSize(); + } + return size; +} + void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 1811a7b..1a5377b 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -119,6 +119,7 @@ public: void replayNodeInParent(ReplayStateStruct& replayStruct, const int level); ANDROID_API void output(uint32_t level = 1); + ANDROID_API int getDebugSize(); bool isRenderable() const { return mDisplayListData && mDisplayListData->hasDrawOps; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0fe01a7..f91e90e 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -485,6 +485,8 @@ void CanvasContext::draw(Rect* dirty) { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawDisplayList called on a context with no canvas or surface!"); + profiler().markPlaybackStart(); + EGLint width, height; mGlobalContext->beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { @@ -492,6 +494,8 @@ void CanvasContext::draw(Rect* dirty) { dirty = NULL; } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { dirty = NULL; + } else { + profiler().unionDirty(dirty); } status_t status; @@ -505,14 +509,17 @@ void CanvasContext::draw(Rect* dirty) { Rect outBounds; status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); - // TODO: Draw debug info - // TODO: Performance tracking + profiler().draw(mCanvas); mCanvas->finish(); + profiler().markPlaybackEnd(); + if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } + + profiler().finishFrame(); } // Called by choreographer to do an RT-driven animation @@ -523,6 +530,8 @@ void CanvasContext::doFrame() { ATRACE_CALL(); + profiler().startFrame(); + TreeInfo info; info.evaluateAnimations = true; info.performStagingPush = false; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index da85d44..a04269b 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -23,6 +23,7 @@ #include <utils/Functor.h> #include <utils/Vector.h> +#include "../DrawProfiler.h" #include "../RenderNode.h" #include "RenderTask.h" #include "RenderThread.h" @@ -77,6 +78,8 @@ public: void notifyFramePending(); + DrawProfiler& profiler() { return mProfiler; } + private: friend class RegisterFrameCallbackTask; @@ -100,6 +103,8 @@ private: bool mHaveNewSurface; const sp<RenderNode> mRootRenderNode; + + DrawProfiler mProfiler; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index ee3e059..7ea358f 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -34,6 +34,8 @@ DrawFrameTask::DrawFrameTask() : mRenderThread(NULL) , mContext(NULL) , mFrameTimeNanos(0) + , mRecordDurationNanos(0) + , mDensity(1.0f) // safe enough default , mSyncResult(kSync_OK) { } @@ -64,15 +66,17 @@ void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { mDirty.set(left, top, right, bottom); } -int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) { +int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); mSyncResult = kSync_OK; mFrameTimeNanos = frameTimeNanos; + mRecordDurationNanos = recordDurationNanos; postAndWait(); // Reset the single-frame data mFrameTimeNanos = 0; + mRecordDurationNanos = 0; mDirty.setEmpty(); return mSyncResult; @@ -87,6 +91,9 @@ void DrawFrameTask::postAndWait() { void DrawFrameTask::run() { ATRACE_NAME("DrawFrame"); + mContext->profiler().setDensity(mDensity); + mContext->profiler().startFrame(mRecordDurationNanos); + bool canUnblockUiThread; bool canDrawThisFrame; { diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index acbc02a..30c8880 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -60,7 +60,8 @@ public: void removeLayer(DeferredLayerUpdater* layer); void setDirty(int left, int top, int right, int bottom); - int drawFrame(nsecs_t frameTimeNanos); + void setDensity(float density) { mDensity = density; } + int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos); virtual void run(); @@ -80,6 +81,8 @@ private: *********************************************/ Rect mDirty; nsecs_t mFrameTimeNanos; + nsecs_t mRecordDurationNanos; + float mDensity; int mSyncResult; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 8e772f2..77c0aa7 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -99,16 +99,20 @@ void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) { post(task); } -CREATE_BRIDGE0(loadSystemProperties) { +CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) { bool needsRedraw = false; if (Caches::hasInstance()) { needsRedraw = Caches::getInstance().initProperties(); } + if (args->context->profiler().loadSystemProperties()) { + needsRedraw = true; + } return (void*) needsRedraw; } bool RenderProxy::loadSystemProperties() { SETUP_TASK(loadSystemProperties); + args->context = mContext; return (bool) postAndWait(task); } @@ -175,10 +179,11 @@ void RenderProxy::setOpaque(bool opaque) { post(task); } -int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, - int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { +int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, + float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); - return mDrawFrameTask.drawFrame(frameTimeNanos); + mDrawFrameTask.setDensity(density); + return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos); } CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) { @@ -315,6 +320,18 @@ void RenderProxy::notifyFramePending() { mRenderThread.queueAtFront(task); } +CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) { + args->context->profiler().dumpData(args->fd); + return NULL; +} + +void RenderProxy::dumpProfileInfo(int fd) { + SETUP_TASK(dumpProfileInfo); + args->context = mContext; + args->fd = fd; + postAndWait(task); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 22d4e22..c8d42ec 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -69,8 +69,8 @@ public: ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius); ANDROID_API void setOpaque(bool opaque); - ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, - int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); + ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, + float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); ANDROID_API void destroyCanvasAndSurface(); ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion); @@ -87,6 +87,8 @@ public: ANDROID_API void fence(); ANDROID_API void notifyFramePending(); + ANDROID_API void dumpProfileInfo(int fd); + private: RenderThread& mRenderThread; CanvasContext* mContext; |