diff options
| author | 2016-07-29 17:16:03 +0200 | |
|---|---|---|
| committer | 2016-08-02 10:55:49 +0200 | |
| commit | dbe6fdb05be5dc7b988c542cbbde7788f15b775f (patch) | |
| tree | da09d9c3620729ace585d45bd1d5a2b3949cc92f | |
| parent | 66e7752ad15f761d09155976b37cb94fc36937d9 (diff) | |
Fix multi-window drag jank if vsync-app is before vsync-sf
If vsync-app is a couple of ms before vsync-sf, we handle the touch
in SystemUI at vsync-app. Then, we have extremely little time to resize
all the stacks in activity manager until vsync-sf, so sometimes this is
done before vsync-sf tick, and sometimes after, which leads to jank.
Figure out the difference of vsync-app and vsync-sf in SystemUI and then
post the updates to be processed at vsync-sf so we have the whole 16ms
to process the change.
Change-Id: Ibe7cb6dfe9fbfe2d3e68e522a95a75138fb0dcf1
Fixes: 30437123
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java | 77 |
1 files changed, 71 insertions, 6 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 998f50f205d7..cb77d7b29c9f 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -31,6 +31,7 @@ import android.graphics.Region.Op; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; +import android.os.Message; import android.util.AttributeSet; import android.view.Display; import android.view.DisplayInfo; @@ -106,6 +107,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, private static final Interpolator IME_ADJUST_INTERPOLATOR = new PathInterpolator(0.2f, 0f, 0.1f, 1f); + private static final long ONE_MS_IN_NS = 1000000; + private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000; + + private static final int MSG_RESIZE_STACK = 0; + private DividerHandleView mHandle; private View mBackground; private MinimizedDockShadow mMinimizedShadow; @@ -150,7 +156,25 @@ public class DividerView extends FrameLayout implements OnTouchListener, private boolean mDockedStackMinimized; private boolean mAdjustedForIme; private DividerState mState; - private final Handler mHandler = new Handler(); + + /** + * The offset between vsync-app and vsync-surfaceflinger. See + * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary. + */ + private long mSurfaceFlingerOffsetMs; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_RESIZE_STACK: + resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj); + break; + default: + super.handleMessage(msg); + } + } + }; private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() { @Override @@ -290,6 +314,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, protected void onAttachedToWindow() { super.onAttachedToWindow(); EventBus.getDefault().register(this); + mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(); } @Override @@ -298,6 +323,25 @@ public class DividerView extends FrameLayout implements OnTouchListener, EventBus.getDefault().unregister(this); } + /** + * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app + * is a couple of milliseconds before vsync-sf, a touch or animation event that causes the + * stacks to be resized are sometimes processed before the vsync-sf tick, and sometimes after, + * which leads to jank. Figure out this difference here and then post all the touch/animation + * events to start being processed at vsync-sf. + * + * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf. + */ + private long calculateAppSurfaceFlingerVsyncOffsetMs() { + Display display = getDisplay(); + + // Calculate vsync offset from SurfaceFlinger. + // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs + long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate()); + long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS); + return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS); + } + @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { if (mStableInsets.left != insets.getStableInsetLeft() @@ -453,7 +497,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) { SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget( mStartPosition, 0 /* velocity */, false /* hardDismiss */); - resizeStack(calculatePosition(x, y), mStartPosition, snapTarget); + resizeStackDelayed(calculatePosition(x, y), mStartPosition, snapTarget); } break; case MotionEvent.ACTION_UP: @@ -532,10 +576,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, final long endDelay) { final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE; ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); - anim.addUpdateListener(animation -> resizeStack((Integer) animation.getAnimatedValue(), + anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(), taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f ? TASK_POSITION_SAME - : snapTarget.taskPosition, snapTarget)); + : snapTarget.taskPosition, + snapTarget)); Runnable endAction = () -> { commitSnapFlags(snapTarget); mWindowManagerProxy.setResizing(false); @@ -551,15 +596,24 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override public void onAnimationCancel(Animator animation) { + mHandler.removeMessages(MSG_RESIZE_STACK); mCancelled = true; } @Override public void onAnimationEnd(Animator animation) { - if (endDelay == 0 || mCancelled) { + long delay = 0; + if (endDelay != 0) { + delay = endDelay; + } else if (mCancelled) { + delay = 0; + } else if (mSurfaceFlingerOffsetMs != 0) { + delay = mSurfaceFlingerOffsetMs; + } + if (delay == 0) { endAction.run(); } else { - mHandler.postDelayed(endAction, endDelay); + mHandler.postDelayed(endAction, delay); } } }); @@ -793,6 +847,17 @@ public class DividerView extends FrameLayout implements OnTouchListener, mDisplayHeight, mDividerSize); } + public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) { + if (mSurfaceFlingerOffsetMs != 0) { + Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition, + taskSnapTarget); + message.setAsynchronous(true); + mHandler.sendMessageDelayed(message, mSurfaceFlingerOffsetMs); + } else { + resizeStack(position, taskPosition, taskSnapTarget); + } + } + public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) { calculateBoundsForPosition(position, mDockSide, mDockedRect); |