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

Commit c97117b7 authored by Steven Thomas's avatar Steven Thomas
Browse files

Add a flag for refresh rate switching

Some devices don't do refresh rate switching, so we should take that
into account when filtering display manager config settings and deciding
scheduling behavior.

This CL adds a sysprop that can be set to indicate if surface flinger
should do refresh rate switching, and modifies surface flinger to have
the correct behavior when we're not doing refresh rate switching.

Bug: 136592946
Bug: 138261472

Test: Ran through various 60/90 switching scenarios on a device with
refresh rate switching.

Test: Set the refresh rate switching sysprop to false, and confirmed we
get a consistent 60Hz.

Test: Inspected dumpsys output and confirmed it looks correct. In
particular, refresh rate stats are output correctly.

Test: Ran automated tests: RefreshRateConfigsTest, RefreshRateStatsTest,
SchedulerTest.

Change-Id: I54cd5be9d2c1b9abc8475c3ce39846cbe9f9fe53
Merged-In: I54cd5be9d2c1b9abc8475c3ce39846cbe9f9fe53
parent 04c2e480
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -96,7 +96,6 @@ PhaseOffsets::PhaseOffsets() {
    highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
                           highFpsLateAppOffsetNs};

    mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
    mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
    mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});

+126 −58
Original line number Diff line number Diff line
@@ -34,10 +34,9 @@ namespace scheduler {
 */
class RefreshRateConfigs {
public:
    // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
    // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
    // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
    // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
    enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
    enum class RefreshRateType { DEFAULT, PERFORMANCE };

    struct RefreshRate {
        // This config ID corresponds to the position of the config in the vector that is stored
@@ -47,26 +46,57 @@ public:
        std::string name;
        // Refresh rate in frames per second, rounded to the nearest integer.
        uint32_t fps = 0;
        // config Id (returned from HWC2::Display::Config::getId())
        hwc2_config_t id;
        // Vsync period in nanoseconds.
        nsecs_t vsyncPeriod;
        // Hwc config Id (returned from HWC2::Display::Config::getId())
        hwc2_config_t hwcId;
    };

    // Returns true if this device is doing refresh rate switching. This won't change at runtime.
    bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }

    // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
    // from multiple threads. This can only be called if refreshRateSwitching() returns true.
    // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
    // baking them in.
    const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
        return mRefreshRates;
    const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
        LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
        return mRefreshRateMap;
    }
    std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
        const auto& refreshRate = mRefreshRates.find(type);
        if (refreshRate != mRefreshRates.end()) {

    const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
        if (!mRefreshRateSwitchingSupported) {
            return getCurrentRefreshRate().second;
        } else {
            auto refreshRate = mRefreshRateMap.find(type);
            LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
            return refreshRate->second;
        }
        return nullptr;
    }

    RefreshRateType getRefreshRateType(hwc2_config_t id) const {
        for (const auto& [type, refreshRate] : mRefreshRates) {
            if (refreshRate->id == id) {
    std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
        int currentConfig = mCurrentConfig;
        if (mRefreshRateSwitchingSupported) {
            for (const auto& [type, refresh] : mRefreshRateMap) {
                if (refresh.configId == currentConfig) {
                    return {type, refresh};
                }
            }
            LOG_ALWAYS_FATAL();
        }
        return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
    }

    const RefreshRate& getRefreshRateFromConfigId(int configId) const {
        LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
        return mRefreshRates[configId];
    }

    RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
        if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;

        for (const auto& [type, refreshRate] : mRefreshRateMap) {
            if (refreshRate.hwcId == hwcId) {
                return type;
            }
        }
@@ -74,64 +104,102 @@ public:
        return RefreshRateType::DEFAULT;
    }

    void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
        mRefreshRates.clear();
    void setCurrentConfig(int config) {
        LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
        mCurrentConfig = config;
    }

    struct InputConfig {
        hwc2_config_t hwcId = 0;
        nsecs_t vsyncPeriod = 0;
    };

        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
        mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
                              std::make_shared<RefreshRate>(
                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
                                                  HWC2_SCREEN_OFF_CONFIG_ID}));
    RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
                       int currentConfig) {
        init(refreshRateSwitching, configs, currentConfig);
    }

        if (configs.size() < 1) {
            ALOGE("Device does not have valid configs. Config size is 0.");
            return;
    RefreshRateConfigs(bool refreshRateSwitching,
                       const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
                       int currentConfig) {
        std::vector<InputConfig> inputConfigs;
        for (const auto& config : configs) {
            inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
        }
        init(refreshRateSwitching, inputConfigs, currentConfig);
    }

        // Create a map between config index and vsync period. This is all the info we need
        // from the configs.
        std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
private:
    void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
              int currentConfig) {
        mRefreshRateSwitchingSupported = refreshRateSwitching;
        LOG_ALWAYS_FATAL_IF(configs.empty());
        LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
        mCurrentConfig = currentConfig;

        auto buildRefreshRate = [&](int configId) -> RefreshRate {
            const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
            const float fps = 1e9 / vsyncPeriod;
            return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
                    vsyncPeriod, configs[configId].hwcId};
        };

        for (int i = 0; i < configs.size(); ++i) {
            configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
            mRefreshRates.push_back(buildRefreshRate(i));
        }

        if (!mRefreshRateSwitchingSupported) return;

        auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
            if (configs.size() < 2) {
                return {};
            }

        std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
                  [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
                      return a.second > b.second;
            std::vector<const RefreshRate*> sortedRefreshRates;
            for (const auto& refreshRate : mRefreshRates) {
                sortedRefreshRates.push_back(&refreshRate);
            }
            std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
                      [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
                          return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
                      });

        // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
        nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
        if (vsyncPeriod != 0) {
            const float fps = 1e9 / vsyncPeriod;
            const int configId = configIdToVsyncPeriod[0].first;
            mRefreshRates.emplace(RefreshRateType::DEFAULT,
                                  std::make_shared<RefreshRate>(
                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
                                                      static_cast<uint32_t>(fps),
                                                      configs.at(configId)->getId()}));
            // When the configs are ordered by the resync rate, we assume that
            // the first one is DEFAULT and the second one is PERFORMANCE,
            // i.e. the higher rate.
            if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
                sortedRefreshRates[1]->vsyncPeriod == 0) {
                return {};
            }

        if (configs.size() < 2) {
            return std::pair<int, int>(sortedRefreshRates[0]->configId,
                                       sortedRefreshRates[1]->configId);
        };

        auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
        if (!defaultAndPerfConfigs) {
            mRefreshRateSwitchingSupported = false;
            return;
        }

        // When the configs are ordered by the resync rate. We assume that the second one is
        // PERFORMANCE, eg. the higher rate.
        vsyncPeriod = configIdToVsyncPeriod[1].second;
        if (vsyncPeriod != 0) {
            const float fps = 1e9 / vsyncPeriod;
            const int configId = configIdToVsyncPeriod[1].first;
            mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
                                  std::make_shared<RefreshRate>(
                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
                                                      static_cast<uint32_t>(fps),
                                                      configs.at(configId)->getId()}));
        }
        mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
        mRefreshRateMap[RefreshRateType::PERFORMANCE] =
                mRefreshRates[defaultAndPerfConfigs->second];
    }

