Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de95d544 authored by Jorim Jaggi's avatar Jorim Jaggi Committed by Android (Google) Code Review
Browse files

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

parents 1edadac4 d6d6de6d
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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;
+92 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+9 −39
Original line number Diff line number Diff line
@@ -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);
        }
        mSfChoreographer.scheduleAtSfVsync(mHandler, message);
    }

    public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+22 −10
Original line number Diff line number Diff line
@@ -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) {
        // 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(frameTimeNs);
                }
                animateLocked(mCurrentFrameTime);
            }
        };
        mAnimationFrameCallback = frameTimeNs -> {
            mCurrentFrameTime = frameTimeNs;
            mSfChoreographer.scheduleAtSfVsync(mAnimationTick);
        };
    }

    void addDisplayLocked(final int displayId) {