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

Commit 89d580c0 authored by Tony Huang's avatar Tony Huang
Browse files

Fix split divider janky

The janky caused by SurfaceFlingerVsyncChoreographer does not work
reliably. Sometimes it didn't update but sometimes update twice per
frame so cause janky.

Fix this by removed using SurfaceFlingerVsyncChoreographer and use
real surface flinger sync signal to update screen.

Bug: 150874701
Test: enter split mode and drag divider to observer janky
Change-Id: I5a8bce36738a97b0ddaf4c4e77949eb5295b4894
parent c9bdb4aa
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -124,8 +124,14 @@ public class SurfaceControlViewHost {
    /** @hide */
    public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
            @NonNull WindowlessWindowManager wwm) {
        this(c, d, wwm, false /* useSfChoreographer */);
    }

    /** @hide */
    public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
            @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
        mWm = wwm;
        mViewRoot = new ViewRootImpl(c, d, mWm);
        mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
        mViewRoot.forceDisableBLAST();
        mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
    }
+11 −3
Original line number Diff line number Diff line
@@ -448,7 +448,7 @@ public final class ViewRootImpl implements ViewParent,
    InputQueue mInputQueue;
    @UnsupportedAppUsage
    FallbackEventHandler mFallbackEventHandler;
    Choreographer mChoreographer;
    final Choreographer mChoreographer;

    // used in relayout to get SurfaceControl size
    // for BLAST adapter surface setup
@@ -691,11 +691,18 @@ public final class ViewRootImpl implements ViewParent,
    private SurfaceControl.Transaction mRtBLASTSyncTransaction = new SurfaceControl.Transaction();

    private String mTag = TAG;

    public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession());
        this(context, display, WindowManagerGlobal.getWindowSession(),
                false /* useSfChoreographer */);
    }

    public ViewRootImpl(Context context, Display display, IWindowSession session) {
        this(context, display, session, false /* useSfChoreographer */);
    }

    public ViewRootImpl(Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
        mContext = context;
        mWindowSession = session;
        mDisplay = display;
@@ -731,7 +738,8 @@ public final class ViewRootImpl implements ViewParent,
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
        mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
        mFallbackEventHandler = new PhoneFallbackEventHandler(context);
        mChoreographer = Choreographer.getInstance();
        mChoreographer = useSfChoreographer
                ? Choreographer.getSfInstance() : Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        mInsetsController = new InsetsController(this);

+0 −94
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;

    /**
     * The offset between vsync-app and vsync-surfaceflinger. See
     * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
     */
    private long mSurfaceFlingerOffsetMs;

    public SurfaceFlingerVsyncChoreographer(Handler handler, Display display,
            Choreographer choreographer) {
        mHandler = handler;
        mChoreographer = choreographer;
        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;
    }
}
+8 −33
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;

import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -32,10 +33,8 @@ import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Slog;
import android.view.Choreographer;
import android.view.Display;
import android.view.InsetsState;
import android.view.MotionEvent;
@@ -57,12 +56,12 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;

import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.logging.MetricsLogger;
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.shared.system.WindowManagerWrapper;
@@ -111,8 +110,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 int MSG_RESIZE_STACK = 0;

    private DividerHandleView mHandle;
    private View mBackground;
    private MinimizedDockShadow mMinimizedShadow;
@@ -149,6 +146,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    private SplitDisplayLayout mSplitLayout;
    private DividerCallbacks mCallback;
    private final Rect mStableInsets = new Rect();
    private final AnimationHandler mAnimationHandler = new AnimationHandler();

    private boolean mGrowRecents;
    private ValueAnimator mCurrentAnimator;
@@ -159,7 +157,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    private boolean mHomeStackResizable;
    private boolean mAdjustedForIme;
    private DividerState mState;
    private final SurfaceFlingerVsyncChoreographer mSfChoreographer;

    private SplitScreenTaskOrganizer mTiles;
    boolean mFirstLayout = true;
@@ -173,18 +170,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    // used interact with keyguard.
    private boolean mSurfaceHidden = false;

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RESIZE_STACK:
                    resizeStackSurfaces(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    private final Handler mHandler = new Handler();

    private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
        @Override
@@ -277,11 +263,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, context.getDisplay(),
                Choreographer.getInstance());
        final DisplayManager displayManager =
                (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
        mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
        mAnimationHandler.setProvider(new SfVsyncFrameCallbackProvider());
    }

    @Override
@@ -326,7 +311,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
    void onDividerRemoved() {
        mRemoved = true;
        mCallback = null;
        mHandler.removeMessages(MSG_RESIZE_STACK);
    }

    @Override
@@ -548,7 +532,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
                    SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
                            mStartPosition, 0 /* velocity */, false /* hardDismiss */);
                    resizeStackDelayed(calculatePosition(x, y), mStartPosition, snapTarget);
                    resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget);
                }
                break;
            case MotionEvent.ACTION_UP:
@@ -633,7 +617,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
        if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position);
        final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
        ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
        anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
        anim.addUpdateListener(animation -> resizeStackSurfaces((int) animation.getAnimatedValue(),
                taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
                        ? TASK_POSITION_SAME
                        : snapTarget.taskPosition,
@@ -682,7 +666,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,

            @Override
            public void onAnimationCancel(Animator animation) {
                mHandler.removeMessages(MSG_RESIZE_STACK);
                mCancelled = true;
            }

@@ -693,8 +676,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                    delay = endDelay;
                } else if (mCancelled) {
                    delay = 0;
                } else if (mSfChoreographer.getSurfaceFlingerOffsetMs() > 0) {
                    delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
                }
                if (delay == 0) {
                    endAction.accept(mCancelled);
@@ -705,6 +686,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                }
            }
        });
        anim.setAnimationHandler(mAnimationHandler);
        mCurrentAnimator = anim;
        return anim;
    }
@@ -1047,13 +1029,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                mDividerSize);
    }

    public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
        Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition,
                taskSnapTarget);
        message.setAsynchronous(true);
        mSfChoreographer.scheduleAtSfVsync(mHandler, message);
    }

    private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
        resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
    }
+3 −1
Original line number Diff line number Diff line
@@ -194,7 +194,9 @@ public class SystemWindows {
                return;
            }
            final Display display = mDisplayController.getDisplay(mDisplayId);
            SurfaceControlViewHost viewRoot = new SurfaceControlViewHost(mContext, display, wwm);
            SurfaceControlViewHost viewRoot =
                    new SurfaceControlViewHost(mContext, display, wwm,
                            true /* useSfChoreographer */);
            attrs.flags |= FLAG_HARDWARE_ACCELERATED;
            viewRoot.setView(view, attrs);
            mViewRoots.put(view, viewRoot);