private:
    std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
    // Whether this device is doing refresh rate switching or not. This must not change after this
    // object is initialized.
    bool mRefreshRateSwitchingSupported;
    // The list of refresh rates, indexed by display config ID. This must not change after this
    // object is initialized.
    std::vector<RefreshRate> mRefreshRates;
    // The mapping of refresh rate type to RefreshRate. This must not change after this object is
    // initialized.
    std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
    // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on
    // the main thread, and read by the Scheduler (and other objects) on other threads, so it's
    // atomic.
    std::atomic<int> mCurrentConfig;
};

} // namespace scheduler
+31 −40
Original line number Diff line number Diff line
@@ -41,21 +41,18 @@ class RefreshRateStats {
    static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;

public:
    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
          : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}

    // Sets power mode. We only collect the information when the power mode is not
    // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
    // on config mode.
    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
                     int currentConfigMode, int currentPowerMode)
          : mRefreshRateConfigs(refreshRateConfigs),
            mTimeStats(timeStats),
            mCurrentConfigMode(currentConfigMode),
            mCurrentPowerMode(currentPowerMode) {}

    // Sets power mode.
    void setPowerMode(int mode) {
        if (mCurrentPowerMode == mode) {
            return;
        }
        // If power mode is normal, the time is going to be recorded under config modes.
        if (mode == HWC_POWER_MODE_NORMAL) {
            mCurrentPowerMode = mode;
            return;
        }
        flushTime();
        mCurrentPowerMode = mode;
    }
