/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "android-base/stringprintf.h" #include "DisplayHardware/HWComposer.h" #include "Scheduler/SchedulerUtils.h" namespace android { namespace scheduler { /** * 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. */ class RefreshRateConfigs { public: // 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 { DEFAULT, PERFORMANCE }; struct RefreshRate { // This config ID corresponds to the position of the config in the vector that is stored // on the device. int configId; // Human readable name of the refresh rate. std::string name; // Refresh rate in frames per second, rounded to the nearest integer. uint32_t fps = 0; // 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& getRefreshRateMap() const { LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported); return mRefreshRateMap; } 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; } } std::pair 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; } } return RefreshRateType::DEFAULT; } void setCurrentConfig(int config) { LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size()); mCurrentConfig = config; } struct InputConfig { hwc2_config_t hwcId = 0; nsecs_t vsyncPeriod = 0; }; RefreshRateConfigs(bool refreshRateSwitching, const std::vector& configs, int currentConfig) { init(refreshRateSwitching, configs, currentConfig); } RefreshRateConfigs(bool refreshRateSwitching, const std::vector>& configs, int currentConfig) { std::vector 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& 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(fps), vsyncPeriod, configs[configId].hwcId}; }; for (int i = 0; i < configs.size(); ++i) { mRefreshRates.push_back(buildRefreshRate(i)); } if (!mRefreshRateSwitchingSupported) return; auto findDefaultAndPerfConfigs = [&]() -> std::optional> { if (configs.size() < 2) { return {}; } std::vector 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(sortedRefreshRates[0]->configId, sortedRefreshRates[1]->configId); }; auto defaultAndPerfConfigs = findDefaultAndPerfConfigs(); if (!defaultAndPerfConfigs) { mRefreshRateSwitchingSupported = false; return; } mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first]; mRefreshRateMap[RefreshRateType::PERFORMANCE] = mRefreshRates[defaultAndPerfConfigs->second]; } // 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 mRefreshRates; // The mapping of refresh rate type to RefreshRate. This must not change after this object is // initialized. std::map 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 mCurrentConfig; }; } // namespace scheduler } // namespace android