diff options
author | Steven Thomas <steventhomas@google.com> | 2020-03-23 19:29:45 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-03-23 19:29:45 +0000 |
commit | 926b76a5193cfe5fbaef78649ff5e2db34b215a7 (patch) | |
tree | 6f3d9f63d204cd7bb66d072b07d0ce970ba7ba70 | |
parent | 1a683cebe3c32f6ee006015e2b811536ea15305e (diff) | |
parent | 7fb29b99dce704891cfef93c226bcf578d812610 (diff) | |
download | native-926b76a5193cfe5fbaef78649ff5e2db34b215a7.tar.gz |
Merge "Add a flag for refresh rate switching" into qt-qpr1-dev
18 files changed, 450 insertions, 494 deletions
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp index 8a2604f4a3..9fa2bbc96e 100644 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp +++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp @@ -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}); diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index d7300583c3..d8137085dd 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -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; + } - // 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})); + struct InputConfig { + hwc2_config_t hwcId = 0; + nsecs_t vsyncPeriod = 0; + }; - if (configs.size() < 1) { - ALOGE("Device does not have valid configs. Config size is 0."); - return; + RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs, + int currentConfig) { + init(refreshRateSwitching, configs, currentConfig); + } + + 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); + } + +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}; + }; - // 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; for (int i = 0; i < configs.size(); ++i) { - configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod()); + mRefreshRates.push_back(buildRefreshRate(i)); } - 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; - }); + if (!mRefreshRateSwitchingSupported) return; - // 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()})); - } + auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> { + if (configs.size() < 2) { + return {}; + } - if (configs.size() < 2) { + 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 and the second one is PERFORMANCE, + // i.e. the higher rate. + if (sortedRefreshRates[0]->vsyncPeriod == 0 || + sortedRefreshRates[1]->vsyncPeriod == 0) { + return {}; + } + + 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 diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 7e7c6307a4..947eb08d90 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -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); - } - totalTime[config->name] = totalTimeForConfig; + // 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; + } + 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(); }; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index f8bd95872c..0b43378394 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -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(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); } } @@ -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) - ? 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; + uint32_t defaultFps, performanceFps; + if (mRefreshRateConfigs.refreshRateSwitchingSupported()) { + defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps; + performanceFps = + mRefreshRateConfigs + .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER) + ? RefreshRateType::DEFAULT + : 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 (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); } } @@ -497,15 +476,13 @@ void Scheduler::expiredDisplayPowerTimerCallback() { } void Scheduler::expiredKernelTimerCallback() { - std::lock_guard<std::mutex> lock(mCallbackLock); ATRACE_INT("ExpiredKernelIdleTimer", 1); - if (mGetCurrentRefreshRateTypeCallback) { - if (mGetCurrentRefreshRateTypeCallback() != 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); - } + 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); } } @@ -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; @@ -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; @@ -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; diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 38184570c1..4b21dadde9 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -49,9 +49,7 @@ public: } using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; - using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>; using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>; - using GetVsyncPeriod = std::function<nsecs_t()>; // Enum to indicate whether to start the transaction early, or at vsync time. enum class TransactionStart { EARLY, NORMAL }; @@ -81,16 +79,6 @@ public: const std::unique_ptr<EventThread> thread; }; - // Stores per-display state about VSYNC. - struct VsyncState { - explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {} - - void resync(const GetVsyncPeriod&); - - Scheduler& scheduler; - std::atomic<nsecs_t> lastResyncTime = 0; - }; - explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, const scheduler::RefreshRateConfigs& refreshRateConfig); @@ -98,12 +86,11 @@ public: /** Creates an EventThread connection. */ sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs, - nsecs_t offsetThresholdForNextVsync, ResyncCallback, + nsecs_t offsetThresholdForNextVsync, impl::EventThread::InterceptVSyncsCallback); sp<IDisplayEventConnection> createDisplayEventConnection( - const sp<ConnectionHandle>& handle, ResyncCallback, - ISurfaceComposer::ConfigChanged configChanged); + const sp<ConnectionHandle>& handle, ISurfaceComposer::ConfigChanged configChanged); // Getter methods. EventThread* getEventThread(const sp<ConnectionHandle>& handle); @@ -143,8 +130,7 @@ public: // no-op. // The period is the vsync period from the current display configuration. void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); - // Creates a callback for resyncing. - ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod); + void resync(); void setRefreshSkipCount(int count); // Passes a vsync sample to DispSync. periodFlushed will be true if // DispSync detected that the vsync period changed, and false otherwise. @@ -167,9 +153,6 @@ public: void updateFpsBasedOnContent(); // Callback that gets invoked when Scheduler wants to change the refresh rate. void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback); - void setGetCurrentRefreshRateTypeCallback( - const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateType); - void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod); // Returns whether idle timer is enabled or not bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; } @@ -209,7 +192,7 @@ private: enum class DisplayPowerTimerState { EXPIRED, RESET }; // Creates a connection on the given EventThread and forwards the given callbacks. - sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&, + sp<EventThreadConnection> createConnectionInternal(EventThread*, ISurfaceComposer::ConfigChanged); nsecs_t calculateAverage() const; @@ -264,7 +247,8 @@ private: std::mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock); bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock); - const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)}; + + std::atomic<nsecs_t> mLastResyncTime = 0; std::unique_ptr<DispSync> mPrimaryDispSync; std::unique_ptr<EventControlThread> mEventControlThread; @@ -300,9 +284,7 @@ private: std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer; std::mutex mCallbackLock; - GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock); ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock); - GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock); // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h index ac10f83ad9..f193553ea3 100644 --- a/services/surfaceflinger/Scheduler/SchedulerUtils.h +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h @@ -30,12 +30,6 @@ using namespace std::chrono_literals; // about layers. static constexpr size_t ARRAY_SIZE = 30; -// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently -// the config is not visible to SF, and is completely maintained by HWC. However, we would -// still like to keep track of time when the device is in this config. -static constexpr int SCREEN_OFF_CONFIG_ID = -1; -static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff; - // This number is used when we try to determine how long do we keep layer information around // before we remove it. It is also used to determine how long the layer stays relevant. // This time period captures infrequent updates when playing YouTube video with static image, diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 32748cffc2..fd7f128411 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -569,14 +569,16 @@ void SurfaceFlinger::bootFinished() readPersistentProperties(); mBootStage = BootStage::FINISHED; - // set the refresh rate according to the policy - const auto& performanceRefreshRate = - mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE); + if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { + // set the refresh rate according to the policy + const auto& performanceRefreshRate = + mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE); - if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) { - setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); - } else { - setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + if (isDisplayConfigAllowed(performanceRefreshRate.configId)) { + setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); + } else { + setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + } } })); } @@ -619,32 +621,6 @@ void SurfaceFlinger::init() { ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset()); Mutex::Autolock _l(mStateLock); - // start the EventThread - mScheduler = - getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, - mRefreshRateConfigs); - auto resyncCallback = - mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); - - mAppConnectionHandle = - mScheduler->createConnection("app", mVsyncModulator.getOffsets().app, - mPhaseOffsets->getOffsetThresholdForNextVsync(), - resyncCallback, - impl::EventThread::InterceptVSyncsCallback()); - mSfConnectionHandle = - mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf, - mPhaseOffsets->getOffsetThresholdForNextVsync(), - resyncCallback, [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }); - - mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); - mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), - mSfConnectionHandle.get()); - - mRegionSamplingThread = - new RegionSamplingThread(*this, *mScheduler, - RegionSamplingThread::EnvironmentTimingTunables()); // Get a RenderEngine for the given display / config (can't fail) int32_t renderEngineFeature = 0; @@ -715,37 +691,6 @@ void SurfaceFlinger::init() { ALOGE("Run StartPropertySetThread failed!"); } - mScheduler->setChangeRefreshRateCallback( - [this](RefreshRateType type, Scheduler::ConfigEvent event) { - Mutex::Autolock lock(mStateLock); - setRefreshRateTo(type, event); - }); - mScheduler->setGetCurrentRefreshRateTypeCallback([this] { - Mutex::Autolock lock(mStateLock); - const auto display = getDefaultDisplayDeviceLocked(); - if (!display) { - // If we don't have a default display the fallback to the default - // refresh rate type - return RefreshRateType::DEFAULT; - } - - const int configId = display->getActiveConfig(); - for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) { - if (refresh && refresh->configId == configId) { - return type; - } - } - // This should never happen, but just gracefully fallback to default. - return RefreshRateType::DEFAULT; - }); - mScheduler->setGetVsyncPeriodCallback([this] { - Mutex::Autolock lock(mStateLock); - return getVsyncPeriod(); - }); - - mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId())); - mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId())); - ALOGV("Done initializing"); } @@ -899,7 +844,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken, info.xdpi = xdpi; info.ydpi = ydpi; info.fps = 1e9 / hwConfig->getVsyncPeriod(); - const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId()); + const auto refreshRateType = + mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId()); const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType); info.appVsyncOffset = offset.late.app; @@ -1001,7 +947,8 @@ void SurfaceFlinger::setActiveConfigInternal() { } std::lock_guard<std::mutex> lock(mActiveConfigLock); - mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId); + mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId); + mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId); display->setActiveConfig(mUpcomingActiveConfig.configId); @@ -1280,9 +1227,6 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { return; } - auto resyncCallback = - mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); - // TODO(b/128863962): Part of the Injector should be refactored, so that it // can be passed to Scheduler. if (enable) { @@ -1294,11 +1238,11 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { impl::EventThread::InterceptVSyncsCallback(), "injEventThread"); } - mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback)); + mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); }); } else { ALOGV("VSync Injections disabled"); mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle), - std::move(resyncCallback)); + [&] { mScheduler->resync(); }); } mInjectVSyncs = enable; @@ -1409,16 +1353,10 @@ status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) { sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) { - auto resyncCallback = mScheduler->makeResyncCallback([this] { - Mutex::Autolock lock(mStateLock); - return getVsyncPeriod(); - }); - const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle; - return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback), - configChanged); + return mScheduler->createDisplayEventConnection(handle, configChanged); } // ---------------------------------------------------------------------------- @@ -1515,13 +1453,8 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::Co ATRACE_CALL(); // Don't do any updating if the current fps is the same as the new one. - const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate); - if (!refreshRateConfig) { - ALOGV("Skipping refresh rate change request for unsupported rate."); - return; - } - - const int desiredConfigId = refreshRateConfig->configId; + const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate); + const int desiredConfigId = refreshRateConfig.configId; if (!isDisplayConfigAllowed(desiredConfigId)) { ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId); @@ -2626,6 +2559,9 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { if (event.connection == HWC2::Connection::Connected) { if (!mPhysicalDisplayTokens.count(info->id)) { ALOGV("Creating display %s", to_string(info->id).c_str()); + if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { + initScheduler(info->id); + } mPhysicalDisplayTokens[info->id] = new BBinder(); DisplayDeviceState state; state.displayId = info->id; @@ -3071,6 +3007,55 @@ void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) { layer->releasePendingBuffer(systemTime()); } +void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { + if (mScheduler) { + // In practice it's not allowed to hotplug in/out the primary display once it's been + // connected during startup, but some tests do it, so just warn and return. + ALOGW("Can't re-init scheduler"); + return; + } + + int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId); + mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false), + getHwComposer().getConfigs( + primaryDisplayId), + currentConfig); + mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats, + currentConfig, HWC_POWER_MODE_OFF); + mRefreshRateStats->setConfigMode(currentConfig); + + // start the EventThread + mScheduler = + getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, + *mRefreshRateConfigs); + mAppConnectionHandle = + mScheduler->createConnection("app", mVsyncModulator.getOffsets().app, + mPhaseOffsets->getOffsetThresholdForNextVsync(), + impl::EventThread::InterceptVSyncsCallback()); + mSfConnectionHandle = + mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf, + mPhaseOffsets->getOffsetThresholdForNextVsync(), + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }); + + mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); + mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), + mSfConnectionHandle.get()); + + mRegionSamplingThread = + new RegionSamplingThread(*this, *mScheduler, + RegionSamplingThread::EnvironmentTimingTunables()); + + mScheduler->setChangeRefreshRateCallback( + [this](RefreshRateType type, Scheduler::ConfigEvent event) { + Mutex::Autolock lock(mStateLock); + setRefreshRateTo(type, event); + }); +} + void SurfaceFlinger::commitTransaction() { if (!mLayersPendingRemoval.isEmpty()) { @@ -4583,7 +4568,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int if (display->isPrimary()) { mTimeStats->setPowerMode(mode); - mRefreshRateStats.setPowerMode(mode); + mRefreshRateStats->setPowerMode(mode); mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL); } @@ -4751,15 +4736,14 @@ void SurfaceFlinger::dumpVSync(std::string& result) const { mUseSmart90ForVideo ? "on" : "off"); StringAppendF(&result, "Allowed Display Configs: "); for (int32_t configId : mAllowedDisplayConfigs) { - for (auto refresh : mRefreshRateConfigs.getRefreshRates()) { - if (refresh.second && refresh.second->configId == configId) { - StringAppendF(&result, "%dHz, ", refresh.second->fps); - } - } + StringAppendF(&result, "%" PRIu32 " Hz, ", + mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps); } StringAppendF(&result, "(config override by backdoor: %s)\n\n", mDebugDisplayConfigSetByBackdoor ? "yes" : "no"); mScheduler->dump(mAppConnectionHandle, result); + StringAppendF(&result, "+ Refresh rate switching: %s\n", + mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off"); } void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { @@ -5124,7 +5108,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\nScheduler state:\n"); result.append(mScheduler->doDump() + "\n"); StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off"); - result.append(mRefreshRateStats.doDump() + "\n"); + result.append(mRefreshRateStats->doDump() + "\n"); result.append(mTimeStats->miniDump()); result.append("\n"); @@ -5595,7 +5579,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1034: { // TODO(b/129297325): expose this via developer menu option n = data.readInt32(); - if (n && !mRefreshRateOverlay) { + if (n && !mRefreshRateOverlay && + mRefreshRateConfigs->refreshRateSwitchingSupported()) { RefreshRateType type; { std::lock_guard<std::mutex> lock(mActiveConfigLock); @@ -6173,25 +6158,6 @@ void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& disp } } -void SurfaceFlinger::setPreferredDisplayConfig() { - const auto& type = mScheduler->getPreferredRefreshRateType(); - const auto& config = mRefreshRateConfigs.getRefreshRate(type); - if (config && isDisplayConfigAllowed(config->configId)) { - ALOGV("switching to Scheduler preferred config %d", config->configId); - setDesiredActiveConfig({type, config->configId, Scheduler::ConfigEvent::Changed}); - } else { - // Set the highest allowed config by iterating backwards on available refresh rates - const auto& refreshRates = mRefreshRateConfigs.getRefreshRates(); - for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { - if (iter->second && isDisplayConfigAllowed(iter->second->configId)) { - ALOGV("switching to allowed config %d", iter->second->configId); - setDesiredActiveConfig({iter->first, iter->second->configId, - Scheduler::ConfigEvent::Changed}); - } - } - } -} - void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display, const std::vector<int32_t>& allowedConfigs) { if (!display->isPrimary()) { @@ -6213,7 +6179,29 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& d mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, display->getActiveConfig()); - setPreferredDisplayConfig(); + if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { + const auto& type = mScheduler->getPreferredRefreshRateType(); + const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type); + if (isDisplayConfigAllowed(config.configId)) { + ALOGV("switching to Scheduler preferred config %d", config.configId); + setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed}); + } else { + // Set the highest allowed config by iterating backwards on available refresh rates + const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap(); + for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { + if (isDisplayConfigAllowed(iter->second.configId)) { + ALOGV("switching to allowed config %d", iter->second.configId); + setDesiredActiveConfig( + {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed}); + break; + } + } + } + } else if (!isDisplayConfigAllowed(display->getActiveConfig())) { + ALOGV("switching to config %d", allowedConfigs[0]); + setDesiredActiveConfig( + {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed}); + } } status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 9744862172..a22d6fc649 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -534,9 +534,6 @@ private: // called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock); - // Query the Scheduler or allowed display configs list for a matching config, and set it - void setPreferredDisplayConfig() REQUIRES(mStateLock); - // called on the main thread in response to setAllowedDisplayConfigs() void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display, const std::vector<int32_t>& allowedConfigs) @@ -558,6 +555,7 @@ private: void commitInputWindowCommands() REQUIRES(mStateLock); void setInputWindowsFinished(); void updateCursorAsync(); + void initScheduler(DisplayId primaryDisplayId); /* handlePageFlip - latch a new buffer if available and compute the dirty * region. Returns whether a new buffer has been latched, i.e., whether it @@ -1135,8 +1133,8 @@ private: sp<Scheduler::ConnectionHandle> mAppConnectionHandle; sp<Scheduler::ConnectionHandle> mSfConnectionHandle; - scheduler::RefreshRateConfigs mRefreshRateConfigs; - scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats}; + std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; // All configs are allowed if the set is empty. using DisplayConfigs = std::set<int32_t>; diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 768074a6cd..b4716eb61e 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -226,6 +226,14 @@ int64_t color_space_agnostic_dataspace(Dataspace defaultValue) { return static_cast<int64_t>(defaultValue); } +bool refresh_rate_switching(bool defaultValue) { + auto temp = SurfaceFlingerProperties::refresh_rate_switching(); + if (temp.has_value()) { + return *temp; + } + return defaultValue; +} + int32_t set_idle_timer_ms(int32_t defaultValue) { auto temp = SurfaceFlingerProperties::set_idle_timer_ms(); if (temp.has_value()) { diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 5f88322f71..e394ccab7b 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -73,6 +73,8 @@ int32_t wcg_composition_pixel_format( int64_t color_space_agnostic_dataspace( android::hardware::graphics::common::V1_2::Dataspace defaultValue); +bool refresh_rate_switching(bool defaultValue); + int32_t set_idle_timer_ms(int32_t defaultValue); int32_t set_touch_timer_ms(int32_t defaultValue); diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 74baf37731..000f21c545 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -15,7 +15,7 @@ module: "android.sysprop.SurfaceFlingerProperties" owner: Platform -# The following two propertiess define (respectively): +# The following two properties define (respectively): # # - The phase offset between hardware vsync and when apps are woken up by the # Choreographer callback @@ -301,6 +301,18 @@ prop { prop_name: "ro.surface_flinger.display_primary_white" } +# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate +# switching on the device, e.g. to switch between 60 and 90 Hz. The settings +# below that are related to refresh rate switching will only have an effect if +# refresh_rate_switching is enabled. +prop { + api_name: "refresh_rate_switching" + type: Boolean + scope: System + access: Readonly + prop_name: "ro.surface_flinger.refresh_rate_switching" +} + # setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is # used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower # refresh rate. Setting this property to 0 means there is no timer. diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt index 79854b36a2..2e804c8a31 100644 --- a/services/surfaceflinger/sysprop/api/system-current.txt +++ b/services/surfaceflinger/sysprop/api/system-current.txt @@ -17,6 +17,7 @@ package android.sysprop { method public static java.util.Optional<java.lang.Long> max_virtual_display_dimension(); method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns(); method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation(); + method public static java.util.Optional<java.lang.Boolean> refresh_rate_switching(); method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework(); method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms(); method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms(); diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 4f8ed1ae1c..349dd3f2f1 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -125,7 +125,17 @@ public: } void setupScheduler() { - mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs()); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mFlinger.mutableRefreshRateConfigs() = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, + configs, + /*currentConfig=*/0); + mFlinger.mutableRefreshRateStats() = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(), + *mFlinger.mutableTimeStats(), + /*currentConfig=*/0, + /*powerMode=*/HWC_POWER_MODE_OFF); + mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs()); mScheduler->mutableEventControlThread().reset(mEventControlThread); mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_)); diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 5f58e7dce9..f40996eecf 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -29,6 +29,7 @@ #include <ui/DebugUtils.h> #include "DisplayIdentificationTest.h" +#include "Scheduler/RefreshRateConfigs.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" @@ -179,7 +180,16 @@ DisplayTransactionTest::~DisplayTransactionTest() { } void DisplayTransactionTest::setupScheduler() { - mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs()); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mFlinger.mutableRefreshRateConfigs() = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs, + /*currentConfig=*/0); + mFlinger.mutableRefreshRateStats() = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(), + *mFlinger.mutableTimeStats(), + /*currentConfig=*/0, + /*powerMode=*/HWC_POWER_MODE_OFF); + mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs()); mScheduler->mutableEventControlThread().reset(mEventControlThread); mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 5067fe890b..f315a8a86c 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -23,7 +23,6 @@ #include "DisplayHardware/HWC2.h" #include "Scheduler/RefreshRateConfigs.h" -#include "mock/DisplayHardware/MockDisplay.h" using namespace std::chrono_literals; using testing::_; @@ -50,9 +49,8 @@ protected: ASSERT_EQ(left.configId, right.configId); ASSERT_EQ(left.name, right.name); ASSERT_EQ(left.fps, right.fps); + ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod); } - - RefreshRateConfigs mConfigs; }; RefreshRateConfigsTest::RefreshRateConfigsTest() { @@ -71,101 +69,39 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) { - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - mConfigs.populate(displayConfigs); - - // We always store a configuration for screen off. - const auto& rates = mConfigs.getRefreshRates(); - ASSERT_EQ(1, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); - ASSERT_NE(rates.end(), powerSavingRate); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE)); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT)); - - RefreshRate expectedConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedConfig, *powerSavingRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - - // Sanity check that getRefreshRate() does not modify the underlying configs. - ASSERT_EQ(1, mConfigs.getRefreshRates().size()); +TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) { + std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs, + /*currentConfig=*/0); + ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported()); } -TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) { - auto display = new Hwc2::mock::Display(); - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - displayConfigs.push_back(config60.build()); - mConfigs.populate(displayConfigs); +TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) { + std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}, + {HWC2_CONFIG_ID_90, VSYNC_90}}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs, + /*currentConfig=*/0); - const auto& rates = mConfigs.getRefreshRates(); + ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported()); + const auto& rates = refreshRateConfigs->getRefreshRateMap(); ASSERT_EQ(2, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); - const auto& defaultRate = rates.find(RefreshRateType::DEFAULT); - ASSERT_NE(rates.end(), powerSavingRate); - ASSERT_NE(rates.end(), defaultRate); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE)); - - RefreshRate expectedPowerSavingConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second); - RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60}; - assertRatesEqual(expectedDefaultConfig, *defaultRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedPowerSavingConfig, - *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); - - // Sanity check that getRefreshRate() does not modify the underlying configs. - ASSERT_EQ(2, mConfigs.getRefreshRates().size()); -} - -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) { - auto display = new Hwc2::mock::Display(); - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - displayConfigs.push_back(config60.build()); - auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config90.setVsyncPeriod(VSYNC_90); - displayConfigs.push_back(config90.build()); - mConfigs.populate(displayConfigs); - - const auto& rates = mConfigs.getRefreshRates(); - ASSERT_EQ(3, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); const auto& defaultRate = rates.find(RefreshRateType::DEFAULT); const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE); - ASSERT_NE(rates.end(), powerSavingRate); ASSERT_NE(rates.end(), defaultRate); ASSERT_NE(rates.end(), performanceRate); - RefreshRate expectedPowerSavingConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second); - RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60}; - assertRatesEqual(expectedDefaultConfig, *defaultRate->second); - RefreshRate expectedPerformanceConfig = - RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90}; - assertRatesEqual(expectedPerformanceConfig, *performanceRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedPowerSavingConfig, - *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); + RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60}; + assertRatesEqual(expectedDefaultConfig, defaultRate->second); + RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90, + HWC2_CONFIG_ID_90}; + assertRatesEqual(expectedPerformanceConfig, performanceRate->second); + + assertRatesEqual(expectedDefaultConfig, + refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT)); assertRatesEqual(expectedPerformanceConfig, - *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); + refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE)); } } // namespace } // namespace scheduler diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index 411ec61770..cec0b32a6b 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -22,7 +22,6 @@ #include <thread> #include "Scheduler/RefreshRateStats.h" -#include "mock/DisplayHardware/MockDisplay.h" #include "mock/MockTimeStats.h" using namespace std::chrono_literals; @@ -42,9 +41,18 @@ protected: RefreshRateStatsTest(); ~RefreshRateStatsTest(); + void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) { + mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>( + /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0); + mRefreshRateStats = + std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats, + /*currentConfig=*/0, + /*currentPowerMode=*/HWC_POWER_MODE_OFF); + } + mock::TimeStats mTimeStats; - RefreshRateConfigs mRefreshRateConfigs; - RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats}; + std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<RefreshRateStats> mRefreshRateStats; }; RefreshRateStatsTest::RefreshRateStatsTest() { @@ -63,63 +71,46 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) { - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - mRefreshRateConfigs.populate(configs); - - // There is one default config, so the refresh rates should have one item. - EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size()); -} - TEST_F(RefreshRateStatsTest, oneConfigTest) { - auto display = new Hwc2::mock::Display(); - - auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config.setVsyncPeriod(VSYNC_90); - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - configs.push_back(config.build()); - - mRefreshRateConfigs.populate(configs); + init({{CONFIG_ID_90, VSYNC_90}}); EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1)); - std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes(); - EXPECT_EQ(2, times.size()); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); + ASSERT_EQ(1, times.size()); EXPECT_NE(0u, times.count("ScreenOff")); - EXPECT_EQ(1u, times.count("90fps")); - EXPECT_EQ(0, times["90fps"]); // Setting up tests on mobile harness can be flaky with time passing, so testing for // exact time changes can result in flaxy numbers. To avoid that remember old // numbers to make sure the correct values are increasing in the next test. int screenOff = times["ScreenOff"]; - int ninety = times["90fps"]; // Screen is off by default. std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(0, times["90fps"]); + EXPECT_EQ(0u, times.count("90fps")); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_LT(ninety, times["90fps"]); + ASSERT_EQ(1u, times.count("90fps")); + EXPECT_LT(0, times["90fps"]); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE); + int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config // does not update refresh rates that come from the config. EXPECT_LT(screenOff, times["ScreenOff"]); @@ -127,93 +118,75 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { } TEST_F(RefreshRateStatsTest, twoConfigsTest) { - auto display = new Hwc2::mock::Display(); - - auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config90.setVsyncPeriod(VSYNC_90); - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - configs.push_back(config90.build()); - - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - configs.push_back(config60.build()); - - mRefreshRateConfigs.populate(configs); + init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}}); EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1)); - std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes(); - EXPECT_EQ(3, times.size()); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); + ASSERT_EQ(1, times.size()); EXPECT_NE(0u, times.count("ScreenOff")); - EXPECT_EQ(1u, times.count("60fps")); - EXPECT_EQ(0, times["60fps"]); - EXPECT_EQ(1u, times.count("90fps")); - EXPECT_EQ(0, times["90fps"]); // Setting up tests on mobile harness can be flaky with time passing, so testing for // exact time changes can result in flaxy numbers. To avoid that remember old // numbers to make sure the correct values are increasing in the next test. int screenOff = times["ScreenOff"]; - int sixty = times["60fps"]; - int ninety = times["90fps"]; // Screen is off by default. std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(sixty, times["60fps"]); - EXPECT_EQ(ninety, times["90fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_EQ(sixty, times["60fps"]); - EXPECT_LT(ninety, times["90fps"]); + ASSERT_EQ(1u, times.count("90fps")); + EXPECT_LT(0, times["90fps"]); // When power mode is normal, time for configs updates. - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); - EXPECT_LT(sixty, times["60fps"]); + ASSERT_EQ(1u, times.count("60fps")); + EXPECT_LT(0, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - sixty = mRefreshRateStats.getTotalTimes()["60fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + int sixty = mRefreshRateStats->getTotalTimes()["60fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_LT(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_LT(sixty, times["60fps"]); // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config // does not update refresh rates that come from the config. - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - sixty = mRefreshRateStats.getTotalTimes()["60fps"]; + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE); + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + sixty = mRefreshRateStats->getTotalTimes()["60fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 740115ea32..571fdfd9fc 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -3,13 +3,13 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> - #include <log/log.h> #include <mutex> #include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" +#include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/Scheduler.h" #include "mock/MockEventThread.h" @@ -34,7 +34,7 @@ protected: MOCK_METHOD0(requestNextVsync, void()); }; - scheduler::RefreshRateConfigs mRefreshRateConfigs; + std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; /** * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else @@ -73,9 +73,14 @@ SchedulerTest::SchedulerTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs, + /*currentConfig=*/0); + std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>(); mEventThread = eventThread.get(); - mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread)); + mScheduler = std::make_unique<MockScheduler>(*mRefreshRateConfigs, std::move(eventThread)); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); mEventThreadConnection = new MockEventThreadConnection(mEventThread); @@ -85,7 +90,7 @@ SchedulerTest::SchedulerTest() { EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillRepeatedly(Return(mEventThreadConnection)); - mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, ResyncCallback(), + mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, impl::EventThread::InterceptVSyncsCallback()); EXPECT_TRUE(mConnectionHandle != nullptr); } @@ -107,7 +112,7 @@ TEST_F(SchedulerTest, testNullPtr) { sp<IDisplayEventConnection> returnedValue; ASSERT_NO_FATAL_FAILURE( returnedValue = - mScheduler->createDisplayEventConnection(nullptr, ResyncCallback(), + mScheduler->createDisplayEventConnection(nullptr, ISurfaceComposer:: eConfigChangedSuppress)); EXPECT_TRUE(returnedValue == nullptr); @@ -130,7 +135,7 @@ TEST_F(SchedulerTest, invalidConnectionHandle) { sp<IDisplayEventConnection> returnedValue; ASSERT_NO_FATAL_FAILURE( returnedValue = - mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(), + mScheduler->createDisplayEventConnection(connectionHandle, ISurfaceComposer:: eConfigChangedSuppress)); EXPECT_TRUE(returnedValue == nullptr); @@ -161,7 +166,7 @@ TEST_F(SchedulerTest, validConnectionHandle) { sp<IDisplayEventConnection> returnedValue; ASSERT_NO_FATAL_FAILURE( returnedValue = - mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(), + mScheduler->createDisplayEventConnection(mConnectionHandle, ISurfaceComposer:: eConfigChangedSuppress)); EXPECT_TRUE(returnedValue != nullptr); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 64d34ee102..1c1b0201d9 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -32,11 +32,11 @@ #include "Layer.h" #include "NativeWindowSurface.h" #include "Scheduler/MessageQueue.h" +#include "Scheduler/RefreshRateConfigs.h" #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" #include "SurfaceFlingerFactory.h" #include "SurfaceInterceptor.h" - #include "TimeStats/TimeStats.h" namespace android { @@ -342,6 +342,8 @@ public: auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; } auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; } auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; } + auto& mutableRefreshRateStats() { return mFlinger->mRefreshRateStats; } + auto& mutableTimeStats() { return mFlinger->mTimeStats; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does |