Merge "Coalesce input events that arrive faster than 333Hz."
This commit is contained in:
@@ -645,8 +645,21 @@ public abstract class HardwareRenderer {
|
||||
}
|
||||
|
||||
attachInfo.mIgnoreDirtyState = false;
|
||||
|
||||
|
||||
final long swapBuffersStartTime;
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
swapBuffersStartTime = System.nanoTime();
|
||||
}
|
||||
|
||||
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
long now = System.nanoTime();
|
||||
Log.d(LOG_TAG, "Latency: Spent "
|
||||
+ ((now - swapBuffersStartTime) * 0.000001f)
|
||||
+ "ms waiting for eglSwapBuffers()");
|
||||
}
|
||||
|
||||
checkEglErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,13 @@ public abstract class InputEvent implements Parcelable {
|
||||
*/
|
||||
public abstract void setTainted(boolean tainted);
|
||||
|
||||
/**
|
||||
* Returns the time (in ns) when this specific event was generated.
|
||||
* The value is in nanosecond precision but it may not have nanosecond accuracy.
|
||||
* @hide
|
||||
*/
|
||||
public abstract long getEventTimeNano();
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2340,6 +2340,12 @@ public class KeyEvent extends InputEvent implements Parcelable {
|
||||
return mEventTime;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public final long getEventTimeNano() {
|
||||
return mEventTime * 1000000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renamed to {@link #getDeviceId}.
|
||||
*
|
||||
|
||||
@@ -140,6 +140,22 @@ public class ViewDebug {
|
||||
*/
|
||||
public static final boolean DEBUG_DRAG = false;
|
||||
|
||||
/**
|
||||
* Enables logging of factors that affect the latency and responsiveness of an application.
|
||||
*
|
||||
* Logs the relative difference between the time an event was created and the time it
|
||||
* was delivered.
|
||||
*
|
||||
* Logs the time spent waiting for Surface.lockCanvas() or eglSwapBuffers().
|
||||
* This is time that the event loop spends blocked and unresponsive. Ideally, drawing
|
||||
* and animations should be perfectly synchronized with VSYNC so that swap buffers
|
||||
* is instantaneous.
|
||||
*
|
||||
* Logs the time spent in ViewRoot.performTraversals() or ViewRoot.draw().
|
||||
* @hide
|
||||
*/
|
||||
public static final boolean DEBUG_LATENCY = false;
|
||||
|
||||
/**
|
||||
* <p>Enables or disables views consistency check. Even when this property is enabled,
|
||||
* view consistency checks happen only if {@link android.util.Config#DEBUG} is set
|
||||
|
||||
@@ -186,6 +186,8 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
final Rect mVisRect; // used to retrieve visible rect of focused view.
|
||||
|
||||
boolean mTraversalScheduled;
|
||||
long mLastTraversalFinishedTimeNanos;
|
||||
long mLastDrawDurationNanos;
|
||||
boolean mWillDrawSoon;
|
||||
boolean mLayoutRequested;
|
||||
boolean mFirst;
|
||||
@@ -671,6 +673,14 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
public void scheduleTraversals() {
|
||||
if (!mTraversalScheduled) {
|
||||
mTraversalScheduled = true;
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
|
||||
final long now = System.nanoTime();
|
||||
Log.d(TAG, "Latency: Scheduled traversal, it has been "
|
||||
+ ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
|
||||
+ "ms since the last traversal finished.");
|
||||
}
|
||||
|
||||
sendEmptyMessage(DO_TRAVERSAL);
|
||||
}
|
||||
}
|
||||
@@ -1389,8 +1399,18 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
|
||||
if (!cancelDraw && !newSurface) {
|
||||
mFullRedrawNeeded = false;
|
||||
|
||||
final long drawStartTime;
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
drawStartTime = System.nanoTime();
|
||||
}
|
||||
|
||||
draw(fullRedrawNeeded);
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
|
||||
}
|
||||
|
||||
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|
||||
|| mReportNextDraw) {
|
||||
if (LOCAL_LOGV) {
|
||||
@@ -1601,8 +1621,20 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
int right = dirty.right;
|
||||
int bottom = dirty.bottom;
|
||||
|
||||
final long lockCanvasStartTime;
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
lockCanvasStartTime = System.nanoTime();
|
||||
}
|
||||
|
||||
canvas = surface.lockCanvas(dirty);
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
long now = System.nanoTime();
|
||||
Log.d(TAG, "Latency: Spent "
|
||||
+ ((now - lockCanvasStartTime) * 0.000001f)
|
||||
+ "ms waiting for surface.lockCanvas()");
|
||||
}
|
||||
|
||||
if (left != dirty.left || top != dirty.top || right != dirty.right ||
|
||||
bottom != dirty.bottom) {
|
||||
mAttachInfo.mIgnoreDirtyState = true;
|
||||
@@ -2011,8 +2043,24 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
Debug.startMethodTracing("ViewRoot");
|
||||
}
|
||||
|
||||
final long traversalStartTime;
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
traversalStartTime = System.nanoTime();
|
||||
mLastDrawDurationNanos = 0;
|
||||
}
|
||||
|
||||
performTraversals();
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
long now = System.nanoTime();
|
||||
Log.d(TAG, "Latency: Spent "
|
||||
+ ((now - traversalStartTime) * 0.000001f)
|
||||
+ "ms in performTraversals(), with "
|
||||
+ (mLastDrawDurationNanos * 0.000001f)
|
||||
+ "ms of that time in draw()");
|
||||
mLastTraversalFinishedTimeNanos = now;
|
||||
}
|
||||
|
||||
if (mProfile) {
|
||||
Debug.stopMethodTracing();
|
||||
mProfile = false;
|
||||
@@ -2180,25 +2228,68 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
}
|
||||
|
||||
private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
|
||||
private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
|
||||
if (mFinishedCallback != null) {
|
||||
Slog.w(TAG, "Received a new input event from the input queue but there is "
|
||||
+ "already an unfinished input event in progress.");
|
||||
}
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mInputEventReceiveTimeNanos = System.nanoTime();
|
||||
mInputEventDeliverTimeNanos = 0;
|
||||
mInputEventDeliverPostImeTimeNanos = 0;
|
||||
}
|
||||
|
||||
mFinishedCallback = finishedCallback;
|
||||
}
|
||||
|
||||
private void finishInputEvent(boolean handled) {
|
||||
private void finishInputEvent(InputEvent event, boolean handled) {
|
||||
if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
|
||||
|
||||
if (mFinishedCallback != null) {
|
||||
mFinishedCallback.finished(handled);
|
||||
mFinishedCallback = null;
|
||||
} else {
|
||||
if (mFinishedCallback == null) {
|
||||
Slog.w(TAG, "Attempted to tell the input queue that the current input event "
|
||||
+ "is finished but there is no input event actually in progress.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
final long now = System.nanoTime();
|
||||
final long eventTime = event.getEventTimeNano();
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
msg.append("Latency: Spent ");
|
||||
msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f);
|
||||
msg.append("ms processing ");
|
||||
if (event instanceof KeyEvent) {
|
||||
final KeyEvent keyEvent = (KeyEvent)event;
|
||||
msg.append("key event, action=");
|
||||
msg.append(KeyEvent.actionToString(keyEvent.getAction()));
|
||||
} else {
|
||||
final MotionEvent motionEvent = (MotionEvent)event;
|
||||
msg.append("motion event, action=");
|
||||
msg.append(MotionEvent.actionToString(motionEvent.getAction()));
|
||||
msg.append(", historySize=");
|
||||
msg.append(motionEvent.getHistorySize());
|
||||
}
|
||||
msg.append(", handled=");
|
||||
msg.append(handled);
|
||||
msg.append(", received at +");
|
||||
msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f);
|
||||
if (mInputEventDeliverTimeNanos != 0) {
|
||||
msg.append("ms, delivered at +");
|
||||
msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f);
|
||||
}
|
||||
if (mInputEventDeliverPostImeTimeNanos != 0) {
|
||||
msg.append("ms, delivered post IME at +");
|
||||
msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f);
|
||||
}
|
||||
msg.append("ms, finished at +");
|
||||
msg.append((now - eventTime) * 0.000001f);
|
||||
msg.append("ms.");
|
||||
Log.d(TAG, msg.toString());
|
||||
}
|
||||
|
||||
mFinishedCallback.finished(handled);
|
||||
mFinishedCallback = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2323,6 +2414,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mInputEventDeliverTimeNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
if (mInputEventConsistencyVerifier != null) {
|
||||
if (event.isTouchEvent()) {
|
||||
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
|
||||
@@ -2425,7 +2520,7 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
|
||||
event.recycle();
|
||||
if (sendDone) {
|
||||
finishInputEvent(handled);
|
||||
finishInputEvent(event, handled);
|
||||
}
|
||||
if (LOCAL_LOGV || WATCH_POINTER) {
|
||||
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
|
||||
@@ -2435,6 +2530,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mInputEventDeliverTimeNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
|
||||
|
||||
if (mInputEventConsistencyVerifier != null) {
|
||||
@@ -2569,6 +2668,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mInputEventDeliverTimeNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
if (mInputEventConsistencyVerifier != null) {
|
||||
mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
|
||||
}
|
||||
@@ -2808,6 +2911,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mInputEventDeliverTimeNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
if (mInputEventConsistencyVerifier != null) {
|
||||
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
|
||||
}
|
||||
@@ -2858,6 +2965,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
}
|
||||
|
||||
private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
|
||||
if (ViewDebug.DEBUG_LATENCY) {
|
||||
mInputEventDeliverPostImeTimeNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
// If the view went away, then the event will not be handled.
|
||||
if (mView == null || !mAdded) {
|
||||
finishKeyEvent(event, sendDone, false);
|
||||
@@ -2971,7 +3082,7 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
|
||||
private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
|
||||
if (sendDone) {
|
||||
finishInputEvent(handled);
|
||||
finishInputEvent(event, handled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3262,16 +3373,19 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
private long mInputEventReceiveTimeNanos;
|
||||
private long mInputEventDeliverTimeNanos;
|
||||
private long mInputEventDeliverPostImeTimeNanos;
|
||||
private InputQueue.FinishedCallback mFinishedCallback;
|
||||
|
||||
private final InputHandler mInputHandler = new InputHandler() {
|
||||
public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
|
||||
startInputEvent(finishedCallback);
|
||||
startInputEvent(event, finishedCallback);
|
||||
dispatchKey(event, true);
|
||||
}
|
||||
|
||||
public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
|
||||
startInputEvent(finishedCallback);
|
||||
startInputEvent(event, finishedCallback);
|
||||
dispatchMotion(event, true);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user