@@ -79,16 +76,15 @@ public:
        flushTime();

        std::unordered_map<std::string, int64_t> totalTime;
        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
            int64_t totalTimeForConfig = 0;
            if (!config) {
                continue;
            }
            if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
                totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
        // Multiple configs may map to the same name, e.g. "60fps". Add the
        // times for such configs together.
        for (const auto& [config, time] : mConfigModesTotalTime) {
            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
        }
            totalTime[config->name] = totalTimeForConfig;
        for (const auto& [config, time] : mConfigModesTotalTime) {
            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
        }
        totalTime["ScreenOff"] = mScreenOffTime;
        return totalTime;
    }

@@ -104,32 +100,26 @@ public:
    }

private:
    void flushTime() {
        // Normal power mode is counted under different config modes.
        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
            flushTimeForMode(mCurrentConfigMode);
        } else {
            flushTimeForMode(SCREEN_OFF_CONFIG_ID);
        }
    }

    // Calculates the time that passed in ms between the last time we recorded time and the time
    // this method was called.
    void flushTimeForMode(int mode) {
    void flushTime() {
        nsecs_t currentTime = systemTime();
        nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
        int64_t timeElapsedMs = ns2ms(timeElapsed);
        mPreviousRecordedTime = currentTime;

        mConfigModesTotalTime[mode] += timeElapsedMs;
        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
            if (!config) {
                continue;
            }
            if (config->configId == mode) {
                mTimeStats.recordRefreshRate(config->fps, timeElapsed);
        uint32_t fps = 0;
        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
            // Normal power mode is counted under different config modes.
            if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
                mConfigModesTotalTime[mCurrentConfigMode] = 0;
            }
            mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
            fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
        } else {
            mScreenOffTime += timeElapsedMs;
        }
        mTimeStats.recordRefreshRate(fps, timeElapsed);
    }

    // Formats the time in milliseconds into easy to read format.
@@ -149,10 +139,11 @@ private:
    // Aggregate refresh rate statistics for telemetry.
    TimeStats& mTimeStats;

    int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
    int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
    int mCurrentConfigMode;
    int32_t mCurrentPowerMode;

    std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
    std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
    int64_t mScreenOffTime = 0;

    nsecs_t mPreviousRecordedTime = systemTime();
};
+45 −63
Original line number Diff line number Diff line
@@ -133,7 +133,6 @@ Scheduler::~Scheduler() {

sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
        ResyncCallback resyncCallback,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    const int64_t id = sNextId++;
    ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
@@ -143,8 +142,7 @@ sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
                            offsetThresholdForNextVsync, std::move(interceptCallback));

    auto eventThreadConnection =
            createConnectionInternal(eventThread.get(), std::move(resyncCallback),
                                     ISurfaceComposer::eConfigChangedSuppress);
            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
    mConnections.emplace(id,
                         std::make_unique<Connection>(new ConnectionHandle(id),
                                                      eventThreadConnection,
@@ -164,17 +162,15 @@ std::unique_ptr<EventThread> Scheduler::makeEventThread(
}

sp<EventThreadConnection> Scheduler::createConnectionInternal(
        EventThread* eventThread, ResyncCallback&& resyncCallback,
        ISurfaceComposer::ConfigChanged configChanged) {
    return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
        EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
    return eventThread->createEventConnection([&] { resync(); }, configChanged);
}

sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
        const sp<Scheduler::ConnectionHandle>& handle,
        ISurfaceComposer::ConfigChanged configChanged) {
    RETURN_VALUE_IF_INVALID(nullptr);
    return createConnectionInternal(mConnections[handle->id]->thread.get(),
                                    std::move(resyncCallback), configChanged);
    return createConnectionInternal(mConnections[handle->id]->thread.get(), configChanged);
}

EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -264,23 +260,15 @@ void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
    setVsyncPeriod(period);
}

ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
    std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
    return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
        if (const auto vsync = ptr.lock()) {
            vsync->resync(getVsyncPeriod);
        }
    };
}

void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
void Scheduler::resync() {
    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);

    const nsecs_t now = systemTime();
    const nsecs_t last = lastResyncTime.exchange(now);
    const nsecs_t last = mLastResyncTime.exchange(now);

    if (now - last > kIgnoreDelay) {
        scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
        resyncToHardwareVsync(false,
                              mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
    }
}

