diff options
| author | 2017-04-04 09:26:00 +0000 | |
|---|---|---|
| committer | 2017-04-04 09:26:03 +0000 | |
| commit | de95d544cde13a7c8d0624c92f445b43cb8cef24 (patch) | |
| tree | 7377706a9193095b727da4a63867e3ec30d1bf03 | |
| parent | 1edadac465f70629c66b0c9b053b8ac154133ec9 (diff) | |
| parent | d6d6de6da4c7c32babd65c23804d9fdc6eeed740 (diff) | |
Merge "Schedule window animations at vsync-sf" into oc-dev
4 files changed, 135 insertions, 49 deletions
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 3316f3aeb60b..aac5baacdb11 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -529,6 +529,18 @@ public final class Choreographer { } } + /** + * 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 000000000000..e40090f63a76 --- /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 97506e6df1c2..0ee3e1917117 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.logging.nano.MetricsProto.MetricsEvent; 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 @@ 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; @@ -161,12 +159,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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 @@ public class DividerView extends FrameLayout implements OnTouchListener, protected void onAttachedToWindow() { super.onAttachedToWindow(); EventBus.getDefault().register(this); - mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(); + mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, getDisplay()); } @Override @@ -328,25 +321,6 @@ 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() @@ -630,8 +604,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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 class DividerView extends FrameLayout implements OnTouchListener, } 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 57fb81ce0b2d..57eaa2b2ad8d 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.Choreographer; 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 @@ public class WindowAnimator { // 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; - - mAnimationFrameCallback = new Choreographer.FrameCallback() { - public void doFrame(long frameTimeNs) { - synchronized (mService.mWindowMap) { - mService.mAnimationScheduled = false; - animateLocked(frameTimeNs); - } + final Handler handler = DisplayThread.getHandler(); + + // 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) { |