am 862540cd: Merge "Revert "DO NOT MERGE Backport of limited jank-tracking metrics"" into lmp-mr1-dev

* commit '862540cdb11a7128ae1fd670136f398645b4af7b':
  Revert "DO NOT MERGE Backport of limited jank-tracking metrics"
This commit is contained in:
John Reck
2015-05-14 00:51:07 +00:00
committed by Android Git Automerger
27 changed files with 116 additions and 805 deletions

View File

@@ -1083,7 +1083,7 @@ public final class ActivityThread {
@Override @Override
public void dumpGfxInfo(FileDescriptor fd, String[] args) { public void dumpGfxInfo(FileDescriptor fd, String[] args) {
dumpGraphicsInfo(fd); dumpGraphicsInfo(fd);
WindowManagerGlobal.getInstance().dumpGfxInfo(fd, args); WindowManagerGlobal.getInstance().dumpGfxInfo(fd);
} }
@Override @Override

View File

@@ -140,19 +140,6 @@ public final class Choreographer {
private long mLastFrameTimeNanos; private long mLastFrameTimeNanos;
private long mFrameIntervalNanos; private long mFrameIntervalNanos;
/**
* Contains information about the current frame for jank-tracking,
* mainly timings of key events along with a bit of metadata about
* view tree state
*
* TODO: Is there a better home for this? Currently Choreographer
* is the only one with CALLBACK_ANIMATION start time, hence why this
* resides here.
*
* @hide
*/
FrameInfo mFrameInfo = new FrameInfo();
/** /**
* Callback type: Input callback. Runs first. * Callback type: Input callback. Runs first.
* @hide * @hide
@@ -526,7 +513,6 @@ public final class Choreographer {
return; // no work to do return; // no work to do
} }
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime(); startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos; final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) { if (jitterNanos >= mFrameIntervalNanos) {
@@ -555,18 +541,12 @@ public final class Choreographer {
return; return;
} }
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false; mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos; mLastFrameTimeNanos = frameTimeNanos;
} }
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
if (DEBUG) { if (DEBUG) {

View File

@@ -1,118 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
package android.view;
import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Class that contains all the timing information for the current frame. This
* is used in conjunction with the hardware renderer to provide
* continous-monitoring jank events
*
* All times in nanoseconds from CLOCK_MONOTONIC/System.nanoTime()
*
* To minimize overhead from System.nanoTime() calls we infer durations of
* things by knowing the ordering of the events. For example, to know how
* long layout & measure took it's displayListRecordStart - performTraversalsStart.
*
* These constants must be kept in sync with FrameInfo.h in libhwui and are
* used for indexing into AttachInfo's mFrameInfo long[], which is intended
* to be quick to pass down to native via JNI, hence a pre-packed format
*
* @hide
*/
final class FrameInfo {
long[] mFrameInfo = new long[9];
// Various flags set to provide extra metadata about the current frame
private static final int FLAGS = 0;
// Is this the first-draw following a window layout?
public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1;
@IntDef(flag = true, value = {
FLAG_WINDOW_LAYOUT_CHANGED })
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
// The intended vsync time, unadjusted by jitter
private static final int INTENDED_VSYNC = 1;
// Jitter-adjusted vsync time, this is what was used as input into the
// animation & drawing system
private static final int VSYNC = 2;
// The time of the oldest input event
private static final int OLDEST_INPUT_EVENT = 3;
// The time of the newest input event
private static final int NEWEST_INPUT_EVENT = 4;
// When input event handling started
private static final int HANDLE_INPUT_START = 5;
// When animation evaluations started
private static final int ANIMATION_START = 6;
// When ViewRootImpl#performTraversals() started
private static final int PERFORM_TRAVERSALS_START = 7;
// When View:draw() started
private static final int DRAW_START = 8;
public void setVsync(long intendedVsync, long usedVsync) {
mFrameInfo[INTENDED_VSYNC] = intendedVsync;
mFrameInfo[VSYNC] = usedVsync;
mFrameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
mFrameInfo[NEWEST_INPUT_EVENT] = 0;
mFrameInfo[FLAGS] = 0;
}
public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) {
if (inputEventOldestTime < mFrameInfo[OLDEST_INPUT_EVENT]) {
mFrameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime;
}
if (inputEventTime > mFrameInfo[NEWEST_INPUT_EVENT]) {
mFrameInfo[NEWEST_INPUT_EVENT] = inputEventTime;
}
}
public void markInputHandlingStart() {
mFrameInfo[HANDLE_INPUT_START] = System.nanoTime();
}
public void markAnimationsStart() {
mFrameInfo[ANIMATION_START] = System.nanoTime();
}
public void markPerformTraversalsStart() {
mFrameInfo[PERFORM_TRAVERSALS_START] = System.nanoTime();
}
public void markDrawStart() {
mFrameInfo[DRAW_START] = System.nanoTime();
}
public void addFlags(@FrameInfoFlags long flags) {
mFrameInfo[FLAGS] |= flags;
}
}

View File

@@ -278,7 +278,7 @@ public abstract class HardwareRenderer {
/** /**
* Outputs extra debugging information in the specified file descriptor. * Outputs extra debugging information in the specified file descriptor.
*/ */
abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args); abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd);
/** /**
* Loads system properties used by the renderer. This method is invoked * Loads system properties used by the renderer. This method is invoked

View File

@@ -16,7 +16,8 @@
package android.view; package android.view;
import android.annotation.IntDef; import com.android.internal.R;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@@ -26,18 +27,16 @@ import android.graphics.drawable.Drawable;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Trace; import android.os.Trace;
import android.util.Log; import android.util.Log;
import android.util.LongSparseArray; import android.util.LongSparseArray;
import android.util.TimeUtils;
import android.view.Surface.OutOfResourcesException; import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo; import android.view.View.AttachInfo;
import com.android.internal.R;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@@ -75,14 +74,6 @@ public class ThreadedRenderer extends HardwareRenderer {
PROFILE_PROPERTY_VISUALIZE_BARS, PROFILE_PROPERTY_VISUALIZE_BARS,
}; };
private static final int FLAG_DUMP_FRAMESTATS = 1 << 0;
private static final int FLAG_DUMP_RESET = 1 << 1;
@IntDef(flag = true, value = {
FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET })
@Retention(RetentionPolicy.SOURCE)
public @interface DumpFlags {}
// Size of the rendered content. // Size of the rendered content.
private int mWidth, mHeight; private int mWidth, mHeight;
@@ -102,12 +93,12 @@ public class ThreadedRenderer extends HardwareRenderer {
private final float mLightRadius; private final float mLightRadius;
private final int mAmbientShadowAlpha; private final int mAmbientShadowAlpha;
private final int mSpotShadowAlpha; private final int mSpotShadowAlpha;
private final float mDensity;
private long mNativeProxy; private long mNativeProxy;
private boolean mInitialized = false; private boolean mInitialized = false;
private RenderNode mRootNode; private RenderNode mRootNode;
private Choreographer mChoreographer; private Choreographer mChoreographer;
private boolean mProfilingEnabled;
private boolean mRootNodeNeedsUpdate; private boolean mRootNodeNeedsUpdate;
ThreadedRenderer(Context context, boolean translucent) { ThreadedRenderer(Context context, boolean translucent) {
@@ -119,7 +110,6 @@ public class ThreadedRenderer extends HardwareRenderer {
(int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f); (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f); mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
a.recycle(); a.recycle();
mDensity = context.getResources().getDisplayMetrics().density;
long rootNodePtr = nCreateRootRenderNode(); long rootNodePtr = nCreateRootRenderNode();
mRootNode = RenderNode.adopt(rootNodePtr); mRootNode = RenderNode.adopt(rootNodePtr);
@@ -224,7 +214,7 @@ public class ThreadedRenderer extends HardwareRenderer {
mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight); mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
lightX, mLightY, mLightZ, mLightRadius, lightX, mLightY, mLightZ, mLightRadius,
mAmbientShadowAlpha, mSpotShadowAlpha, mDensity); mAmbientShadowAlpha, mSpotShadowAlpha);
} }
@Override @Override
@@ -243,25 +233,32 @@ public class ThreadedRenderer extends HardwareRenderer {
} }
@Override @Override
void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) { void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
pw.flush(); pw.flush();
int flags = 0; nDumpProfileInfo(mNativeProxy, fd);
for (int i = 0; i < args.length; i++) { }
switch (args[i]) {
case "framestats": private static int search(String[] values, String value) {
flags |= FLAG_DUMP_FRAMESTATS; for (int i = 0; i < values.length; i++) {
break; if (values[i].equals(value)) return i;
case "reset":
flags |= FLAG_DUMP_RESET;
break;
}
} }
nDumpProfileInfo(mNativeProxy, fd, flags); 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 @Override
boolean loadSystemProperties() { boolean loadSystemProperties() {
boolean changed = nLoadSystemProperties(mNativeProxy); boolean changed = nLoadSystemProperties(mNativeProxy);
boolean wantProfiling = checkIfProfilingRequested();
if (wantProfiling != mProfilingEnabled) {
mProfilingEnabled = wantProfiling;
changed = true;
}
if (changed) { if (changed) {
invalidateRoot(); invalidateRoot();
} }
@@ -310,12 +307,20 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override @Override
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
attachInfo.mIgnoreDirtyState = true; attachInfo.mIgnoreDirtyState = true;
long frameTimeNanos = mChoreographer.getFrameTimeNanos();
attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; long recordDuration = 0;
choreographer.mFrameInfo.markDrawStart(); if (mProfilingEnabled) {
recordDuration = System.nanoTime();
}
updateRootDisplayList(view, callbacks); updateRootDisplayList(view, callbacks);
if (mProfilingEnabled) {
recordDuration = System.nanoTime() - recordDuration;
}
attachInfo.mIgnoreDirtyState = false; attachInfo.mIgnoreDirtyState = false;
// register animating rendernodes which started animating prior to renderer // register animating rendernodes which started animating prior to renderer
@@ -332,8 +337,8 @@ public class ThreadedRenderer extends HardwareRenderer {
attachInfo.mPendingAnimatingRenderNodes = null; attachInfo.mPendingAnimatingRenderNodes = null;
} }
final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo; int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); recordDuration, view.getResources().getDisplayMetrics().density);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false); setEnabled(false);
attachInfo.mViewRootImpl.mSurface.release(); attachInfo.mViewRootImpl.mSurface.release();
@@ -495,9 +500,10 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native boolean nPauseSurface(long nativeProxy, Surface window); private static native boolean nPauseSurface(long nativeProxy, Surface window);
private static native void nSetup(long nativeProxy, int width, int height, private static native void nSetup(long nativeProxy, int width, int height,
float lightX, float lightY, float lightZ, float lightRadius, float lightX, float lightY, float lightZ, float lightRadius,
int ambientShadowAlpha, int spotShadowAlpha, float density); int ambientShadowAlpha, int spotShadowAlpha);
private static native void nSetOpaque(long nativeProxy, boolean opaque); private static native void nSetOpaque(long nativeProxy, boolean opaque);
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); private static native int nSyncAndDrawFrame(long nativeProxy,
long frameTimeNanos, long recordDuration, float density);
private static native void nDestroy(long nativeProxy); private static native void nDestroy(long nativeProxy);
private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
@@ -517,6 +523,5 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native void nStopDrawing(long nativeProxy); private static native void nStopDrawing(long nativeProxy);
private static native void nNotifyFramePending(long nativeProxy); private static native void nNotifyFramePending(long nativeProxy);
private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
@DumpFlags int dumpFlags);
} }

View File

@@ -56,7 +56,6 @@ import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.util.TimeUtils;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException; import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo; import android.view.View.AttachInfo;
@@ -1503,7 +1502,6 @@ public final class ViewRootImpl implements ViewParent,
// to resume them // to resume them
mDirty.set(0, 0, mWidth, mHeight); mDirty.set(0, 0, mWidth, mHeight);
} }
mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
} }
final int surfaceGenerationId = mSurface.getGenerationId(); final int surfaceGenerationId = mSurface.getGenerationId();
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
@@ -2507,9 +2505,6 @@ public final class ViewRootImpl implements ViewParent,
} }
} }
mAttachInfo.mDrawingTime =
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// If accessibility focus moved, always invalidate the root. // If accessibility focus moved, always invalidate the root.
@@ -2629,6 +2624,7 @@ public final class ViewRootImpl implements ViewParent,
dirty.setEmpty(); dirty.setEmpty();
mIsAnimating = false; mIsAnimating = false;
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
mView.mPrivateFlags |= View.PFLAG_DRAWN; mView.mPrivateFlags |= View.PFLAG_DRAWN;
if (DEBUG_DRAW) { if (DEBUG_DRAW) {
@@ -5782,16 +5778,6 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount); mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
deliverInputEvent(q); deliverInputEvent(q);
} }

View File

@@ -459,7 +459,7 @@ public final class WindowManagerGlobal {
} }
} }
public void dumpGfxInfo(FileDescriptor fd, String[] args) { public void dumpGfxInfo(FileDescriptor fd) {
FileOutputStream fout = new FileOutputStream(fd); FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new FastPrintWriter(fout); PrintWriter pw = new FastPrintWriter(fout);
try { try {
@@ -476,7 +476,7 @@ public final class WindowManagerGlobal {
HardwareRenderer renderer = HardwareRenderer renderer =
root.getView().mAttachInfo.mHardwareRenderer; root.getView().mAttachInfo.mHardwareRenderer;
if (renderer != null) { if (renderer != null) {
renderer.dumpGfxInfo(pw, fd, args); renderer.dumpGfxInfo(pw, fd);
} }
} }

View File

@@ -49,7 +49,6 @@
#include <AnimationContext.h> #include <AnimationContext.h>
#include <DisplayListRenderer.h> #include <DisplayListRenderer.h>
#include <FrameInfo.h>
#include <RenderNode.h> #include <RenderNode.h>
#include <renderthread/RenderProxy.h> #include <renderthread/RenderProxy.h>
@@ -395,7 +394,7 @@ static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfaceP
proxy->initialize(surface); proxy->initialize(surface);
// Shadows can't be used via this interface, so just set the light source // Shadows can't be used via this interface, so just set the light source
// to all 0s. (and width & height are unused, TODO remove them) // to all 0s. (and width & height are unused, TODO remove them)
proxy->setup(0, 0, (Vector3){0, 0, 0}, 0, 0, 0, 1.0f); proxy->setup(0, 0, (Vector3){0, 0, 0}, 0, 0, 0);
return (jlong) proxy; return (jlong) proxy;
} }
@@ -407,11 +406,8 @@ static void setSurface(JNIEnv* env, jclass clazz, jlong rendererPtr, jlong surfa
static void draw(JNIEnv* env, jclass clazz, jlong rendererPtr) { static void draw(JNIEnv* env, jclass clazz, jlong rendererPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(rendererPtr); RenderProxy* proxy = reinterpret_cast<RenderProxy*>(rendererPtr);
nsecs_t vsync = systemTime(CLOCK_MONOTONIC); nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo()) proxy->syncAndDrawFrame(frameTimeNs, 0, 1.0f);
.setVsync(vsync, vsync)
.addFlag(FrameInfoFlags::kSurfaceCanvas);
proxy->syncAndDrawFrame();
} }
static void destroy(JNIEnv* env, jclass clazz, jlong rendererPtr) { static void destroy(JNIEnv* env, jclass clazz, jlong rendererPtr) {

View File

@@ -283,10 +283,10 @@ static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject
static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr, static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
jint width, jint height, jint width, jint height,
jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius, jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius,
jint ambientShadowAlpha, jint spotShadowAlpha, jfloat density) { jint ambientShadowAlpha, jint spotShadowAlpha) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
proxy->setup(width, height, (Vector3){lightX, lightY, lightZ}, lightRadius, proxy->setup(width, height, (Vector3){lightX, lightY, lightZ}, lightRadius,
ambientShadowAlpha, spotShadowAlpha, density); ambientShadowAlpha, spotShadowAlpha);
} }
static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
@@ -296,13 +296,9 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
} }
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density) {
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
"Mismatched size expectations, given %d expected %d",
frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density);
return proxy->syncAndDrawFrame();
} }
static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz, static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
@@ -397,10 +393,10 @@ static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobjec
} }
static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz, static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) { jlong proxyPtr, jobject javaFileDescriptor) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
proxy->dumpProfileInfo(fd, dumpFlags); proxy->dumpProfileInfo(fd);
} }
#endif #endif
@@ -434,9 +430,9 @@ static JNINativeMethod gMethods[] = {
{ "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize }, { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface }, { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
{ "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface }, { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
{ "nSetup", "(JIIFFFFIIF)V", (void*) android_view_ThreadedRenderer_setup }, { "nSetup", "(JIIFFFFII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
{ "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroy", "(J)V", (void*) android_view_ThreadedRenderer_destroy }, { "nDestroy", "(J)V", (void*) android_view_ThreadedRenderer_destroy },
{ "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
{ "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
@@ -451,7 +447,7 @@ static JNINativeMethod gMethods[] = {
{ "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
{ "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing }, { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
#endif #endif
{ "setupShadersDiskCache", "(Ljava/lang/String;)V", { "setupShadersDiskCache", "(Ljava/lang/String;)V",
(void*) android_view_ThreadedRenderer_setupShadersDiskCache }, (void*) android_view_ThreadedRenderer_setupShadersDiskCache },

View File

@@ -18,7 +18,6 @@ ifeq ($(USE_OPENGL_RENDERER),true)
AssetAtlas.cpp \ AssetAtlas.cpp \
DamageAccumulator.cpp \ DamageAccumulator.cpp \
FontRenderer.cpp \ FontRenderer.cpp \
FrameInfo.cpp \
GammaFontRenderer.cpp \ GammaFontRenderer.cpp \
Caches.cpp \ Caches.cpp \
DisplayList.cpp \ DisplayList.cpp \
@@ -33,7 +32,6 @@ ifeq ($(USE_OPENGL_RENDERER),true)
GradientCache.cpp \ GradientCache.cpp \
Image.cpp \ Image.cpp \
Interpolator.cpp \ Interpolator.cpp \
JankTracker.cpp \
Layer.cpp \ Layer.cpp \
LayerCache.cpp \ LayerCache.cpp \
LayerRenderer.cpp \ LayerRenderer.cpp \

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2015 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 "FrameInfo.h"
#include <cstring>
namespace android {
namespace uirenderer {
void FrameInfo::importUiThreadInfo(int64_t* info) {
memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
}
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2015 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 FRAMEINFO_H_
#define FRAMEINFO_H_
#include "utils/Macros.h"
#include <cutils/compiler.h>
#include <utils/Timers.h>
#include <memory.h>
namespace android {
namespace uirenderer {
#define UI_THREAD_FRAME_INFO_SIZE 9
HWUI_ENUM(FrameInfoIndex,
kFlags = 0,
kIntendedVsync,
kVsync,
kOldestInputEvent,
kNewestInputEvent,
kHandleInputStart,
kAnimationStart,
kPerformTraversalsStart,
kDrawStart,
// End of UI frame info
kSyncStart,
kIssueDrawCommandsStart,
kSwapBuffers,
kFrameCompleted,
// Must be the last value!
kNumIndexes
);
HWUI_ENUM(FrameInfoFlags,
kWindowLayoutChanged = 1 << 0,
kRTAnimation = 1 << 1,
kSurfaceCanvas = 1 << 2,
);
class ANDROID_API UiFrameInfoBuilder {
public:
UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
}
UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
mBuffer[FrameInfoIndex::kVsync] = vsyncTime;
mBuffer[FrameInfoIndex::kIntendedVsync] = intendedVsync;
return *this;
}
UiFrameInfoBuilder& addFlag(FrameInfoFlagsEnum flag) {
mBuffer[FrameInfoIndex::kFlags] |= static_cast<uint64_t>(flag);
return *this;
}
private:
int64_t* mBuffer;
};
class FrameInfo {
public:
void importUiThreadInfo(int64_t* info);
void markSyncStart() {
mFrameInfo[FrameInfoIndex::kSyncStart] = systemTime(CLOCK_MONOTONIC);
}
void markIssueDrawCommandsStart() {
mFrameInfo[FrameInfoIndex::kIssueDrawCommandsStart] = systemTime(CLOCK_MONOTONIC);
}
void markSwapBuffers() {
mFrameInfo[FrameInfoIndex::kSwapBuffers] = systemTime(CLOCK_MONOTONIC);
}
void markFrameCompleted() {
mFrameInfo[FrameInfoIndex::kFrameCompleted] = systemTime(CLOCK_MONOTONIC);
}
int64_t operator[](FrameInfoIndexEnum index) const {
if (index == FrameInfoIndex::kNumIndexes) return 0;
return mFrameInfo[static_cast<int>(index)];
}
int64_t operator[](int index) const {
if (index < 0 || index >= FrameInfoIndex::kNumIndexes) return 0;
return mFrameInfo[static_cast<int>(index)];
}
private:
int64_t mFrameInfo[FrameInfoIndex::kNumIndexes];
};
} /* namespace uirenderer */
} /* namespace android */
#endif /* FRAMEINFO_H_ */

