Loading services/surfaceflinger/Scheduler/PhaseOffsets.cpp +0 −1 Original line number Diff line number Diff line Loading @@ -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}); Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.h +126 −58 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; } } Loading @@ -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 Loading services/surfaceflinger/Scheduler/RefreshRateStats.h +31 −40 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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; } Loading @@ -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. Loading @@ -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(); }; Loading services/surfaceflinger/Scheduler/Scheduler.cpp +42 −65 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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, Loading @@ -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) { Loading Loading @@ -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(750); 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); } } Loading Loading @@ -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); } Loading Loading @@ -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) { Loading Loading @@ -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 (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. if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) { resyncToHardwareVsync(true, mGetVsyncPeriod()); } resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod); } } Loading Loading @@ -497,17 +476,15 @@ void Scheduler::expiredDisplayPowerTimerCallback() { } void Scheduler::expiredKernelTimerCallback() { std::lock_guard<std::mutex> lock(mCallbackLock); ATRACE_INT("ExpiredKernelIdleTimer", 1); if (mGetCurrentRefreshRateTypeCallback) { if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) { const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate(); if (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 re-update the DispSync model anyways. disableHardwareVsync(false); } } } std::string Scheduler::doDump() { std::ostringstream stream; Loading Loading @@ -540,6 +517,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; Loading Loading @@ -567,16 +548,12 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { } // Content detection is on, find the appropriate refresh rate with minimal error auto begin = mRefreshRateConfigs.getRefreshRates().cbegin(); auto begin = mRefreshRateConfigs.getRefreshRateMap().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(begin, 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; Loading @@ -584,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; Loading services/surfaceflinger/Scheduler/Scheduler.h +6 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/surfaceflinger/Scheduler/PhaseOffsets.cpp +0 −1 Original line number Diff line number Diff line Loading @@ -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}); Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.h +126 −58 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; } } Loading @@ -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 Loading
services/surfaceflinger/Scheduler/RefreshRateStats.h +31 −40 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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; } Loading @@ -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. Loading @@ -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(); }; Loading
services/surfaceflinger/Scheduler/Scheduler.cpp +42 −65 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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, Loading @@ -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) { Loading Loading @@ -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(750); 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); } } Loading Loading @@ -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); } Loading Loading @@ -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) { Loading Loading @@ -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 (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. if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) { resyncToHardwareVsync(true, mGetVsyncPeriod()); } resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod); } } Loading Loading @@ -497,17 +476,15 @@ void Scheduler::expiredDisplayPowerTimerCallback() { } void Scheduler::expiredKernelTimerCallback() { std::lock_guard<std::mutex> lock(mCallbackLock); ATRACE_INT("ExpiredKernelIdleTimer", 1); if (mGetCurrentRefreshRateTypeCallback) { if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) { const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate(); if (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 re-update the DispSync model anyways. disableHardwareVsync(false); } } } std::string Scheduler::doDump() { std::ostringstream stream; Loading Loading @@ -540,6 +517,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; Loading Loading @@ -567,16 +548,12 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { } // Content detection is on, find the appropriate refresh rate with minimal error auto begin = mRefreshRateConfigs.getRefreshRates().cbegin(); auto begin = mRefreshRateConfigs.getRefreshRateMap().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(begin, 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; Loading @@ -584,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; Loading
services/surfaceflinger/Scheduler/Scheduler.h +6 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes