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

Commit d261b761 authored by petsjonkin's avatar petsjonkin Committed by Oleg Petšjonkin
Browse files

LowPower mode config for vrr displays

Bug: b/335458967
Test: atest DisplayServiceTests
Change-Id: I2a1b5100a0c8948f91aa776f39a9b7a4a89faded
parent 8662d824
Loading
Loading
Loading
Loading
+20 −5
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@ import android.annotation.Nullable;
import android.content.res.Resources;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Collections;
import java.util.List;

/**
 * RefreshRates config for display
@@ -58,12 +62,17 @@ public class RefreshRateData {
     */
    public final int defaultRefreshRateInHbmSunlight;

    public final List<SupportedModeData> lowPowerSupportedModes;

    @VisibleForTesting
    public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate,
            int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight) {
            int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight,
            List<SupportedModeData> lowPowerSupportedModes) {
        this.defaultRefreshRate = defaultRefreshRate;
        this.defaultPeakRefreshRate = defaultPeakRefreshRate;
        this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr;
        this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight;
        this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes);
    }


@@ -71,9 +80,10 @@ public class RefreshRateData {
    public String toString() {
        return "RefreshRateData {"
                + "defaultRefreshRate: " + defaultRefreshRate
                + "defaultPeakRefreshRate: " + defaultPeakRefreshRate
                + "defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
                + "defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
                + ", defaultPeakRefreshRate: " + defaultPeakRefreshRate
                + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
                + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
                + ", lowPowerSupportedModes=" + lowPowerSupportedModes
                + "} ";
    }

@@ -90,8 +100,13 @@ public class RefreshRateData {
        int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight(
                refreshRateConfigs, resources);

        NonNegativeFloatToFloatMap modes =
                refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes();
        List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes);

        return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
                defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight);
                defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
                lowPowerSupportedModes);
    }

    private static int loadDefaultRefreshRate(
+4 −20
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.feature.DisplayManagerFlags;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@@ -42,7 +41,7 @@ public class SensorData {
    public final String name;
    public final float minRefreshRate;
    public final float maxRefreshRate;
    public final List<SupportedMode> supportedModes;
    public final List<SupportedModeData> supportedModes;

    @VisibleForTesting
    public SensorData() {
@@ -61,7 +60,7 @@ public class SensorData {

    @VisibleForTesting
    public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate,
            List<SupportedMode> supportedModes) {
            List<SupportedModeData> supportedModes) {
        this.type = type;
        this.name = name;
        this.minRefreshRate = minRefreshRate;
@@ -214,26 +213,11 @@ public class SensorData {
            minRefreshRate = rr.getMinimum().floatValue();
            maxRefreshRate = rr.getMaximum().floatValue();
        }
        ArrayList<SupportedMode> supportedModes = new ArrayList<>();
        NonNegativeFloatToFloatMap configSupportedModes = sensorDetails.getSupportedModes();
        if (configSupportedModes != null) {
            for (NonNegativeFloatToFloatPoint supportedMode : configSupportedModes.getPoint()) {
                supportedModes.add(new SupportedMode(supportedMode.getFirst().floatValue(),
                        supportedMode.getSecond().floatValue()));
            }
        }
        List<SupportedModeData> supportedModes = SupportedModeData.load(
                sensorDetails.getSupportedModes());

        return new SensorData(sensorDetails.getType(), sensorDetails.getName(), minRefreshRate,
                maxRefreshRate, supportedModes);
    }

    public static class SupportedMode {
        public final float refreshRate;
        public final float vsyncRate;

        public SupportedMode(float refreshRate, float vsyncRate) {
            this.refreshRate = refreshRate;
            this.vsyncRate = vsyncRate;
        }
    }
}
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.server.display.config;

import android.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * Supported display mode data. Display mode is uniquely identified by refreshRate-vsync pair
 */
public class SupportedModeData {
    public final float refreshRate;
    public final float vsyncRate;

    public SupportedModeData(float refreshRate, float vsyncRate) {
        this.refreshRate = refreshRate;
        this.vsyncRate = vsyncRate;
    }

    @Override
    public String toString() {
        return "SupportedModeData{"
                + "refreshRate= " + refreshRate
                + ", vsyncRate= " + vsyncRate
                + '}';
    }

    static List<SupportedModeData> load(@Nullable NonNegativeFloatToFloatMap configMap) {
        ArrayList<SupportedModeData> supportedModes = new ArrayList<>();
        if (configMap != null) {
            for (NonNegativeFloatToFloatPoint supportedMode : configMap.getPoint()) {
                supportedModes.add(new SupportedModeData(supportedMode.getFirst().floatValue(),
                        supportedMode.getSecond().floatValue()));
            }
        }
        return supportedModes;
    }
}
+55 −22
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.RefreshRateData;
import com.android.server.display.config.SupportedModeData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.AmbientFilter;
@@ -451,15 +452,6 @@ public class DisplayModeDirector {
        return config != null && config.isVrrSupportEnabled();
    }

    private boolean isVrrSupportedByAnyDisplayLocked() {
        for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
            if (mDisplayDeviceConfigByDisplay.valueAt(i).isVrrSupportEnabled()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
     */
@@ -939,18 +931,44 @@ public class DisplayModeDirector {
        private final Uri mMatchContentFrameRateSetting =
                Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE);

        private final boolean mVsynLowPowerVoteEnabled;
        private final boolean mVsyncLowPowerVoteEnabled;
        private final boolean mPeakRefreshRatePhysicalLimitEnabled;

        private final Context mContext;
        private final Handler mHandler;
        private float mDefaultPeakRefreshRate;
        private float mDefaultRefreshRate;
        private boolean mIsLowPower = false;

        private final DisplayManager.DisplayListener mDisplayListener =
                new DisplayManager.DisplayListener() {
                    @Override
                    public void onDisplayAdded(int displayId) {
                        synchronized (mLock) {
                            updateLowPowerModeAllowedModesLocked();
                        }
                    }

                    @Override
                    public void onDisplayRemoved(int displayId) {
                        mVotesStorage.updateVote(displayId, Vote.PRIORITY_LOW_POWER_MODE_MODES,
                                null);
                    }

                    @Override
                    public void onDisplayChanged(int displayId) {
                        synchronized (mLock) {
                            updateLowPowerModeAllowedModesLocked();
                        }
                    }
                };

        SettingsObserver(@NonNull Context context, @NonNull Handler handler,
                DisplayManagerFlags flags) {
            super(handler);
            mContext = context;
            mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
            mHandler = handler;
            mVsyncLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
            mPeakRefreshRatePhysicalLimitEnabled = flags.isPeakRefreshRatePhysicalLimitEnabled();
            // We don't want to load from the DeviceConfig while constructing since this leads to
            // a spike in the latency of DisplayManagerService startup. This happens because
@@ -983,6 +1001,7 @@ public class DisplayModeDirector {
                    UserHandle.USER_SYSTEM);
            cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
                    this);
            mInjector.registerDisplayListener(mDisplayListener, mHandler);

            float deviceConfigDefaultPeakRefresh =
                    mConfigParameterProvider.getPeakRefreshRateDefault();
@@ -995,6 +1014,7 @@ public class DisplayModeDirector {
                updateLowPowerModeSettingLocked();
                updateModeSwitchingTypeSettingLocked();
            }

        }

        public void setDefaultRefreshRate(float refreshRate) {
@@ -1061,23 +1081,36 @@ public class DisplayModeDirector {
        }

        private void updateLowPowerModeSettingLocked() {
            boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
            mIsLowPower = Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
            final Vote vote;
            if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) {
                vote = Vote.forSupportedRefreshRates(List.of(
                        new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
                                /* vsyncRate= */ 240f),
                        new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
                                /* vsyncRate= */ 60f)
                ));
            } else if (inLowPowerMode) {
            if (mIsLowPower) {
                vote = Vote.forRenderFrameRates(0f, 60f);
            } else {
                vote = null;
            }
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE, vote);
            mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, vote);
            mBrightnessObserver.onLowPowerModeEnabledLocked(mIsLowPower);
            updateLowPowerModeAllowedModesLocked();
        }

        private void updateLowPowerModeAllowedModesLocked() {
            if (!mVsyncLowPowerVoteEnabled) {
                return;
            }
            if (mIsLowPower) {
                for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
                    DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.valueAt(i);
                    List<SupportedModeData> supportedModes = config
                            .getRefreshRateData().lowPowerSupportedModes;
                    mVotesStorage.updateVote(
                            mDisplayDeviceConfigByDisplay.keyAt(i),
                            Vote.PRIORITY_LOW_POWER_MODE_MODES,
                            Vote.forSupportedRefreshRates(supportedModes));
                }
            } else {
                mVotesStorage.removeAllVotesForPriority(Vote.PRIORITY_LOW_POWER_MODE_MODES);
            }
        }

        /**
+27 −10
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.server.display.mode;

import android.annotation.NonNull;

import com.android.server.display.config.SupportedModeData;

import java.util.ArrayList;
import java.util.List;

interface Vote {
@@ -102,9 +105,15 @@ interface Vote {
    // For internal application to limit display modes to specific ids
    int PRIORITY_SYSTEM_REQUESTED_MODES = 14;

    // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
    // PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
    // Settings.Global.LOW_POWER_MODE is on.
    // Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
    // higher priority votes), render rate limit can still apply
    int PRIORITY_LOW_POWER_MODE_MODES = 14;

    // PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
    // Settings.Global.LOW_POWER_MODE is on.
    int PRIORITY_LOW_POWER_MODE = 15;
    int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 15;

    // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
    // higher priority voters' result is a range, it will fix the rate to a single choice.
@@ -177,22 +186,26 @@ interface Vote {
        return new BaseModeRefreshRateVote(baseModeRefreshRate);
    }

    static Vote forSupportedRefreshRates(
            List<SupportedRefreshRatesVote.RefreshRates> refreshRates) {
        return new SupportedRefreshRatesVote(refreshRates);
    static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
        if (supportedModes.isEmpty()) {
            return null;
        }
        List<SupportedRefreshRatesVote.RefreshRates> rates = new ArrayList<>();
        for (SupportedModeData data : supportedModes) {
            rates.add(new SupportedRefreshRatesVote.RefreshRates(data.refreshRate, data.vsyncRate));
        }
        return new SupportedRefreshRatesVote(rates);
    }

    static Vote forSupportedModes(List<Integer> modeIds) {
        return new SupportedModesVote(modeIds);
    }



    static Vote forSupportedRefreshRatesAndDisableSwitching(
            List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
        return new CombinedVote(
                List.of(forDisableRefreshRateSwitching(),
                        forSupportedRefreshRates(supportedRefreshRates)));
                        new SupportedRefreshRatesVote(supportedRefreshRates)));
    }

    static String priorityToString(int priority) {
@@ -213,8 +226,10 @@ interface Vote {
                return "PRIORITY_HIGH_BRIGHTNESS_MODE";
            case PRIORITY_PROXIMITY:
                return "PRIORITY_PROXIMITY";
            case PRIORITY_LOW_POWER_MODE:
                return "PRIORITY_LOW_POWER_MODE";
            case PRIORITY_LOW_POWER_MODE_MODES:
                return "PRIORITY_LOW_POWER_MODE_MODES";
            case PRIORITY_LOW_POWER_MODE_RENDER_RATE:
                return "PRIORITY_LOW_POWER_MODE_RENDER_RATE";
            case PRIORITY_SKIN_TEMPERATURE:
                return "PRIORITY_SKIN_TEMPERATURE";
            case PRIORITY_UDFPS:
@@ -227,6 +242,8 @@ interface Vote {
                return "PRIORITY_LIMIT_MODE";
            case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
                return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
            case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
                return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
            case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
                return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
            case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
Loading