@@ -338,15 +326,19 @@ void Scheduler::dumpPrimaryDispSync(std::string& result) const {

std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
        std::string const& name, int windowType) {
    RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
    uint32_t defaultFps, performanceFps;
    if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
        defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
        performanceFps =
                mRefreshRateConfigs
                        .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
                                                        ? RefreshRateType::DEFAULT
            : RefreshRateType::PERFORMANCE;

    const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
    const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0;

    const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT);
    const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0;
                                                        : RefreshRateType::PERFORMANCE)
                        .fps;
    } else {
        defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
        performanceFps = defaultFps;
    }
    return mLayerHistory.createLayer(name, defaultFps, performanceFps);
}

@@ -398,17 +390,6 @@ void Scheduler::setChangeRefreshRateCallback(
    mChangeRefreshRateCallback = changeRefreshRateCallback;
}

void Scheduler::setGetCurrentRefreshRateTypeCallback(
        const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateTypeCallback) {
    std::lock_guard<std::mutex> lock(mCallbackLock);
    mGetCurrentRefreshRateTypeCallback = getCurrentRefreshRateTypeCallback;
}

void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
    std::lock_guard<std::mutex> lock(mCallbackLock);
    mGetVsyncPeriod = getVsyncPeriod;
}

void Scheduler::updateFrameSkipping(const int64_t skipCount) {
    ATRACE_INT("FrameSkipCount", skipCount);
    if (mSkipCount != skipCount) {
@@ -460,14 +441,12 @@ void Scheduler::resetTimerCallback() {

void Scheduler::resetKernelTimerCallback() {
    ATRACE_INT("ExpiredKernelIdleTimer", 0);
    std::lock_guard<std::mutex> lock(mCallbackLock);
    if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
    // If we're not in performance mode then the kernel timer shouldn't do
    // anything, as the refresh rate during DPU power collapse will be the
    // same.
        if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
            resyncToHardwareVsync(true, mGetVsyncPeriod());
        }
    if (refreshRate.first == Scheduler::RefreshRateType::PERFORMANCE) {
        resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod);
    }
}

@@ -499,15 +478,14 @@ void Scheduler::expiredDisplayPowerTimerCallback() {
void Scheduler::expiredKernelTimerCallback() {
    std::lock_guard<std::mutex> lock(mCallbackLock);
    ATRACE_INT("ExpiredKernelIdleTimer", 1);
    if (mGetCurrentRefreshRateTypeCallback) {
        if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
    if (mRefreshRateConfigs.getCurrentRefreshRate().first !=
        Scheduler::RefreshRateType::PERFORMANCE) {
        // Disable HW Vsync if the timer expired, as we don't need it
        // enabled if we're not pushing frames, and if we're in PERFORMANCE
        // mode then we'll need to re-update the DispSync model anyways.
        disableHardwareVsync(false);
    }
}
}

std::string Scheduler::doDump() {
    std::ostringstream stream;
@@ -540,6 +518,10 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO
}

Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
    if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
        return RefreshRateType::DEFAULT;
    }

    // HDR content is not supported on PERFORMANCE mode
    if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
        return RefreshRateType::DEFAULT;
@@ -567,11 +549,11 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
    }

    // Content detection is on, find the appropriate refresh rate with minimal error
    auto iter = min_element(mRefreshRateConfigs.getRefreshRates().cbegin(),
                            mRefreshRateConfigs.getRefreshRates().cend(),
    auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
                            mRefreshRateConfigs.getRefreshRateMap().cend(),
                            [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
                                return std::abs(l.second->fps - static_cast<float>(rate)) <
                                        std::abs(r.second->fps - static_cast<float>(rate));
                                return std::abs(l.second.fps - static_cast<float>(rate)) <
                                        std::abs(r.second.fps - static_cast<float>(rate));
                            });
    RefreshRateType currRefreshRateType = iter->first;

@@ -579,11 +561,11 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
    // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
    // align well with both
    constexpr float MARGIN = 0.05f;
    float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
    float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps /
            float(mContentRefreshRate);
    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
        while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
            ratio = iter->second->fps / float(mContentRefreshRate);
        while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
            ratio = iter->second.fps / float(mContentRefreshRate);

            if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
                currRefreshRateType = iter->first;
+6 −24

File changed.

Preview size limit exceeded, changes collapsed.

Loading