View File

@@ -1,159 +0,0 @@
/*
* Copyright (C) 2015 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 "JankTracker.h"
#include <algorithm>
#include <cstdio>
#include <inttypes.h>
namespace android {
namespace uirenderer {
static const char* JANK_TYPE_NAMES[] = {
"Missed Vsync",
"High input latency",
"Slow UI thread",
"Slow bitmap uploads",
"Slow draw",
};
struct Comparison {
FrameInfoIndexEnum start;
FrameInfoIndexEnum end;
};
static const Comparison COMPARISONS[] = {
{FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
{FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
{FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
{FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
{FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
};
// If the event exceeds 10 seconds throw it away, this isn't a jank event
// it's an ANR and will be handled as such
static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
/*
* Frames that are exempt from jank metrics.
* First-draw frames, for example, are expected to
* be slow, this is hidden from the user with window animations and
* other tricks
*
* Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
* for now
*
* TODO: kSurfaceCanvas can negatively impact other drawing by using up
* time on the RenderThread, figure out how to attribute that as a jank-causer
*/
static const int64_t EXEMPT_FRAMES_FLAGS
= FrameInfoFlags::kWindowLayoutChanged
| FrameInfoFlags::kSurfaceCanvas;
JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
reset();
setFrameInterval(frameIntervalNanos);
}
void JankTracker::setFrameInterval(nsecs_t frameInterval) {
mFrameInterval = frameInterval;
mThresholds[kMissedVsync] = 1;
/*
* Due to interpolation and sample rate differences between the touch
* panel and the display (example, 85hz touch panel driving a 60hz display)
* we call high latency 1.5 * frameinterval
*
* NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
* on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
* Thus this must always be larger than frameInterval, or it will fail
*/
mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
// Note that these do not add up to 1. This is intentional. It's to deal
// with variance in values, and should be sort of an upper-bound on what
// is reasonable to expect.
mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
}
void JankTracker::addFrame(const FrameInfo& frame) {
using namespace FrameInfoIndex;
mTotalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration = frame[kFrameCompleted] - frame[kIntendedVsync];
uint32_t framebucket = std::min(
static_cast<typeof sizeof(mFrameCounts)>(ns2ms(totalDuration)),
sizeof(mFrameCounts) / sizeof(mFrameCounts[0]));
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
mFrameCounts[framebucket]++;
return;
}
if (frame[kFlags] & EXEMPT_FRAMES_FLAGS) {
return;
}
mFrameCounts[framebucket]++;
mJankFrameCount++;
for (int i = 0; i < NUM_BUCKETS; i++) {
int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
mBuckets[i].count++;
}
}
}
void JankTracker::dump(int fd) {
FILE* file = fdopen(fd, "a");
fprintf(file, "\nFrame stats:");
fprintf(file, "\n Total frames rendered: %u", mTotalFrameCount);
fprintf(file, "\n Janky frames: %u (%.2f%%)", mJankFrameCount,
(float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
fprintf(file, "\n 90th percentile: %ums", findPercentile(90));
fprintf(file, "\n 95th percentile: %ums", findPercentile(95));
fprintf(file, "\n 99th percentile: %ums", findPercentile(99));
for (int i = 0; i < NUM_BUCKETS; i++) {
fprintf(file, "\n Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
}
fprintf(file, "\n");
fflush(file);
}
void JankTracker::reset() {
memset(mBuckets, 0, sizeof(mBuckets));
memset(mFrameCounts, 0, sizeof(mFrameCounts));
mTotalFrameCount = 0;
mJankFrameCount = 0;
}
uint32_t JankTracker::findPercentile(int percentile) {
int pos = percentile * mTotalFrameCount / 100;
int remaining = mTotalFrameCount - pos;
for (int i = sizeof(mFrameCounts) / sizeof(mFrameCounts[0]) - 1; i >= 0; i--) {
remaining -= mFrameCounts[i];
if (remaining <= 0) {
return i;
}
}
return 0;
}
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2015 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 JANKTRACKER_H_
#define JANKTRACKER_H_
#include "FrameInfo.h"
#include "renderthread/TimeLord.h"
#include "utils/RingBuffer.h"
#include <memory>
namespace android {
namespace uirenderer {
enum JankType {
kMissedVsync = 0,
kHighInputLatency,
kSlowUI,
kSlowSync,
kSlowRT,
// must be last
NUM_BUCKETS,
};
struct JankBucket {
// Number of frames that hit this bucket
uint32_t count;
};
// TODO: Replace DrawProfiler with this
class JankTracker {
public:
JankTracker(nsecs_t frameIntervalNanos);
void setFrameInterval(nsecs_t frameIntervalNanos);
void addFrame(const FrameInfo& frame);
void dump(int fd);
void reset();
private:
uint32_t findPercentile(int p);
JankBucket mBuckets[NUM_BUCKETS];
int64_t mThresholds[NUM_BUCKETS];
uint32_t mFrameCounts[128];
int64_t mFrameInterval;
uint32_t mTotalFrameCount;
uint32_t mJankFrameCount;
};
} /* namespace uirenderer */
} /* namespace android */
#endif /* JANKTRACKER_H_ */

View File

@@ -47,8 +47,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
, mOpaque(!translucent) , mOpaque(!translucent)
, mCanvas(NULL) , mCanvas(NULL)
, mHaveNewSurface(false) , mHaveNewSurface(false)
, mRootRenderNode(rootRenderNode) , mRootRenderNode(rootRenderNode) {
, mCurrentFrameInfo(NULL) {
mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord()); mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord());
mRenderThread.renderState().registerCanvasContext(this); mRenderThread.renderState().registerCanvasContext(this);
} }
@@ -153,13 +152,9 @@ void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
} }
} }
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) { void CanvasContext::prepareTree(TreeInfo& info) {
mRenderThread.removeFrameCallback(this); mRenderThread.removeFrameCallback(this);
mCurrentFrameInfo = &mFrames.next();
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->markSyncStart();
info.damageAccumulator = &mDamageAccumulator; info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas; info.renderer = mCanvas;
if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) { if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
@@ -209,7 +204,6 @@ void CanvasContext::draw() {
"drawRenderNode called on a context with no canvas or surface!"); "drawRenderNode called on a context with no canvas or surface!");
profiler().markPlaybackStart(); profiler().markPlaybackStart();
mCurrentFrameInfo->markIssueDrawCommandsStart();
SkRect dirty; SkRect dirty;
mDamageAccumulator.finish(&dirty); mDamageAccumulator.finish(&dirty);
@@ -247,19 +241,12 @@ void CanvasContext::draw() {
profiler().markPlaybackEnd(); profiler().markPlaybackEnd();
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
mCurrentFrameInfo->markSwapBuffers();
if (status & DrawGlInfo::kStatusDrew) { if (status & DrawGlInfo::kStatusDrew) {
swapBuffers(); swapBuffers();
} else { } else {
mEglManager.cancelFrame(); mEglManager.cancelFrame();
} }
// TODO: Use a fence for real completion?
mCurrentFrameInfo->markFrameCompleted();
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
profiler().finishFrame(); profiler().finishFrame();
} }
@@ -272,14 +259,9 @@ void CanvasContext::doFrame() {
ATRACE_CALL(); ATRACE_CALL();
profiler().startFrame(); profiler().startFrame();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
UiFrameInfoBuilder(frameInfo)
.addFlag(FrameInfoFlags::kRTAnimation)
.setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
mRenderThread.timeLord().latestVsync());
TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
prepareTree(info, frameInfo); prepareTree(info);
if (info.out.canDrawThisFrame) { if (info.out.canDrawThisFrame) {
draw(); draw();
} }
@@ -392,28 +374,6 @@ void CanvasContext::setTextureAtlas(RenderThread& thread,
thread.eglManager().setTextureAtlas(buffer, map, mapSize); thread.eglManager().setTextureAtlas(buffer, map, mapSize);
} }
void CanvasContext::dumpFrames(int fd) {
FILE* file = fdopen(fd, "a");
fprintf(file, "\n\n---PROFILEDATA---");
for (size_t i = 0; i < mFrames.size(); i++) {
FrameInfo& frame = mFrames[i];
if (frame[FrameInfoIndex::kSyncStart] == 0) {
continue;
}
fprintf(file, "\n");
for (int i = 0; i < FrameInfoIndex::kNumIndexes; i++) {
fprintf(file, "%" PRId64 ",", frame[i]);
}
}
fprintf(file, "\n---PROFILEDATA---\n\n");
fflush(file);
}
void CanvasContext::resetFrameStats() {
mFrames.clear();
mRenderThread.jankTracker().reset();
}
} /* namespace renderthread */ } /* namespace renderthread */
} /* namespace uirenderer */ } /* namespace uirenderer */
} /* namespace android */ } /* namespace android */

