Merge "Schedule window animations at vsync-sf" into oc-dev
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user