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

Commit 1902d072 authored by Ady Abraham's avatar Ady Abraham
Browse files

SurfaceFlinger: use scheduler::RefreshRateConfigs in setRefreshRateTo

Use cached values for display configs instead of querying HWC for the
available configs every time we want to change config.

setRefreshRateTo average time reduced from 0.108 ms to 0.032 ms

Test: collected systrace for config changes
Bug: 122906558
Change-Id: I543973ba01df067bd5ad23a2c2ab48b9a90621ae
parent 9c04ead1
Loading
Loading
Loading
Loading
+16 −13
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ namespace android {
namespace scheduler {

/**
 * This class is used to encapsulate configuration for refresh rates. It holds infomation
 * This class is used to encapsulate configuration for refresh rates. It holds information
 * about available refresh rates on the device, and the mapping between the numbers and human
 * readable names.
 */
@@ -40,8 +40,6 @@ public:
    enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };

    struct RefreshRate {
        // Type of the refresh rate.
        RefreshRateType type;
        // This config ID corresponds to the position of the config in the vector that is stored
        // on the device.
        int configId;
@@ -59,13 +57,16 @@ public:
    }
    ~RefreshRateConfigs() = default;

    const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; }
    const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
        return mRefreshRates;
    }
    const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }

private:
    void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
        mRefreshRates.push_back(
                RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
        mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
                              RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});

        if (configs.size() < 1) {
            ALOGE("Device does not have valid configs. Config size is 0.");
@@ -88,9 +89,10 @@ private:
        nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
        if (vsyncPeriod != 0) {
            const float fps = 1e9 / vsyncPeriod;
            mRefreshRates.push_back(
                    RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first,
                                base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
            mRefreshRates.emplace(RefreshRateType::DEFAULT,
                                  RefreshRate{configIdToVsyncPeriod[0].first,
                                              base::StringPrintf("%2.ffps", fps),
                                              static_cast<uint32_t>(fps)});
        }

        if (configs.size() < 2) {
@@ -102,13 +104,14 @@ private:
        vsyncPeriod = configIdToVsyncPeriod[1].second;
        if (vsyncPeriod != 0) {
            const float fps = 1e9 / vsyncPeriod;
            mRefreshRates.push_back(
                    RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first,
                                base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
            mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
                                  RefreshRate{configIdToVsyncPeriod[1].first,
                                              base::StringPrintf("%2.ffps", fps),
                                              static_cast<uint32_t>(fps)});
        }
    }

    std::vector<RefreshRate> mRefreshRates;
    std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
};

} // namespace scheduler
+6 −7
Original line number Diff line number Diff line
@@ -41,10 +41,9 @@ class RefreshRateStats {
    static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;

public:
    explicit RefreshRateStats(
            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
    explicit RefreshRateStats(const std::shared_ptr<RefreshRateConfigs>& refreshRateConfigs,
                              const std::shared_ptr<TimeStats>& timeStats)
          : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)),
          : mRefreshRateConfigs(refreshRateConfigs),
            mTimeStats(timeStats),
            mPreviousRecordedTime(systemTime()) {}
    ~RefreshRateStats() = default;
@@ -84,7 +83,7 @@ public:
        flushTime();

        std::unordered_map<std::string, int64_t> totalTime;
        for (auto config : mRefreshRateConfigs->getRefreshRates()) {
        for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
            int64_t totalTimeForConfig = 0;
            if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
                totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
@@ -124,7 +123,7 @@ private:
        mPreviousRecordedTime = currentTime;

        mConfigModesTotalTime[mode] += timeElapsedMs;
        for (const auto& config : mRefreshRateConfigs->getRefreshRates()) {
        for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
            if (config.configId == mode) {
                mTimeStats->recordRefreshRate(config.fps, timeElapsed);
            }
@@ -143,7 +142,7 @@ private:
    }

    // Keeps information about refresh rate configs that device has.
    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs;

    // Aggregate refresh rate statistics for telemetry.
    std::shared_ptr<TimeStats> mTimeStats;
+21 −56
Original line number Diff line number Diff line
@@ -201,9 +201,6 @@ const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");

constexpr float kDefaultRefreshRate = 60.f;
constexpr float kPerformanceRefreshRate = 90.f;

// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
@@ -712,8 +709,10 @@ void SurfaceFlinger::init() {
            setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None);
        });
    }
    mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
                                                                              *display->getId()),
    mRefreshRateConfigs[*display->getId()] = std::make_shared<scheduler::RefreshRateConfigs>(
            getHwComposer().getConfigs(*display->getId()));
    mRefreshRateStats =
            std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()],
                                                          mTimeStats);

    ALOGV("Done initializing");