View File

@@ -17,14 +17,7 @@
#ifndef CANVASCONTEXT_H_ #ifndef CANVASCONTEXT_H_
#define CANVASCONTEXT_H_ #define CANVASCONTEXT_H_
#include "DamageAccumulator.h" #include <set>
#include "DrawProfiler.h"
#include "IContextFactory.h"
#include "FrameInfo.h"
#include "RenderNode.h"
#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include <cutils/compiler.h> #include <cutils/compiler.h>
#include <EGL/egl.h> #include <EGL/egl.h>
@@ -32,7 +25,14 @@
#include <utils/Functor.h> #include <utils/Functor.h>
#include <utils/Vector.h> #include <utils/Vector.h>
#include <set> #include "../DamageAccumulator.h"
#include "../DrawProfiler.h"
#include "../IContextFactory.h"
#include "../RenderNode.h"
#include "RenderTask.h"
#include "RenderThread.h"
#define FUNCTOR_PROCESS_DELAY 4
namespace android { namespace android {
namespace uirenderer { namespace uirenderer {
@@ -75,7 +75,7 @@ public:
void setOpaque(bool opaque); void setOpaque(bool opaque);
void makeCurrent(); void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater); void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo); void prepareTree(TreeInfo& info);
void draw(); void draw();
void destroy(); void destroy();
@@ -103,9 +103,6 @@ public:
DrawProfiler& profiler() { return mProfiler; } DrawProfiler& profiler() { return mProfiler; }
void dumpFrames(int fd);
void resetFrameStats();
private: private:
friend class RegisterFrameCallbackTask; friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object // TODO: Replace with something better for layer & other GL object
@@ -136,9 +133,6 @@ private:
const sp<RenderNode> mRootRenderNode; const sp<RenderNode> mRootRenderNode;
DrawProfiler mProfiler; DrawProfiler mProfiler;
FrameInfo* mCurrentFrameInfo;
// Ring buffer large enough for 1 second worth of frames
RingBuffer<FrameInfo, 60> mFrames;
std::set<RenderNode*> mPrefetechedLayers; std::set<RenderNode*> mPrefetechedLayers;
}; };

