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

Commit 5e04f437 authored by Ady Abraham's avatar Ady Abraham
Browse files

Change setFrameRatecompatibility for preferredRefreshRate

When an app provides a preferredRefreshRate, it should be
translated to a setFrameRate call with
FRAME_RATE_COMPATIBILITY_DEFAULT instead of FRAME_RATE_COMPATIBILITY_EXACT
as it can be any value, and not only a supported refresh rate.

Test: atest RefreshRatePolicyTest
Test: atest FrameRateSelectionPriorityTests

Bug: 257071160
Change-Id: Ib8ee4ba74eac0ba16f7a2591d8aeb5f924224c1b
parent 30a76b53
Loading
Loading
Loading
Loading
+90 −14
Original line number Diff line number Diff line
@@ -16,15 +16,21 @@

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.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;

import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.Display.Mode;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl.RefreshRateRange;

import java.util.HashMap;
import java.util.Objects;

/**
 * Policy to select a lower refresh rate for the display if applicable.
@@ -154,39 +160,109 @@ class RefreshRatePolicy {
        return LAYER_PRIORITY_UNSET;
    }

    float getPreferredRefreshRate(WindowState w) {
    public static class FrameRateVote {
        float mRefreshRate;
        @Surface.FrameRateCompatibility int mCompatibility;

        FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility) {
            update(refreshRate, compatibility);
        }

        FrameRateVote() {
            reset();
        }

        boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility) {
            if (!refreshRateEquals(refreshRate) || mCompatibility != compatibility) {
                mRefreshRate = refreshRate;
                mCompatibility = compatibility;
                return true;
            }
            return false;
        }

        boolean reset() {
            return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof FrameRateVote)) {
                return false;
            }

            FrameRateVote other = (FrameRateVote) o;
            return refreshRateEquals(other.mRefreshRate)
                    && mCompatibility == other.mCompatibility;
        }

        @Override
        public int hashCode() {
            return Objects.hash(mRefreshRate, mCompatibility);
        }

        @Override
        public String toString() {
            return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility;
        }

        private boolean refreshRateEquals(float refreshRate) {
            return mRefreshRate <= refreshRate + RefreshRateRange.FLOAT_TOLERANCE
                    && mRefreshRate >= refreshRate - RefreshRateRange.FLOAT_TOLERANCE;
        }
    }

    boolean updateFrameRateVote(WindowState w) {
        @DisplayManager.SwitchingType int refreshRateSwitchingType =
                mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType();

        // If refresh rate switching is disabled there is no point to set the frame rate on the
        // surface as the refresh rate will be limited by display manager to a single value
        // and SurfaceFlinger wouldn't be able to change it anyways.
        if (refreshRateSwitchingType == SWITCHING_TYPE_NONE) {
            return w.mFrameRateVote.reset();
        }

        // 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.isAnimating(TRANSITION | PARENTS)) {
            return 0;
            return w.mFrameRateVote.reset();
        }

        // If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate
        // of that mode id.
        if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
            final int preferredModeId = w.mAttrs.preferredDisplayModeId;
            if (preferredModeId > 0) {
                DisplayInfo info = w.getDisplayInfo();
                if (info != null) {
                    for (Display.Mode mode : info.supportedModes) {
                        if (preferredModeId == mode.getModeId()) {
                        return mode.getRefreshRate();
                            return w.mFrameRateVote.update(mode.getRefreshRate(),
                                    Surface.FRAME_RATE_COMPATIBILITY_EXACT);

                        }
                    }
                }
            }
        }

        if (w.mAttrs.preferredRefreshRate > 0) {
            return w.mAttrs.preferredRefreshRate;
            return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate,
                    Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
        }

        // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny
        // list, we return the low refresh rate as the preferred one.
        if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
            final String packageName = w.getOwningPackage();
            if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
            return mLowRefreshRateMode.getRefreshRate();
                return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(),
                        Surface.FRAME_RATE_COMPATIBILITY_EXACT);
            }
        }

        return 0;
        return w.mFrameRateVote.reset();
    }

    float getPreferredMinRefreshRate(WindowState w) {
+8 −18
Original line number Diff line number Diff line
@@ -24,8 +24,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -203,7 +201,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -261,6 +258,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
import com.android.server.wm.SurfaceAnimator.AnimationType;

import dalvik.annotation.optimization.NeverCompile;
@@ -792,7 +790,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
     * preferredDisplayModeId or is part of the high refresh rate deny list.
     * The variable is cached, so we do not send too many updates to SF.
     */
    float mAppPreferredFrameRate = 0f;
    FrameRateVote mFrameRateVote = new FrameRateVote();

    static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */

@@ -5507,20 +5505,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                    mFrameRateSelectionPriority);
        }

        // If refresh rate switching is disabled there is no point to set the frame rate on the
        // surface as the refresh rate will be limited by display manager to a single value
        // and SurfaceFlinger wouldn't be able to change it anyways.
        @DisplayManager.SwitchingType int refreshRateSwitchingType =
                mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType();
        if (refreshRateSwitchingType != SWITCHING_TYPE_NONE
                && refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
            final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
            if (mAppPreferredFrameRate != refreshRate) {
                mAppPreferredFrameRate = refreshRate;
        boolean voteChanged = refreshRatePolicy.updateFrameRateVote(this);
        if (voteChanged) {
            getPendingTransaction().setFrameRate(
                        mSurfaceControl, mAppPreferredFrameRate,
                        Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS);
            }
                    mSurfaceControl, mFrameRateVote.mRefreshRate,
                    mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS);

        }
    }

+34 −28
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -36,6 +37,8 @@ import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;

import com.android.server.wm.RefreshRatePolicy.FrameRateVote;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,12 +53,18 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class FrameRateSelectionPriorityTests extends WindowTestsBase {
    private static final float FLOAT_TOLERANCE = 0.01f;
    private static final int LOW_MODE_ID = 3;

    private DisplayPolicy mDisplayPolicy = mock(DisplayPolicy.class);
    private RefreshRatePolicy mRefreshRatePolicy;
    private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
    private FrameRateVote mTempFrameRateVote = new FrameRateVote();

    private static final FrameRateVote FRAME_RATE_VOTE_NONE = new FrameRateVote();
    private static final FrameRateVote FRAME_RATE_VOTE_60_EXACT =
            new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
    private static final FrameRateVote FRAME_RATE_VOTE_60_PREFERRED =
            new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);

    WindowState createWindow(String name) {
        WindowState window = createWindow(null, TYPE_APPLICATION, name);
@@ -85,12 +94,12 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        assertNotNull("Window state is created", appWindow);

        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority doesn't change.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Call the function a few times.
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -109,16 +118,15 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
                .getPreferredModeId(appWindow), 0);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
                .getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE);

        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
        assertFalse(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
                .updateFrameRateVote(appWindow));
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority stays MAX_VALUE.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);

@@ -127,7 +135,7 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes to 1.
        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), 1);
        verify(appWindow.getPendingTransaction(), never()).setFrameRate(
@@ -138,27 +146,27 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
    public void testApplicationInFocusWithModeId() {
        final WindowState appWindow = createWindow("appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Application is in focus.
        appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);
        // Update the mode ID to a requested number.
        appWindow.mAttrs.preferredDisplayModeId = 1;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 0);
        assertEquals(appWindow.mAppPreferredFrameRate, 60, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_60_EXACT);

        // Remove the mode ID request.
        appWindow.mAttrs.preferredDisplayModeId = 0;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 1);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Verify we called actions on Transactions correctly.
        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
@@ -175,7 +183,7 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
    public void testApplicationNotInFocusWithModeId() {
        final WindowState appWindow = createWindow("appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        final WindowState inFocusWindow = createWindow("inFocus");
        appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -183,14 +191,14 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // The window is not in focus.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Update the mode ID to a requested number.
        appWindow.mAttrs.preferredDisplayModeId = 1;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority changes.
        assertEquals(appWindow.mFrameRateSelectionPriority, 2);
        assertEquals(appWindow.mAppPreferredFrameRate, 60, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_60_EXACT);

        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
@@ -204,7 +212,7 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
    public void testApplicationNotInFocusWithoutModeId() {
        final WindowState appWindow = createWindow("appWindow");
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        final WindowState inFocusWindow = createWindow("inFocus");
        appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -212,14 +220,14 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // The window is not in focus.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Make sure that the mode ID is not set.
        appWindow.mAttrs.preferredDisplayModeId = 0;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        // Priority doesn't change.
        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
@@ -237,11 +245,10 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);

        assertEquals(0, mRefreshRatePolicy.getPreferredModeId(appWindow));
        assertEquals(60, mRefreshRatePolicy.getPreferredRefreshRate(appWindow), FLOAT_TOLERANCE);

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority);
        assertEquals(60, appWindow.mAppPreferredFrameRate, FLOAT_TOLERANCE);
        assertEquals(FRAME_RATE_VOTE_60_EXACT, appWindow.mFrameRateVote);

        // Call the function a few times.
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -262,19 +269,19 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
                .thenReturn(DisplayManager.SWITCHING_TYPE_NONE);

        assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Update the mode ID to a requested number.
        appWindow.mAttrs.preferredDisplayModeId = 1;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();

        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        // Remove the mode ID request.
        appWindow.mAttrs.preferredDisplayModeId = 0;
        appWindow.updateFrameRateSelectionPriorityIfNeeded();

        assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE);
        assertEquals(appWindow.mFrameRateVote, FRAME_RATE_VOTE_NONE);

        verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
@@ -292,11 +299,10 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
        appWindow.mAttrs.preferredRefreshRate = 60;

        assertEquals(0, mRefreshRatePolicy.getPreferredModeId(appWindow));
        assertEquals(60, mRefreshRatePolicy.getPreferredRefreshRate(appWindow), FLOAT_TOLERANCE);

        appWindow.updateFrameRateSelectionPriorityIfNeeded();
        assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority);
        assertEquals(60, appWindow.mAppPreferredFrameRate, FLOAT_TOLERANCE);
        assertEquals(FRAME_RATE_VOTE_60_PREFERRED, appWindow.mFrameRateVote);

        // Call the function a few times.
        appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -307,6 +313,6 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
                any(SurfaceControl.class), anyInt());
        verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
                appWindow.getSurfaceControl(), 60,
                Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS);
                Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, Surface.CHANGE_FRAME_RATE_ALWAYS);
    }
}
+159 −36

File changed.

Preview size limit exceeded, changes collapsed.