@@ -921,14 +920,6 @@ void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int
                                            ConfigEvent event) {
    ATRACE_CALL();

    Vector<DisplayInfo> configs;
    // Lock is acquired by setRefreshRateTo.
    getDisplayConfigsLocked(displayToken, &configs);
    if (mode < 0 || mode >= static_cast<int>(configs.size())) {
        ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
        return;
    }

    // Lock is acquired by setRefreshRateTo.
    const auto display = getDisplayDeviceLocked(displayToken);
    if (!display) {
@@ -1426,6 +1417,8 @@ bool SurfaceFlinger::isConfigAllowed(const DisplayId& displayId, int32_t config)
}

void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent event) {
    ATRACE_CALL();

    mPhaseOffsets->setRefreshRateType(refreshRate);

    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
@@ -1435,47 +1428,23 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent e
        return;
    }

    // TODO(b/113612090): There should be a message queue flush here. Because this esentially
    // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better
    // manner, once the setActiveConfig is synchronous, and is executed at a known time in a
    // refresh cycle.

    // Don't do any updating if the current fps is the same as the new one.
    const nsecs_t currentVsyncPeriod = getVsyncPeriod();
    if (currentVsyncPeriod == 0) {
        return;
    }

    // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than
    // floating numbers.
    const float currentFps = 1e9 / currentVsyncPeriod;
    const float newFps = refreshRate == RefreshRateType::PERFORMANCE ? kPerformanceRefreshRate
                                                                     : kDefaultRefreshRate;
    if (std::abs(currentFps - newFps) <= 1) {
        return;
    }

    const auto displayId = getInternalDisplayIdLocked();
    LOG_ALWAYS_FATAL_IF(!displayId);
    const auto displayToken = getInternalDisplayTokenLocked();

    auto configs = getHwComposer().getConfigs(*displayId);
    for (int i = 0; i < configs.size(); i++) {
        if (!isConfigAllowed(*displayId, i)) {
            ALOGV("Skipping config %d as it is not part of allowed configs", i);
            continue;
    auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
    const auto display = getDisplayDeviceLocked(displayToken);
    if (desiredConfigId == display->getActiveConfig()) {
        return;
    }

        const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
        if (vsyncPeriod == 0) {
            continue;
        }
        const float fps = 1e9 / vsyncPeriod;
        // TODO(b/113612090): There should be a better way at determining which config
        // has the right refresh rate.
        if (std::abs(fps - newFps) <= 1) {
            setDesiredActiveConfig(getInternalDisplayTokenLocked(), i, event);
        }
    if (!isConfigAllowed(*displayId, desiredConfigId)) {
        ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
        return;
    }

    setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
}

void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -5664,17 +5633,13 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal(
    // make sure that the current config is still allowed
    int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
    if (!isConfigAllowed(*displayId, currentConfigIndex)) {
        // TODO(b/122906558): stop querying HWC for the available configs and instead use the cached
        // configs queried on boot
        auto configs = getHwComposer().getConfigs(*displayId);

        for (int i = 0; i < configs.size(); i++) {
            if (isConfigAllowed(*displayId, i)) {
        for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
            if (isConfigAllowed(*displayId, config.configId)) {
                // TODO: we switch to the first allowed config. In the future
                // we may want to enhance this logic to pick a similar config
                // to the current one
                ALOGV("Old config is not allowed - switching to config %d", i);
                setDesiredActiveConfig(displayToken, i, ConfigEvent::Changed);
                ALOGV("Old config is not allowed - switching to config %d", config.configId);
                setDesiredActiveConfig(displayToken, config.configId, ConfigEvent::Changed);
                break;
            }
        }
+3 −0
Original line number Diff line number Diff line
@@ -1115,6 +1115,9 @@ private:
    sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;

    std::unordered_map<DisplayId, std::shared_ptr<scheduler::RefreshRateConfigs>>
            mRefreshRateConfigs;

    std::mutex mAllowedConfigsLock;
    std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs
            GUARDED_BY(mAllowedConfigsLock);
+3 −1
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ protected:

    std::unique_ptr<RefreshRateStats> mRefreshRateStats;
    std::shared_ptr<android::mock::TimeStats> mTimeStats;
    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs;
};

RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -61,7 +62,8 @@ RefreshRateStatsTest::~RefreshRateStatsTest() {

void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
    mTimeStats = std::make_shared<android::mock::TimeStats>();
    mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats);
    mRefreshRateConfigs = std::make_shared<RefreshRateConfigs>(configs);
    mRefreshRateStats = std::make_unique<RefreshRateStats>(mRefreshRateConfigs, mTimeStats);
}

namespace {