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

Commit b006bccb authored by Vishnu Nair's avatar Vishnu Nair
Browse files

[wm] Force max refresh during shell transitions

Windowing animations should run at max refresh rate. This was
initially implemented by overriding any app requested refresh
rates via layout params. This does not work if the app requests
a framerate directly on the surface or on a child surface.

With VRR, UI Toolkit will try to set more explicit refresh rates on
the main surface control. This change will explicitly request the
refresh rate on the display level surface control with the policy to
override any child refresh rate requests. This requested rate will
be subjective to other inputs from DisplayModeDirector which
may override the rate.

The request to set the max refresh rate will also be applied as soon as
the TransitionController knows a transition is about to start. This allows
a MRR display to switch refresh rates and minimize some jank due the
switch.

Bug: 300019131
Test: atest TransitionTests
Change-Id: I8a14f4679b825d7c3d54c22682224b623bfe94ee
parent ae72fda6
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -9,3 +9,11 @@ flag {
    is_fixed_read_only: true
    bug: "292032926"
}

flag {
    namespace: "window_surfaces"
    name: "explicit_refresh_rate_hints"
    description: "Performance related hints during transitions"
    is_fixed_read_only: true
    bug: "300019131"
}
+4 −2
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.server.wm;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;

import static com.android.window.flags.Flags.explicitRefreshRateHints;

import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.Display.Mode;
@@ -137,7 +139,7 @@ class RefreshRatePolicy {
        // to run in default refresh rate. But if the display size of default mode is different
        // from the using preferred mode, then still keep the preferred mode to avoid disturbing
        // the animation.
        if (w.isAnimationRunningSelfOrParent()) {
        if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) {
            Display.Mode preferredMode = null;
            for (Display.Mode mode : mDisplayInfo.supportedModes) {
                if (preferredDisplayModeId == mode.getModeId()) {
@@ -251,7 +253,7 @@ class RefreshRatePolicy {

        // If app is animating, it's not able to control refresh rate because we want the animation
        // to run in default refresh rate.
        if (w.isAnimationRunningSelfOrParent()) {
        if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) {
            return w.mFrameRateVote.reset();
        }

+1 −0
Original line number Diff line number Diff line
@@ -703,6 +703,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
        if (dc == null || mTargetDisplays.contains(dc)) return;
        mTargetDisplays.add(dc);
        addOnTopTasks(dc, mOnTopTasksStart);
        mController.startPerfHintForDisplay(dc.mDisplayId);
    }

    /**
+42 −2
Original line number Diff line number Diff line
@@ -22,8 +22,10 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.SystemPerformanceHinter.HINT_SF;

import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
import static com.android.window.flags.Flags.explicitRefreshRateHints;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +41,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -48,6 +51,8 @@ import android.view.WindowManager;
import android.window.ITransitionMetricsReporter;
import android.window.ITransitionPlayer;
import android.window.RemoteTransition;
import android.window.SystemPerformanceHinter;
import android.window.SystemPerformanceHinter.HighPerfSession;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
@@ -125,6 +130,8 @@ class TransitionController {
    SnapshotController mSnapshotController;
    TransitionTracer mTransitionTracer;

    private SystemPerformanceHinter mSystemPerformanceHinter;

    private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
            new ArrayList<>();

@@ -176,6 +183,24 @@ class TransitionController {

    private final IBinder.DeathRecipient mTransitionPlayerDeath;

    /**
     * Tracks active perf sessions that boost frame rate and hint sf to increase its
     * estimated work duration.
     */
    private final ArraySet<HighPerfSession> mHighPerfSessions = new ArraySet<>();


    /**
     * Starts a perf hint session which will boost the refresh rate for the display and change
     * sf duration to handle larger workloads.
     */
    void startPerfHintForDisplay(int displayId) {
        if (explicitRefreshRateHints()) {
            mHighPerfSessions.add(mSystemPerformanceHinter.startSession(HINT_SF, displayId,
                    "Transition collected"));
        }
    }

    static class QueuedTransition {
        final Transition mTransition;
        final OnStartCollect mOnStartCollect;
@@ -255,6 +280,12 @@ class TransitionController {
        mIsWaitingForDisplayEnabled = !wms.mDisplayEnabled;
        registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
        setSyncEngine(wms.mSyncEngine);
        setSystemPerformanceHinter(wms.mSystemPerformanceHinter);
    }

    @VisibleForTesting
    void setSystemPerformanceHinter(SystemPerformanceHinter hinter) {
        mSystemPerformanceHinter = hinter;
    }

    @VisibleForTesting
@@ -1194,18 +1225,27 @@ class TransitionController {
        final boolean animatingState = !mPlayingTransitions.isEmpty()
                    || (mCollectingTransition != null && mCollectingTransition.isStarted());
        if (animatingState && !mAnimatingState) {
            if (!explicitRefreshRateHints()) {
                t.setEarlyWakeupStart();
            }
            // Usually transitions put quite a load onto the system already (with all the things
            // happening in app), so pause task snapshot persisting to not increase the load.
            mSnapshotController.setPause(true);
            mAnimatingState = true;
            Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */);
        } else if (!animatingState && mAnimatingState) {
            if (!explicitRefreshRateHints()) {
                t.setEarlyWakeupEnd();
            }
            mAtm.mWindowManager.scheduleAnimationLocked();
            mSnapshotController.setPause(false);
            mAnimatingState = false;
            Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */);
            // We close all perf sessions here when all transitions finish. The sessions are created
            // when we collect transitions because we have access to the display id.
            for (HighPerfSession perfSession : mHighPerfSessions) {
                perfSession.close();
            }
        }
    }

+10 −0
Original line number Diff line number Diff line
@@ -304,6 +304,7 @@ import android.window.ClientWindowFrames;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;
import android.window.SystemPerformanceHinter;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContextInfo;
@@ -1038,6 +1039,8 @@ public class WindowManagerService extends IWindowManager.Stub
        sThreadPriorityBooster.reset();
    }

    SystemPerformanceHinter mSystemPerformanceHinter;

    void openSurfaceTransaction() {
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
@@ -1332,6 +1335,13 @@ public class WindowManagerService extends IWindowManager.Stub
        mBlurController = new BlurController(mContext, mPowerManager);
        mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
        mAccessibilityController = new AccessibilityController(this);
        mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> {
            synchronized (mGlobalLock) {
                DisplayContent dc = mRoot.getDisplayContent(displayId);
                return (dc == null) ? null : dc.getSurfaceControl();
            }

        }, mTransactionFactory);
    }

    DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
Loading