Merge "Schedule window animations at vsync-sf" into oc-dev

This commit is contained in:
Jorim Jaggi
2017-04-04 09:26:00 +00:00
committed by Android (Google) Code Review
4 changed files with 134 additions and 48 deletions

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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;
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) {