View File

@@ -34,6 +34,8 @@ namespace renderthread {
DrawFrameTask::DrawFrameTask() DrawFrameTask::DrawFrameTask()
: mRenderThread(NULL) : mRenderThread(NULL)
, mContext(NULL) , mContext(NULL)
, mFrameTimeNanos(0)
, mRecordDurationNanos(0)
, mDensity(1.0f) // safe enough default , mDensity(1.0f) // safe enough default
, mSyncResult(kSync_OK) { , mSyncResult(kSync_OK) {
} }
@@ -66,12 +68,18 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
} }
} }
int DrawFrameTask::drawFrame() { int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
mSyncResult = kSync_OK; mSyncResult = kSync_OK;
mFrameTimeNanos = frameTimeNanos;
mRecordDurationNanos = recordDurationNanos;
postAndWait(); postAndWait();
// Reset the single-frame data
mFrameTimeNanos = 0;
mRecordDurationNanos = 0;
return mSyncResult; return mSyncResult;
} }
@@ -85,7 +93,7 @@ void DrawFrameTask::run() {
ATRACE_NAME("DrawFrame"); ATRACE_NAME("DrawFrame");
mContext->profiler().setDensity(mDensity); mContext->profiler().setDensity(mDensity);
mContext->profiler().startFrame(); mContext->profiler().startFrame(mRecordDurationNanos);
bool canUnblockUiThread; bool canUnblockUiThread;
bool canDrawThisFrame; bool canDrawThisFrame;
@@ -114,7 +122,7 @@ void DrawFrameTask::run() {
bool DrawFrameTask::syncFrameState(TreeInfo& info) { bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL(); ATRACE_CALL();
mRenderThread->timeLord().vsyncReceived(mFrameInfo[FrameInfoIndex::kVsync]); mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
mContext->makeCurrent(); mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse(); Caches::getInstance().textureCache.resetMarkInUse();
@@ -122,7 +130,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mContext->processLayerUpdate(mLayers[i].get()); mContext->processLayerUpdate(mLayers[i].get());
} }
mLayers.clear(); mLayers.clear();
mContext->prepareTree(info, mFrameInfo); mContext->prepareTree(info);
// This is after the prepareTree so that any pending operations // This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed. // (RenderNode tree state, prefetched layers, etc...) will be flushed.

View File

@@ -25,7 +25,6 @@
#include "RenderTask.h" #include "RenderTask.h"
#include "../Rect.h" #include "../Rect.h"
#include "../FrameInfo.h"
#include "../TreeInfo.h" #include "../TreeInfo.h"
namespace android { namespace android {
@@ -63,9 +62,7 @@ public:
void removeLayerUpdate(DeferredLayerUpdater* layer); void removeLayerUpdate(DeferredLayerUpdater* layer);
void setDensity(float density) { mDensity = density; } void setDensity(float density) { mDensity = density; }
int drawFrame(); int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
int64_t* frameInfo() { return mFrameInfo; }
virtual void run(); virtual void run();
@@ -83,12 +80,12 @@ private:
/********************************************* /*********************************************
* Single frame data * Single frame data
*********************************************/ *********************************************/
nsecs_t mFrameTimeNanos;
nsecs_t mRecordDurationNanos;
float mDensity; float mDensity;
std::vector< sp<DeferredLayerUpdater> > mLayers; std::vector< sp<DeferredLayerUpdater> > mLayers;
int mSyncResult; int mSyncResult;
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
}; };
} /* namespace renderthread */ } /* namespace renderthread */

