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

Commit 0a7c50d3 authored by Steven Thomas's avatar Steven Thomas Committed by Android (Google) Code Review
Browse files

Merge "Add a flag for refresh rate switching"

parents dbda3d09 2bbaabe1
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ PhaseOffsets::PhaseOffsets() {
    const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
    const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);

    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
@@ -41,10 +41,9 @@ inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateC
 */
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
@@ -54,26 +53,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;
            }
        }
@@ -81,64 +111,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 android::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();
};
+40 −63
Original line number Diff line number Diff line
@@ -118,20 +118,18 @@ DispSync& Scheduler::getPrimaryDispSync() {

Scheduler::ConnectionHandle Scheduler::createConnection(
        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
        ResyncCallback resyncCallback,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    auto eventThread = makeEventThread(connectionName, phaseOffsetNs, offsetThresholdForNextVsync,
                                       std::move(interceptCallback));
    return createConnection(std::move(eventThread), std::move(resyncCallback));
    return createConnection(std::move(eventThread));
}

Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread,
                                                        ResyncCallback&& resyncCallback) {
Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
    const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
    ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);

    auto connection = createConnectionInternal(eventThread.get(), std::move(resyncCallback),
                                               ISurfaceComposer::eConfigChangedSuppress);
    auto connection =
            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);

    mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
    return handle;
@@ -148,17 +146,14 @@ 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(
        ConnectionHandle handle, ResyncCallback resyncCallback,
        ISurfaceComposer::ConfigChanged configChanged) {
        ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
    RETURN_IF_INVALID_HANDLE(handle, nullptr);
    return createConnectionInternal(mConnections[handle].thread.get(), std::move(resyncCallback),
                                    configChanged);
    return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
}

EventThread* Scheduler::getEventThread(ConnectionHandle handle) {
@@ -248,23 +243,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);
    }
}

@@ -314,15 +301,19 @@ nsecs_t Scheduler::getDispSyncExpectedPresentTime() {

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);
}

@@ -368,16 +359,6 @@ void Scheduler::setChangeRefreshRateCallback(ChangeRefreshRateCallback&& callbac
    mChangeRefreshRateCallback = std::move(callback);
}

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

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

void Scheduler::resetIdleTimer() {
    if (mIdleTimer) {
        mIdleTimer->reset();
@@ -416,16 +397,13 @@ void Scheduler::setDisplayPowerState(bool normal) {
void Scheduler::kernelIdleTimerCallback(TimerState state) {
    ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));

    std::lock_guard<std::mutex> lock(mCallbackLock);
    if (!mGetCurrentRefreshRateTypeCallback || !mGetVsyncPeriod) return;

    const auto type = mGetCurrentRefreshRateTypeCallback();
    if (state == TimerState::Reset && type == RefreshRateType::PERFORMANCE) {
    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
    if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) {
        // 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.
        resyncToHardwareVsync(true /* makeAvailable */, mGetVsyncPeriod());
    } else if (state == TimerState::Expired && type != RefreshRateType::PERFORMANCE) {
        resyncToHardwareVsync(true /* makeAvailable */, refreshRate.second.vsyncPeriod);
    } else if (state == TimerState::Expired && refreshRate.first != 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 update the DispSync model anyway.
@@ -485,6 +463,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 && mFeatures.isHDRContent) {
        return RefreshRateType::DEFAULT;
@@ -514,16 +496,11 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
    // Content detection is on, find the appropriate refresh rate with minimal error
    // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs)
    const float rate = static_cast<float>(mFeatures.contentRefreshRate);
    auto begin = mRefreshRateConfigs.getRefreshRates().cbegin();

    // Skip POWER_SAVING config as it is not a real config
    if (begin->first == RefreshRateType::POWER_SAVING) {
        ++begin;
    }
    auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRates().cend(),
    auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
                            mRefreshRateConfigs.getRefreshRateMap().cend(),
                            [rate](const auto& lhs, const auto& rhs) -> bool {
                                return std::abs(lhs.second->fps - rate) <
                                        std::abs(rhs.second->fps - rate);
                                return std::abs(lhs.second.fps - rate) <
                                        std::abs(rhs.second.fps - rate);
                            });
    RefreshRateType currRefreshRateType = iter->first;

@@ -531,10 +508,10 @@ 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 / rate;
    float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate;
    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
        while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
            ratio = iter->second->fps / rate;
        while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
            ratio = iter->second.fps / rate;

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

File changed.

Preview size limit exceeded, changes collapsed.

Loading