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

Commit b59ef4fb authored by Oleg Blinnikov's avatar Oleg Blinnikov
Browse files

User refresh rate for external display

Previously smoothdisplay and force
peak refresh rate were applied to
external displays. This is incorrect
because external displays need to have
stable and predictable refresh rate.
e.g. configured to be around 59-61hz.

This cl also uses user-preferred display
mode for external displays to set
the SizeAndPhysicalRefreshRatesRange.

Test: atest DisplayModeDirectorTest DisplayObserverTest
Change-Id: I1ee3a1cc007fe913a75099397f4f482a6af1202d
Bug: 370657357
Bug: 372026069
Flag: com.android.server.display.feature.flags.enable_user_refresh_rate_for_external_display
parent f21cc714
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -207,6 +207,10 @@ public class DisplayManagerFlags {
            Flags.FLAG_BLOCK_AUTOBRIGHTNESS_CHANGES_ON_STYLUS_USAGE,
            Flags::blockAutobrightnessChangesOnStylusUsage
    );
    private final FlagState mIsUserRefreshRateForExternalDisplayEnabled = new FlagState(
            Flags.FLAG_ENABLE_USER_REFRESH_RATE_FOR_EXTERNAL_DISPLAY,
            Flags::enableUserRefreshRateForExternalDisplay
    );

    private final FlagState mEnableBatteryStatsForAllDisplays = new FlagState(
            Flags.FLAG_ENABLE_BATTERY_STATS_FOR_ALL_DISPLAYS,
@@ -446,6 +450,14 @@ public class DisplayManagerFlags {
        return mBlockAutobrightnessChangesOnStylusUsage.isEnabled();
    }

    /**
     * @return {@code true} if need to use user refresh rate settings for
     * external displays.
     */
    public boolean isUserRefreshRateForExternalDisplayEnabled() {
        return mIsUserRefreshRateForExternalDisplayEnabled.isEnabled();
    }

    /**
     * dumps all flagstates
     * @param pw printWriter
@@ -491,6 +503,7 @@ public class DisplayManagerFlags {
        pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
        pw.println(" " + mEnableBatteryStatsForAllDisplays);
        pw.println(" " + mBlockAutobrightnessChangesOnStylusUsage);
        pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled);
    }

    private static class FlagState {
+11 −0
Original line number Diff line number Diff line
@@ -373,3 +373,14 @@ flag {
    bug: "352411468"
    is_fixed_read_only: true
}

flag {
    name: "enable_user_refresh_rate_for_external_display"
    namespace: "display_manager"
    description: "Apply refresh rate from user preferred display mode to external displays"
    bug: "370657357"
    is_fixed_read_only: true
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}
+56 −25
Original line number Diff line number Diff line
@@ -1194,6 +1194,13 @@ public class DisplayModeDirector {
        @GuardedBy("mLock")
        private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
                float defaultRefreshRate, int displayId) {
            if (mDisplayObserver.isExternalDisplayLocked(displayId)) {
                if (mLoggingEnabled) {
                    Slog.d(TAG, "skip updateRefreshRateSettingLocked for external display "
                            + displayId);
                }
                return;
            }
            // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
            // used to predict if we're going to be doing frequent refresh rate switching, and if
            // so, enable the brightness observer. The logic here is more complicated and fragile
@@ -1243,6 +1250,8 @@ public class DisplayModeDirector {
        }

        private void removeRefreshRateSetting(int displayId) {
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
                    null);
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
                    null);
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
@@ -1458,11 +1467,11 @@ public class DisplayModeDirector {
        public void onDisplayAdded(int displayId) {
            updateDisplayDeviceConfig(displayId);
            DisplayInfo displayInfo = getDisplayInfo(displayId);
            registerExternalDisplay(displayInfo);
            updateDisplayModes(displayId, displayInfo);
            updateLayoutLimitedFrameRate(displayId, displayInfo);
            updateUserSettingDisplayPreferredSize(displayInfo);
            updateDisplaysPeakRefreshRateAndResolution(displayInfo);
            addDisplaysSynchronizedPeakRefreshRate(displayInfo);
        }

        @Override
@@ -1477,7 +1486,7 @@ public class DisplayModeDirector {
            updateLayoutLimitedFrameRate(displayId, null);
            removeUserSettingDisplayPreferredSize(displayId);
            removeDisplaysPeakRefreshRateAndResolution(displayId);
            removeDisplaysSynchronizedPeakRefreshRate(displayId);
            unregisterExternalDisplay(displayId);
        }

        @Override
@@ -1489,6 +1498,30 @@ public class DisplayModeDirector {
            updateUserSettingDisplayPreferredSize(displayInfo);
        }

        private void registerExternalDisplay(DisplayInfo displayInfo) {
            if (displayInfo == null || displayInfo.type != Display.TYPE_EXTERNAL) {
                return;
            }
            synchronized (mLock) {
                mExternalDisplaysConnected.add(displayInfo.displayId);
                if (mExternalDisplaysConnected.size() == 1) {
                    addDisplaysSynchronizedPeakRefreshRate();
                }
            }
        }

        private void unregisterExternalDisplay(int displayId) {
            synchronized (mLock) {
                if (!isExternalDisplayLocked(displayId)) {
                    return;
                }
                mExternalDisplaysConnected.remove(displayId);
                if (mExternalDisplaysConnected.isEmpty()) {
                    removeDisplaysSynchronizedPeakRefreshRate();
                }
            }
        }

        boolean isExternalDisplayLocked(int displayId) {
            return mExternalDisplaysConnected.contains(displayId);
        }
@@ -1534,11 +1567,25 @@ public class DisplayModeDirector {
                return;
            }

            if (info.type == Display.TYPE_EXTERNAL
                    && mDisplayManagerFlags.isUserRefreshRateForExternalDisplayEnabled()
                    && !isRefreshRateSynchronizationEnabled()) {
                mVotesStorage.updateVote(info.displayId,
                        Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
                        Vote.forSizeAndPhysicalRefreshRatesRange(
                                /* minWidth */ preferredMode.getPhysicalWidth(),
                                /* minHeight */ preferredMode.getPhysicalHeight(),
                                /* width */ preferredMode.getPhysicalWidth(),
                                /* height */ preferredMode.getPhysicalHeight(),
                                /* minRefreshRate */ preferredMode.getRefreshRate(),
                                /* maxRefreshRate */ preferredMode.getRefreshRate()));
            } else {
                mVotesStorage.updateVote(info.displayId,
                        Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
                        Vote.forSize(/* width */ preferredMode.getPhysicalWidth(),
                                /* height */ preferredMode.getPhysicalHeight()));
            }
        }

        @Nullable
        private Display.Mode findDisplayPreferredMode(@NonNull DisplayInfo info) {
@@ -1584,17 +1631,10 @@ public class DisplayModeDirector {
         * Sets 60Hz target refresh rate as the vote with
         * {@link Vote#PRIORITY_SYNCHRONIZED_REFRESH_RATE} priority.
         */
        private void addDisplaysSynchronizedPeakRefreshRate(@Nullable final DisplayInfo info) {
            if (info == null || info.type != Display.TYPE_EXTERNAL
                    || !isRefreshRateSynchronizationEnabled()) {
                return;
            }
            synchronized (mLock) {
                mExternalDisplaysConnected.add(info.displayId);
                if (mExternalDisplaysConnected.size() != 1) {
        private void addDisplaysSynchronizedPeakRefreshRate() {
            if (!isRefreshRateSynchronizationEnabled()) {
                return;
            }
            }
            // set minRefreshRate as the max refresh rate.
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE,
                    Vote.forPhysicalRefreshRates(
@@ -1610,19 +1650,10 @@ public class DisplayModeDirector {
                                    + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
        }

        private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) {
        private void removeDisplaysSynchronizedPeakRefreshRate() {
            if (!isRefreshRateSynchronizationEnabled()) {
                return;
            }
            synchronized (mLock) {
                if (!isExternalDisplayLocked(displayId)) {
                    return;
                }
                mExternalDisplaysConnected.remove(displayId);
                if (!mExternalDisplaysConnected.isEmpty()) {
                    return;
                }
            }
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE, null);
        }
+1 −1
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ interface Vote {
    // It votes [minRefreshRate, Float.POSITIVE_INFINITY]
    int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;

    // User setting preferred display resolution.
    // User setting preferred display resolution, for external displays also includes refresh rate.
    int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;

    // APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
+59 −2
Original line number Diff line number Diff line
@@ -1871,6 +1871,60 @@ public class DisplayModeDirectorTest {
                /* frameRateHigh= */ peakRefreshRate);
    }

    @Test
    public void testPeakRefreshRate_notAppliedToExternalDisplays() {
        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                .thenReturn(true);
        mInjector.mDisplayInfo.type = Display.TYPE_EXTERNAL;
        DisplayModeDirector director =
                new DisplayModeDirector(mContext, mHandler, mInjector,
                        mDisplayManagerFlags, mDisplayDeviceConfigProvider);
        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
        director.getDisplayObserver().onDisplayAdded(DISPLAY_ID);
        director.getDisplayObserver().onDisplayAdded(DISPLAY_ID_2);

        Display.Mode[] modes1 = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 130),
        };
        Display.Mode[] modes2 = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 140),
        };
        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
        supportedModesByDisplay.put(DISPLAY_ID, modes1);
        supportedModesByDisplay.put(DISPLAY_ID_2, modes2);

        Sensor lightSensor = createLightSensor();
        SensorManager sensorManager = createMockSensorManager(lightSensor);
        director.start(sensorManager);
        director.injectSupportedModesByDisplay(supportedModesByDisplay);

        // Disable Smooth Display
        setPeakRefreshRate(RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);

        Vote vote1 = director.getVote(DISPLAY_ID,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        Vote vote2 = director.getVote(DISPLAY_ID_2,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertThat(vote1).isNull();
        assertThat(vote2).isNull();

        // Enable Smooth Display
        setPeakRefreshRate(Float.POSITIVE_INFINITY);

        vote1 = director.getVote(DISPLAY_ID,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        vote2 = director.getVote(DISPLAY_ID_2,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertThat(vote1).isNull();
        assertThat(vote2).isNull();
    }

    @Test
    public void testPeakRefreshRate_DisplayChanged() {
        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
@@ -1968,8 +2022,9 @@ public class DisplayModeDirectorTest {
    @Test
    @Parameters({
        "true, true, 60",
        "false, true, 50",
        "true, false, 50"
        "false, true, 60",
        "true, false, 50",
        "false, false, 50"
    })
    public void testExternalDisplayMaxRefreshRate(boolean isRefreshRateSynchronizationEnabled,
            boolean isExternalDisplay, float expectedMaxRenderFrameRate) {
@@ -3810,6 +3865,7 @@ public class DisplayModeDirectorTest {
                SensorManagerInternal sensorManagerInternal) {
            mDeviceConfig = new FakeDeviceConfig();
            mDisplayInfo = new DisplayInfo();
            mDisplayInfo.type = Display.TYPE_INTERNAL;
            mDisplayInfo.defaultModeId = MODE_ID;
            mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
                    800, 600, /* refreshRate= */ 60)};
@@ -3856,6 +3912,7 @@ public class DisplayModeDirectorTest {
        @Override
        public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
            displayInfo.copyFrom(mDisplayInfo);
            displayInfo.displayId = displayId;
            return mDisplayInfoValid;
        }

Loading