View File

@@ -16,14 +16,14 @@
#include "RenderProxy.h" #include "RenderProxy.h"
#include "DeferredLayerUpdater.h" #include "CanvasContext.h"
#include "DisplayList.h" #include "RenderTask.h"
#include "LayerRenderer.h" #include "RenderThread.h"
#include "Rect.h"
#include "renderthread/CanvasContext.h" #include "../DeferredLayerUpdater.h"
#include "renderthread/RenderTask.h" #include "../DisplayList.h"
#include "renderthread/RenderThread.h" #include "../LayerRenderer.h"
#include "utils/Macros.h" #include "../Rect.h"
namespace android { namespace android {
namespace uirenderer { namespace uirenderer {
@@ -52,11 +52,6 @@ namespace renderthread {
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
ARGS(method) *args = (ARGS(method) *) task->payload() ARGS(method) *args = (ARGS(method) *) task->payload()
HWUI_ENUM(DumpFlags,
kFrameStats = 1 << 0,
kReset = 1 << 1,
);
CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory) { RenderNode* rootRenderNode, IContextFactory* contextFactory) {
return new CanvasContext(*args->thread, args->translucent, return new CanvasContext(*args->thread, args->translucent,
@@ -97,7 +92,7 @@ void RenderProxy::destroyContext() {
} }
CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) { CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) {
args->thread->setFrameInterval(args->frameIntervalNanos); args->thread->timeLord().setFrameInterval(args->frameIntervalNanos);
return NULL; return NULL;
} }
@@ -180,8 +175,7 @@ CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height,
} }
void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius, void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha, float density) { uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
mDrawFrameTask.setDensity(density);
SETUP_TASK(setup); SETUP_TASK(setup);
args->context = mContext; args->context = mContext;
args->width = width; args->width = width;
@@ -205,12 +199,10 @@ void RenderProxy::setOpaque(bool opaque) {
post(task); post(task);
} }
int64_t* RenderProxy::frameInfo() { int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
return mDrawFrameTask.frameInfo(); float density) {
} mDrawFrameTask.setDensity(density);
return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
int RenderProxy::syncAndDrawFrame() {
return mDrawFrameTask.drawFrame();
} }
CREATE_BRIDGE1(destroy, CanvasContext* context) { CREATE_BRIDGE1(destroy, CanvasContext* context) {
@@ -379,28 +371,19 @@ void RenderProxy::notifyFramePending() {
mRenderThread.queueAtFront(task); mRenderThread.queueAtFront(task);
} }
CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) { CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) {
args->context->profiler().dumpData(args->fd); args->context->profiler().dumpData(args->fd);
if (args->dumpFlags & DumpFlags::kFrameStats) {
args->context->dumpFrames(args->fd);
}
if (args->dumpFlags & DumpFlags::kReset) {
args->context->resetFrameStats();
}
return NULL; return NULL;
} }
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { void RenderProxy::dumpProfileInfo(int fd) {
SETUP_TASK(dumpProfileInfo); SETUP_TASK(dumpProfileInfo);
args->context = mContext; args->context = mContext;
args->fd = fd; args->fd = fd;
args->dumpFlags = dumpFlags;
postAndWait(task); postAndWait(task);
} }
CREATE_BRIDGE2(outputLogBuffer, int fd, RenderThread* thread) { CREATE_BRIDGE1(outputLogBuffer, int fd) {
args->thread->jankTracker().dump(args->fd);
RenderNode::outputLogBuffer(args->fd); RenderNode::outputLogBuffer(args->fd);
return NULL; return NULL;
} }
@@ -408,7 +391,6 @@ CREATE_BRIDGE2(outputLogBuffer, int fd, RenderThread* thread) {
void RenderProxy::outputLogBuffer(int fd) { void RenderProxy::outputLogBuffer(int fd) {
SETUP_TASK(outputLogBuffer); SETUP_TASK(outputLogBuffer);
args->fd = fd; args->fd = fd;
args->thread = &RenderThread::getInstance();
staticPostAndWait(task); staticPostAndWait(task);
} }

View File

@@ -71,10 +71,10 @@ public:
ANDROID_API void updateSurface(const sp<ANativeWindow>& window); ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window); ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window);
ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius, ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha, float density); uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setOpaque(bool opaque); ANDROID_API void setOpaque(bool opaque);
ANDROID_API int64_t* frameInfo(); ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
ANDROID_API int syncAndDrawFrame(); float density);
ANDROID_API void destroy(); ANDROID_API void destroy();
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
@@ -95,7 +95,7 @@ public:
ANDROID_API void stopDrawing(); ANDROID_API void stopDrawing();
ANDROID_API void notifyFramePending(); ANDROID_API void notifyFramePending();
ANDROID_API void dumpProfileInfo(int fd, int dumpFlags); ANDROID_API void dumpProfileInfo(int fd);
ANDROID_API static void outputLogBuffer(int fd); ANDROID_API static void outputLogBuffer(int fd);
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);

