Merge "Schedule window animations at vsync-sf" into oc-dev
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 3316f3a..aac5baa 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -529,6 +529,18 @@
}
}
+ /**
+ * Like {@link #getLastFrameTimeNanos}, but always returns the last frame time, not matter
+ * whether callbacks are currently running.
+ * @return The frame start time of the last frame, in the {@link System#nanoTime()} time base.
+ * @hide
+ */
+ public long getLastFrameTimeNanos() {
+ synchronized (mLock) {
+ return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
+ }
+ }
+
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
diff --git a/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
new file mode 100644
index 0000000..e40090f
--- /dev/null
+++ b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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 com.android.internal.view;
+
+import android.os.Handler;
+import android.os.Message;
+import android.view.Choreographer;
+import android.view.Display;
+
+/**
+ * Utility class to schedule things at vsync-sf instead of vsync-app
+ * @hide
+ */
+public class SurfaceFlingerVsyncChoreographer {
+
+ private static final long ONE_MS_IN_NS = 1000000;
+ private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
+
+ private final Handler mHandler;
+ private final Choreographer mChoreographer = Choreographer.getInstance();
+
+ /**
+ * The offset between vsync-app and vsync-surfaceflinger. See
+ * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
+ */
+ private long mSurfaceFlingerOffsetMs;
+
+ public SurfaceFlingerVsyncChoreographer(Handler handler, Display display) {
+ mHandler = handler;
+ mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(display);
+ }
+
+ public long getSurfaceFlingerOffsetMs() {
+ return mSurfaceFlingerOffsetMs;
+ }
+
+ /**
+ * 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 a surface
+ * flinger transaction 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) {
+
+ // 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);
+ }
+
+ public void scheduleAtSfVsync(Runnable r) {
+ final long delay = calculateDelay();
+ if (delay <= 0) {
+ r.run();
+ } else {
+ mHandler.postDelayed(r, delay);
+ }
+ }
+
+ public void scheduleAtSfVsync(Handler h, Message m) {
+ final long delay = calculateDelay();
+ if (delay <= 0) {
+ h.handleMessage(m);
+ } else {
+ m.setAsynchronous(true);
+ h.sendMessageDelayed(m, delay);
+ }
+ }
+
+ private long calculateDelay() {
+ final long sinceFrameStart = System.nanoTime() - mChoreographer.getLastFrameTimeNanos();
+ return mSurfaceFlingerOffsetMs - sinceFrameStart / 1000000;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 97506e6..0ee3e19 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -58,6 +58,7 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.internal.policy.DockedDividerUtils;
+import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
@@ -108,9 +109,6 @@
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;
@@ -161,12 +159,7 @@
private boolean mHomeStackResizable;
private boolean mAdjustedForIme;
private DividerState mState;
-
- /**
- * The offset between vsync-app and vsync-surfaceflinger. See
- * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
- */
- private long mSurfaceFlingerOffsetMs;
+ private SurfaceFlingerVsyncChoreographer mSfChoreographer;
private final Handler mHandler = new Handler() {
@Override
@@ -319,7 +312,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
- mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs();
+ mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, getDisplay());
}
@Override
@@ -328,25 +321,6 @@
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()
@@ -630,8 +604,8 @@
delay = endDelay;
} else if (mCancelled) {
delay = 0;
- } else if (mSurfaceFlingerOffsetMs != 0) {
- delay = mSurfaceFlingerOffsetMs;
+ } else if (mSfChoreographer.getSurfaceFlingerOffsetMs() > 0) {
+ delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
}
if (delay == 0) {
endAction.run();
@@ -916,14 +890,10 @@
}
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);
- }
+ Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition,
+ taskSnapTarget);
+ message.setAsynchronous(true);
+ mSfChoreographer.scheduleAtSfVsync(mHandler, message);
}
public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 57fb81c..57eaa2b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -17,17 +17,15 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import android.content.Context;
+import android.os.Handler;
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
@@ -36,6 +34,9 @@
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
+import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
+import com.android.server.DisplayThread;
+
import java.io.PrintWriter;
/**
@@ -82,20 +83,31 @@
// check if some got replaced and can be removed.
private boolean mRemoveReplacedWindows = false;
+ private long mCurrentFrameTime;
+ private final Runnable mAnimationTick;
+ private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
+
WindowAnimator(final WindowManagerService service) {
mService = service;
mContext = service.mContext;
mPolicy = service.mPolicy;
mWindowPlacerLocked = service.mWindowPlacerLocked;
+ final Handler handler = DisplayThread.getHandler();
- mAnimationFrameCallback = new Choreographer.FrameCallback() {
- public void doFrame(long frameTimeNs) {
- synchronized (mService.mWindowMap) {
- mService.mAnimationScheduled = false;
- animateLocked(frameTimeNs);
- }
+ // TODO: Multi-display: If displays have different vsync tick, have a separate tick per
+ // display.
+ mSfChoreographer = new SurfaceFlingerVsyncChoreographer(handler,
+ mService.getDefaultDisplayContentLocked().getDisplay());
+ mAnimationTick = () -> {
+ synchronized (mService.mWindowMap) {
+ mService.mAnimationScheduled = false;
+ animateLocked(mCurrentFrameTime);
}
};
+ mAnimationFrameCallback = frameTimeNs -> {
+ mCurrentFrameTime = frameTimeNs;
+ mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
+ };
}
void addDisplayLocked(final int displayId) {