Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.h +16 −13 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ namespace android { namespace scheduler { /** * This class is used to encapsulate configuration for refresh rates. It holds infomation * This class is used to encapsulate configuration for refresh rates. It holds information * about available refresh rates on the device, and the mapping between the numbers and human * readable names. */ Loading @@ -40,8 +40,6 @@ public: enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE }; struct RefreshRate { // Type of the refresh rate. RefreshRateType type; // This config ID corresponds to the position of the config in the vector that is stored // on the device. int configId; Loading @@ -59,13 +57,16 @@ public: } ~RefreshRateConfigs() = default; const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; } const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() { return mRefreshRates; } const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; } private: void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { // This is the rate that HWC encapsulates right now when the device is in DOZE mode. mRefreshRates.push_back( RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); mRefreshRates.emplace(RefreshRateType::POWER_SAVING, RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); if (configs.size() < 1) { ALOGE("Device does not have valid configs. Config size is 0."); Loading @@ -88,9 +89,10 @@ private: nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; mRefreshRates.push_back( RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); mRefreshRates.emplace(RefreshRateType::DEFAULT, RefreshRate{configIdToVsyncPeriod[0].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); } if (configs.size() < 2) { Loading @@ -102,13 +104,14 @@ private: vsyncPeriod = configIdToVsyncPeriod[1].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; mRefreshRates.push_back( RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); mRefreshRates.emplace(RefreshRateType::PERFORMANCE, RefreshRate{configIdToVsyncPeriod[1].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); } } std::vector<RefreshRate> mRefreshRates; std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates; }; } // namespace scheduler Loading services/surfaceflinger/Scheduler/RefreshRateStats.h +6 −7 Original line number Diff line number Diff line Loading @@ -41,10 +41,9 @@ class RefreshRateStats { static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR; public: explicit RefreshRateStats( const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, explicit RefreshRateStats(const std::shared_ptr<RefreshRateConfigs>& refreshRateConfigs, const std::shared_ptr<TimeStats>& timeStats) : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)), : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats), mPreviousRecordedTime(systemTime()) {} ~RefreshRateStats() = default; Loading Loading @@ -84,7 +83,7 @@ public: flushTime(); std::unordered_map<std::string, int64_t> totalTime; for (auto config : mRefreshRateConfigs->getRefreshRates()) { for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) { int64_t totalTimeForConfig = 0; if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) { totalTimeForConfig = mConfigModesTotalTime.at(config.configId); Loading Loading @@ -124,7 +123,7 @@ private: mPreviousRecordedTime = currentTime; mConfigModesTotalTime[mode] += timeElapsedMs; for (const auto& config : mRefreshRateConfigs->getRefreshRates()) { for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) { if (config.configId == mode) { mTimeStats->recordRefreshRate(config.fps, timeElapsed); } Loading @@ -143,7 +142,7 @@ private: } // Keeps information about refresh rate configs that device has. std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs; // Aggregate refresh rate statistics for telemetry. std::shared_ptr<TimeStats> mTimeStats; Loading services/surfaceflinger/SurfaceFlinger.cpp +21 −56 Original line number Diff line number Diff line Loading @@ -201,9 +201,6 @@ const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER" const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); const String16 sDump("android.permission.DUMP"); constexpr float kDefaultRefreshRate = 60.f; constexpr float kPerformanceRefreshRate = 90.f; // --------------------------------------------------------------------------- int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; Loading Loading @@ -712,8 +709,10 @@ void SurfaceFlinger::init() { setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None); }); } mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs( *display->getId()), mRefreshRateConfigs[*display->getId()] = std::make_shared<scheduler::RefreshRateConfigs>( getHwComposer().getConfigs(*display->getId())); mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()], mTimeStats); ALOGV("Done initializing"); Loading Loading @@ -921,14 +920,6 @@ void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int ConfigEvent event) { ATRACE_CALL(); Vector<DisplayInfo> configs; // Lock is acquired by setRefreshRateTo. getDisplayConfigsLocked(displayToken, &configs); if (mode < 0 || mode >= static_cast<int>(configs.size())) { ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size()); return; } // Lock is acquired by setRefreshRateTo. const auto display = getDisplayDeviceLocked(displayToken); if (!display) { Loading Loading @@ -1426,6 +1417,8 @@ bool SurfaceFlinger::isConfigAllowed(const DisplayId& displayId, int32_t config) } void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent event) { ATRACE_CALL(); mPhaseOffsets->setRefreshRateType(refreshRate); const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); Loading @@ -1435,47 +1428,23 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent e return; } // TODO(b/113612090): There should be a message queue flush here. Because this esentially // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better // manner, once the setActiveConfig is synchronous, and is executed at a known time in a // refresh cycle. // Don't do any updating if the current fps is the same as the new one. const nsecs_t currentVsyncPeriod = getVsyncPeriod(); if (currentVsyncPeriod == 0) { return; } // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than // floating numbers. const float currentFps = 1e9 / currentVsyncPeriod; const float newFps = refreshRate == RefreshRateType::PERFORMANCE ? kPerformanceRefreshRate : kDefaultRefreshRate; if (std::abs(currentFps - newFps) <= 1) { return; } const auto displayId = getInternalDisplayIdLocked(); LOG_ALWAYS_FATAL_IF(!displayId); const auto displayToken = getInternalDisplayTokenLocked(); auto configs = getHwComposer().getConfigs(*displayId); for (int i = 0; i < configs.size(); i++) { if (!isConfigAllowed(*displayId, i)) { ALOGV("Skipping config %d as it is not part of allowed configs", i); continue; auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId; const auto display = getDisplayDeviceLocked(displayToken); if (desiredConfigId == display->getActiveConfig()) { return; } const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod(); if (vsyncPeriod == 0) { continue; } const float fps = 1e9 / vsyncPeriod; // TODO(b/113612090): There should be a better way at determining which config // has the right refresh rate. if (std::abs(fps - newFps) <= 1) { setDesiredActiveConfig(getInternalDisplayTokenLocked(), i, event); } if (!isConfigAllowed(*displayId, desiredConfigId)) { ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId); return; } setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event); } void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, Loading Loading @@ -5677,17 +5646,13 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal( // make sure that the current config is still allowed int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId); if (!isConfigAllowed(*displayId, currentConfigIndex)) { // TODO(b/122906558): stop querying HWC for the available configs and instead use the cached // configs queried on boot auto configs = getHwComposer().getConfigs(*displayId); for (int i = 0; i < configs.size(); i++) { if (isConfigAllowed(*displayId, i)) { for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) { if (isConfigAllowed(*displayId, config.configId)) { // TODO: we switch to the first allowed config. In the future // we may want to enhance this logic to pick a similar config // to the current one ALOGV("Old config is not allowed - switching to config %d", i); setDesiredActiveConfig(displayToken, i, ConfigEvent::Changed); ALOGV("Old config is not allowed - switching to config %d", config.configId); setDesiredActiveConfig(displayToken, config.configId, ConfigEvent::Changed); break; } } Loading services/surfaceflinger/SurfaceFlinger.h +3 −0 Original line number Diff line number Diff line Loading @@ -1116,6 +1116,9 @@ private: sp<Scheduler::ConnectionHandle> mSfConnectionHandle; std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; std::unordered_map<DisplayId, std::shared_ptr<scheduler::RefreshRateConfigs>> mRefreshRateConfigs; std::mutex mAllowedConfigsLock; std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs GUARDED_BY(mAllowedConfigsLock); Loading services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +3 −1 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ protected: std::unique_ptr<RefreshRateStats> mRefreshRateStats; std::shared_ptr<android::mock::TimeStats> mTimeStats; std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs; }; RefreshRateStatsTest::RefreshRateStatsTest() { Loading @@ -61,7 +62,8 @@ RefreshRateStatsTest::~RefreshRateStatsTest() { void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) { mTimeStats = std::make_shared<android::mock::TimeStats>(); mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats); mRefreshRateConfigs = std::make_shared<RefreshRateConfigs>(configs); mRefreshRateStats = std::make_unique<RefreshRateStats>(mRefreshRateConfigs, mTimeStats); } namespace { Loading Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.h +16 −13 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ namespace android { namespace scheduler { /** * This class is used to encapsulate configuration for refresh rates. It holds infomation * This class is used to encapsulate configuration for refresh rates. It holds information * about available refresh rates on the device, and the mapping between the numbers and human * readable names. */ Loading @@ -40,8 +40,6 @@ public: enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE }; struct RefreshRate { // Type of the refresh rate. RefreshRateType type; // This config ID corresponds to the position of the config in the vector that is stored // on the device. int configId; Loading @@ -59,13 +57,16 @@ public: } ~RefreshRateConfigs() = default; const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; } const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() { return mRefreshRates; } const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; } private: void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { // This is the rate that HWC encapsulates right now when the device is in DOZE mode. mRefreshRates.push_back( RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); mRefreshRates.emplace(RefreshRateType::POWER_SAVING, RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); if (configs.size() < 1) { ALOGE("Device does not have valid configs. Config size is 0."); Loading @@ -88,9 +89,10 @@ private: nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; mRefreshRates.push_back( RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); mRefreshRates.emplace(RefreshRateType::DEFAULT, RefreshRate{configIdToVsyncPeriod[0].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); } if (configs.size() < 2) { Loading @@ -102,13 +104,14 @@ private: vsyncPeriod = configIdToVsyncPeriod[1].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; mRefreshRates.push_back( RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); mRefreshRates.emplace(RefreshRateType::PERFORMANCE, RefreshRate{configIdToVsyncPeriod[1].first, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); } } std::vector<RefreshRate> mRefreshRates; std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates; }; } // namespace scheduler Loading
services/surfaceflinger/Scheduler/RefreshRateStats.h +6 −7 Original line number Diff line number Diff line Loading @@ -41,10 +41,9 @@ class RefreshRateStats { static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR; public: explicit RefreshRateStats( const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, explicit RefreshRateStats(const std::shared_ptr<RefreshRateConfigs>& refreshRateConfigs, const std::shared_ptr<TimeStats>& timeStats) : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)), : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats), mPreviousRecordedTime(systemTime()) {} ~RefreshRateStats() = default; Loading Loading @@ -84,7 +83,7 @@ public: flushTime(); std::unordered_map<std::string, int64_t> totalTime; for (auto config : mRefreshRateConfigs->getRefreshRates()) { for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) { int64_t totalTimeForConfig = 0; if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) { totalTimeForConfig = mConfigModesTotalTime.at(config.configId); Loading Loading @@ -124,7 +123,7 @@ private: mPreviousRecordedTime = currentTime; mConfigModesTotalTime[mode] += timeElapsedMs; for (const auto& config : mRefreshRateConfigs->getRefreshRates()) { for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) { if (config.configId == mode) { mTimeStats->recordRefreshRate(config.fps, timeElapsed); } Loading @@ -143,7 +142,7 @@ private: } // Keeps information about refresh rate configs that device has. std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs; // Aggregate refresh rate statistics for telemetry. std::shared_ptr<TimeStats> mTimeStats; Loading
services/surfaceflinger/SurfaceFlinger.cpp +21 −56 Original line number Diff line number Diff line Loading @@ -201,9 +201,6 @@ const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER" const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); const String16 sDump("android.permission.DUMP"); constexpr float kDefaultRefreshRate = 60.f; constexpr float kPerformanceRefreshRate = 90.f; // --------------------------------------------------------------------------- int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; Loading Loading @@ -712,8 +709,10 @@ void SurfaceFlinger::init() { setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None); }); } mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs( *display->getId()), mRefreshRateConfigs[*display->getId()] = std::make_shared<scheduler::RefreshRateConfigs>( getHwComposer().getConfigs(*display->getId())); mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()], mTimeStats); ALOGV("Done initializing"); Loading Loading @@ -921,14 +920,6 @@ void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int ConfigEvent event) { ATRACE_CALL(); Vector<DisplayInfo> configs; // Lock is acquired by setRefreshRateTo. getDisplayConfigsLocked(displayToken, &configs); if (mode < 0 || mode >= static_cast<int>(configs.size())) { ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size()); return; } // Lock is acquired by setRefreshRateTo. const auto display = getDisplayDeviceLocked(displayToken); if (!display) { Loading Loading @@ -1426,6 +1417,8 @@ bool SurfaceFlinger::isConfigAllowed(const DisplayId& displayId, int32_t config) } void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent event) { ATRACE_CALL(); mPhaseOffsets->setRefreshRateType(refreshRate); const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); Loading @@ -1435,47 +1428,23 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent e return; } // TODO(b/113612090): There should be a message queue flush here. Because this esentially // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better // manner, once the setActiveConfig is synchronous, and is executed at a known time in a // refresh cycle. // Don't do any updating if the current fps is the same as the new one. const nsecs_t currentVsyncPeriod = getVsyncPeriod(); if (currentVsyncPeriod == 0) { return; } // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than // floating numbers. const float currentFps = 1e9 / currentVsyncPeriod; const float newFps = refreshRate == RefreshRateType::PERFORMANCE ? kPerformanceRefreshRate : kDefaultRefreshRate; if (std::abs(currentFps - newFps) <= 1) { return; } const auto displayId = getInternalDisplayIdLocked(); LOG_ALWAYS_FATAL_IF(!displayId); const auto displayToken = getInternalDisplayTokenLocked(); auto configs = getHwComposer().getConfigs(*displayId); for (int i = 0; i < configs.size(); i++) { if (!isConfigAllowed(*displayId, i)) { ALOGV("Skipping config %d as it is not part of allowed configs", i); continue; auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId; const auto display = getDisplayDeviceLocked(displayToken); if (desiredConfigId == display->getActiveConfig()) { return; } const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod(); if (vsyncPeriod == 0) { continue; } const float fps = 1e9 / vsyncPeriod; // TODO(b/113612090): There should be a better way at determining which config // has the right refresh rate. if (std::abs(fps - newFps) <= 1) { setDesiredActiveConfig(getInternalDisplayTokenLocked(), i, event); } if (!isConfigAllowed(*displayId, desiredConfigId)) { ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId); return; } setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event); } void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, Loading Loading @@ -5677,17 +5646,13 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal( // make sure that the current config is still allowed int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId); if (!isConfigAllowed(*displayId, currentConfigIndex)) { // TODO(b/122906558): stop querying HWC for the available configs and instead use the cached // configs queried on boot auto configs = getHwComposer().getConfigs(*displayId); for (int i = 0; i < configs.size(); i++) { if (isConfigAllowed(*displayId, i)) { for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) { if (isConfigAllowed(*displayId, config.configId)) { // TODO: we switch to the first allowed config. In the future // we may want to enhance this logic to pick a similar config // to the current one ALOGV("Old config is not allowed - switching to config %d", i); setDesiredActiveConfig(displayToken, i, ConfigEvent::Changed); ALOGV("Old config is not allowed - switching to config %d", config.configId); setDesiredActiveConfig(displayToken, config.configId, ConfigEvent::Changed); break; } } Loading
services/surfaceflinger/SurfaceFlinger.h +3 −0 Original line number Diff line number Diff line Loading @@ -1116,6 +1116,9 @@ private: sp<Scheduler::ConnectionHandle> mSfConnectionHandle; std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; std::unordered_map<DisplayId, std::shared_ptr<scheduler::RefreshRateConfigs>> mRefreshRateConfigs; std::mutex mAllowedConfigsLock; std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs GUARDED_BY(mAllowedConfigsLock); Loading
services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +3 −1 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ protected: std::unique_ptr<RefreshRateStats> mRefreshRateStats; std::shared_ptr<android::mock::TimeStats> mTimeStats; std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs; }; RefreshRateStatsTest::RefreshRateStatsTest() { Loading @@ -61,7 +62,8 @@ RefreshRateStatsTest::~RefreshRateStatsTest() { void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) { mTimeStats = std::make_shared<android::mock::TimeStats>(); mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats); mRefreshRateConfigs = std::make_shared<RefreshRateConfigs>(configs); mRefreshRateStats = std::make_unique<RefreshRateStats>(mRefreshRateConfigs, mTimeStats); } namespace { Loading