View File

@@ -143,8 +143,7 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
, mFrameCallbackTaskPending(false) , mFrameCallbackTaskPending(false)
, mFrameCallbackTask(0) , mFrameCallbackTask(0)
, mRenderState(NULL) , mRenderState(NULL)
, mEglManager(NULL) , mEglManager(NULL) {
, mJankTracker(NULL) {
mFrameCallbackTask = new DispatchFrameCallbacks(this); mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false); mLooper = new Looper(false);
run("RenderThread"); run("RenderThread");
@@ -154,11 +153,6 @@ RenderThread::~RenderThread() {
LOG_ALWAYS_FATAL("Can't destroy the render thread"); LOG_ALWAYS_FATAL("Can't destroy the render thread");
} }
void RenderThread::setFrameInterval(nsecs_t frameInterval) {
mTimeLord.setFrameInterval(frameInterval);
mJankTracker->setFrameInterval(frameInterval);
}
void RenderThread::initializeDisplayEventReceiver() { void RenderThread::initializeDisplayEventReceiver() {
LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
mDisplayEventReceiver = new DisplayEventReceiver(); mDisplayEventReceiver = new DisplayEventReceiver();
@@ -175,7 +169,6 @@ void RenderThread::initThreadLocals() {
initializeDisplayEventReceiver(); initializeDisplayEventReceiver();
mEglManager = new EglManager(*this); mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this); mRenderState = new RenderState(*this);
mJankTracker = new JankTracker(mTimeLord.frameIntervalNanos());
} }
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {

View File

@@ -19,8 +19,8 @@
#include "RenderTask.h" #include "RenderTask.h"
#include "../JankTracker.h" #include <memory>
#include "TimeLord.h" #include <set>
#include <cutils/compiler.h> #include <cutils/compiler.h>
#include <utils/Looper.h> #include <utils/Looper.h>
@@ -28,8 +28,7 @@
#include <utils/Singleton.h> #include <utils/Singleton.h>
#include <utils/Thread.h> #include <utils/Thread.h>
#include <memory> #include "TimeLord.h"
#include <set>
namespace android { namespace android {
@@ -86,12 +85,9 @@ public:
// the next vsync. If it is not currently registered this does nothing. // the next vsync. If it is not currently registered this does nothing.
void pushBackFrameCallback(IFrameCallback* callback); void pushBackFrameCallback(IFrameCallback* callback);
void setFrameInterval(nsecs_t frameInterval);
TimeLord& timeLord() { return mTimeLord; } TimeLord& timeLord() { return mTimeLord; }
RenderState& renderState() { return *mRenderState; } RenderState& renderState() { return *mRenderState; }
EglManager& eglManager() { return *mEglManager; } EglManager& eglManager() { return *mEglManager; }
JankTracker& jankTracker() { return *mJankTracker; }
protected: protected:
virtual bool threadLoop(); virtual bool threadLoop();
@@ -136,8 +132,6 @@ private:
TimeLord mTimeLord; TimeLord mTimeLord;
RenderState* mRenderState; RenderState* mRenderState;
EglManager* mEglManager; EglManager* mEglManager;
JankTracker* mJankTracker;
}; };
} /* namespace renderthread */ } /* namespace renderthread */

View File

@@ -32,7 +32,7 @@ bool TimeLord::vsyncReceived(nsecs_t vsync) {
return false; return false;
} }
nsecs_t TimeLord::computeFrameTimeNanos() { nsecs_t TimeLord::computeFrameTimeMs() {
// Logic copied from Choreographer.java // Logic copied from Choreographer.java
nsecs_t now = systemTime(CLOCK_MONOTONIC); nsecs_t now = systemTime(CLOCK_MONOTONIC);
nsecs_t jitterNanos = now - mFrameTimeNanos; nsecs_t jitterNanos = now - mFrameTimeNanos;
@@ -40,11 +40,7 @@ nsecs_t TimeLord::computeFrameTimeNanos() {
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
mFrameTimeNanos = now - lastFrameOffset; mFrameTimeNanos = now - lastFrameOffset;
} }
return mFrameTimeNanos; return nanoseconds_to_milliseconds(mFrameTimeNanos);
}
nsecs_t TimeLord::computeFrameTimeMs() {
return nanoseconds_to_milliseconds(computeFrameTimeNanos());
} }
} /* namespace renderthread */ } /* namespace renderthread */

View File

@@ -29,13 +29,9 @@ class RenderThread;
class TimeLord { class TimeLord {
public: public:
void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; } void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
// returns true if the vsync is newer, false if it was rejected for staleness // returns true if the vsync is newer, false if it was rejected for staleness
bool vsyncReceived(nsecs_t vsync); bool vsyncReceived(nsecs_t vsync);
nsecs_t latestVsync() { return mFrameTimeNanos; }
nsecs_t computeFrameTimeMs(); nsecs_t computeFrameTimeMs();
nsecs_t computeFrameTimeNanos();
private: private:
friend class RenderThread; friend class RenderThread;

View File

@@ -88,7 +88,7 @@ int main(int argc, char* argv[]) {
proxy->initialize(surface); proxy->initialize(surface);
float lightX = width / 2.0; float lightX = width / 2.0;
proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)}, proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
dp(800.0f), 255 * 0.075, 255 * 0.15, gDisplay.density); dp(800.0f), 255 * 0.075, 255 * 0.15);
android::uirenderer::Rect DUMMY; android::uirenderer::Rect DUMMY;
@@ -116,7 +116,8 @@ int main(int argc, char* argv[]) {
cards[ci]->mutateStagingProperties().setTranslationY(i); cards[ci]->mutateStagingProperties().setTranslationY(i);
cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
} }
proxy->syncAndDrawFrame(); nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
usleep(12000); usleep(12000);
} }

View File

@@ -29,12 +29,4 @@
friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \ friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
friend inline hash_t hash_type(const Type& entry) { return entry.hash(); } friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
#define HWUI_ENUM(name, ...) \
namespace name { \
enum _##name { \
__VA_ARGS__ \
}; \
} \
typedef enum name::_##name name##Enum
#endif /* MACROS_H */ #endif /* MACROS_H */

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2015 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 RINGBUFFER_H_
#define RINGBUFFER_H_
#include "utils/Macros.h"
#include <stddef.h>
namespace android {
namespace uirenderer {
template<class T, size_t SIZE>
class RingBuffer {
PREVENT_COPY_AND_ASSIGN(RingBuffer);
public:
RingBuffer() : mHead(-1), mCount(0) {}
~RingBuffer() {}
size_t capacity() { return SIZE; }
size_t size() { return mCount; }
T& next() {
mHead = (mHead + 1) % SIZE;
if (mCount < SIZE) {
mCount++;
}
return mBuffer[mHead];
}
T& front() {
return this[0];
}
T& back() {
return this[size() - 1];
}
T& operator[](size_t index) {
return mBuffer[(mHead + index + 1) % mCount];
}
void clear() {
mCount = 0;
mHead = -1;
}
private:
T mBuffer[SIZE];
int mHead;
size_t mCount;
};
}; // namespace uirenderer
}; // namespace android
#endif /* RINGBUFFER_H_ */