/* * 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 #include #include #include #include #include #include #include #include "DisplayHardware/DisplayMode.h" #include "Scheduler/OneShotTimer.h" #include "ThreadContext.h" #include "Utils/Dumper.h" namespace android::scheduler { using namespace std::chrono_literals; using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; // Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with // the DisplayManager (or override) `Policy`, the `LayerRequirement` of each active layer, // and `GlobalSignals`. class RefreshRateSelector { public: // Margin used when matching refresh rates to the content desired ones. static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION = std::chrono::nanoseconds(800us).count(); // The lowest Render Frame Rate that will ever be selected static constexpr Fps kMinSupportedFrameRate = 20_Hz; // Start range for FrameRateCategory Normal and High. static constexpr Fps kFrameRateCategoryRateHigh = 90_Hz; static constexpr Fps kFrameRateCategoryRateNormal = 60_Hz; static constexpr std::pair kFrameRateCategoryRates = {kFrameRateCategoryRateNormal, kFrameRateCategoryRateHigh}; class Policy { static constexpr int kAllowGroupSwitchingDefault = false; public: // The default mode, used to ensure we only initiate display mode switches within the // same mode group as defaultMode's group. DisplayModeId defaultMode; // Whether or not we switch mode groups to get the best frame rate. bool allowGroupSwitching = kAllowGroupSwitchingDefault; // The primary refresh rate ranges. @see DisplayModeSpecs.aidl for details. // TODO(b/257072060): use the render range when selecting SF render rate // or the app override frame rate FpsRanges primaryRanges; // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details. FpsRanges appRequestRanges; // The idle timer configuration, if provided. std::optional idleScreenConfigOpt; Policy() = default; Policy(DisplayModeId defaultMode, FpsRange range, bool allowGroupSwitching = kAllowGroupSwitchingDefault, const std::optional& idleScreenConfigOpt = std::nullopt) : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range}, allowGroupSwitching, idleScreenConfigOpt) {} Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges, bool allowGroupSwitching = kAllowGroupSwitchingDefault, const std::optional& idleScreenConfigOpt = std::nullopt) : defaultMode(defaultMode), allowGroupSwitching(allowGroupSwitching), primaryRanges(primaryRanges), appRequestRanges(appRequestRanges), idleScreenConfigOpt(idleScreenConfigOpt) {} bool operator==(const Policy& other) const { using namespace fps_approx_ops; return similarExceptIdleConfig(other) && idleScreenConfigOpt == other.idleScreenConfigOpt; } bool operator!=(const Policy& other) const { return !(*this == other); } bool primaryRangeIsSingleRate() const { return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max); } bool similarExceptIdleConfig(const Policy& updated) const { using namespace fps_approx_ops; return defaultMode == updated.defaultMode && primaryRanges == updated.primaryRanges && appRequestRanges == updated.appRequestRanges && allowGroupSwitching == updated.allowGroupSwitching; } std::string toString() const; }; enum class SetPolicyResult { Invalid, Unchanged, Changed }; // We maintain the display manager policy and the override policy separately. The override // policy is used by CTS tests to get a consistent device state for testing. While the override // policy is set, it takes precedence over the display manager policy. Once the override policy // is cleared, we revert to using the display manager policy. struct DisplayManagerPolicy : Policy { using Policy::Policy; }; struct OverridePolicy : Policy { using Policy::Policy; }; struct NoOverridePolicy {}; using PolicyVariant = std::variant; SetPolicyResult setPolicy(const PolicyVariant&) EXCLUDES(mLock) REQUIRES(kMainThreadContext); void onModeChangeInitiated() REQUIRES(kMainThreadContext) { mNumModeSwitchesInPolicy++; } // Gets the current policy, which will be the override policy if active, and the display manager // policy otherwise. Policy getCurrentPolicy() const EXCLUDES(mLock); // Gets the display manager policy, regardless of whether an override policy is active. Policy getDisplayManagerPolicy() const EXCLUDES(mLock); // Returns true if mode is allowed by the current policy. bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock); // Describes the different options the layer voted for refresh rate enum class LayerVoteType { NoVote, // Doesn't care about the refresh rate Min, // Minimal refresh rate available Max, // Maximal refresh rate available Heuristic, // Specific refresh rate that was calculated by platform using a heuristic ExplicitDefault, // Specific refresh rate that was provided by the app with Default // compatibility ExplicitExactOrMultiple, // Specific refresh rate that was provided by the app with // ExactOrMultiple compatibility ExplicitExact, // Specific refresh rate that was provided by the app with // Exact compatibility ExplicitGte, // Greater than or equal to frame rate provided by the app ExplicitCategory, // Specific frame rate category was provided by the app ftl_last = ExplicitCategory }; // Captures the layer requirements for a refresh rate. This will be used to determine the // display refresh rate. struct LayerRequirement { // Layer's name. Used for debugging purposes. std::string name; // Layer's owner uid uid_t ownerUid = static_cast(-1); // Layer vote type. LayerVoteType vote = LayerVoteType::NoVote; // Layer's desired refresh rate, if applicable. Fps desiredRefreshRate; // If a seamless mode switch is required. Seamlessness seamlessness = Seamlessness::Default; // Layer frame rate category. FrameRateCategory frameRateCategory = FrameRateCategory::Default; // Goes together with frame rate category vote. Allow refresh rate changes only // if there would be no jank. bool frameRateCategorySmoothSwitchOnly = false; // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer // would have on choosing the refresh rate. float weight = 0.0f; // Whether layer is in focus or not based on WindowManager's state bool focused = false; bool operator==(const LayerRequirement& other) const { return name == other.name && vote == other.vote && isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) && seamlessness == other.seamlessness && weight == other.weight && focused == other.focused && frameRateCategory == other.frameRateCategory; } bool operator!=(const LayerRequirement& other) const { return !(*this == other); } bool isNoVote() const { return RefreshRateSelector::isNoVote(vote); } }; // Returns true if the layer explicitly instructs to not contribute to refresh rate selection. // In other words, true if the layer should be ignored. static bool isNoVote(LayerVoteType vote) { return vote == LayerVoteType::NoVote; } // Global state describing signals that affect refresh rate choice. struct GlobalSignals { // Whether the user touched the screen recently. Used to apply touch boost. bool touch = false; // True if the system hasn't seen any buffers posted to layers recently. bool idle = false; // Whether the display is about to be powered on, or has been in PowerMode::ON // within the timeout of DisplayPowerTimer. bool powerOnImminent = false; bool shouldEmitEvent() const { return !idle; } bool operator==(GlobalSignals other) const { return touch == other.touch && idle == other.idle && powerOnImminent == other.powerOnImminent; } auto toString() const { return ftl::Concat("{touch=", touch, ", idle=", idle, ", powerOnImminent=", powerOnImminent, '}'); } }; struct ScoredFrameRate { FrameRateMode frameRateMode; float score = 0.0f; bool operator==(const ScoredFrameRate& other) const { return frameRateMode == other.frameRateMode && score == other.score; } static bool scoresEqual(float lhs, float rhs) { constexpr float kEpsilon = 0.0001f; return std::abs(lhs - rhs) <= kEpsilon; } struct DescendingScore { bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const { return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score); } }; }; using FrameRateRanking = std::vector; struct RankedFrameRates { FrameRateRanking ranking; // Ordered by descending score. GlobalSignals consideredSignals; Fps pacesetterFps; bool operator==(const RankedFrameRates& other) const { return ranking == other.ranking && consideredSignals == other.consideredSignals && isApproxEqual(pacesetterFps, other.pacesetterFps); } }; // If valid, `pacesetterFps` (used by follower displays) filters the ranking to modes matching // that refresh rate. RankedFrameRates getRankedFrameRates(const std::vector&, GlobalSignals, Fps pacesetterFps = {}) const EXCLUDES(mLock); FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) { std::lock_guard lock(mLock); return {mMinRefreshRateModeIt->second->getPeakFps(), mMaxRefreshRateModeIt->second->getPeakFps()}; } ftl::Optional onKernelTimerChanged(ftl::Optional desiredModeIdOpt, bool timerExpired) const EXCLUDES(mLock); void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock); // See mActiveModeOpt for thread safety. FrameRateMode getActiveMode() const EXCLUDES(mLock); // Returns a known frame rate that is the closest to frameRate Fps findClosestKnownFrameRate(Fps frameRate) const; enum class KernelIdleTimerController { Sysprop, HwcApi, ftl_last = HwcApi }; // Configuration flags. struct Config { enum class FrameRateOverride { // Do not override the frame rate for an app Disabled, // Override the frame rate for an app to a value which is also // a display refresh rate AppOverrideNativeRefreshRates, // Override the frame rate for an app to any value AppOverride, // Override the frame rate for all apps and all values. Enabled, ftl_last = Enabled }; FrameRateOverride enableFrameRateOverride = FrameRateOverride::Disabled; // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if // no threshold is set. int frameRateMultipleThreshold = 0; // The Idle Timer timeout. 0 timeout means no idle timer. std::chrono::milliseconds legacyIdleTimerTimeout = 0ms; // The controller representing how the kernel idle timer will be configured // either on the HWC api or sysprop. ftl::Optional kernelIdleTimerController; }; RefreshRateSelector( DisplayModes, DisplayModeId activeModeId, Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled, .frameRateMultipleThreshold = 0, .legacyIdleTimerTimeout = 0ms, .kernelIdleTimerController = {}}); RefreshRateSelector(const RefreshRateSelector&) = delete; RefreshRateSelector& operator=(const RefreshRateSelector&) = delete; DisplayModes displayModes() const { std::lock_guard lock(mLock); return mDisplayModes; } // Returns whether switching modes (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only // differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default, // we can probably remove canSwitch altogether since all devices will be able // to switch to a frame rate divisor. bool canSwitch() const EXCLUDES(mLock) { std::lock_guard lock(mLock); return mDisplayModes.size() > 1 || mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled; } // Class to enumerate options around toggling the kernel timer on and off. enum class KernelIdleTimerAction { TurnOff, // Turn off the idle timer. TurnOn // Turn on the idle timer. }; // Checks whether kernel idle timer should be active depending the policy decisions around // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; bool supportsAppFrameRateOverrideByContent() const { return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled; } bool supportsFrameRateOverride() const { return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled; } // Return the display refresh rate divisor to match the layer // frame rate, or 0 if the display refresh rate is not a multiple of the // layer refresh rate. static int getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate); // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000 // for an integer t. static bool isFractionalPairOrMultiple(Fps, Fps); using UidToFrameRateOverride = std::map; // Returns the frame rate override for each uid. UidToFrameRateOverride getFrameRateOverrides(const std::vector&, Fps displayFrameRate, GlobalSignals) const EXCLUDES(mLock); // Gets the FpsRange that the FrameRateCategory represents. static FpsRange getFrameRateCategoryRange(FrameRateCategory category); std::optional kernelIdleTimerController() { return mConfig.kernelIdleTimerController; } struct IdleTimerCallbacks { struct Callbacks { std::function onReset; std::function onExpired; }; Callbacks platform; Callbacks kernel; Callbacks vrr; }; void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) { std::scoped_lock lock(mIdleTimerCallbacksMutex); mIdleTimerCallbacks = std::move(callbacks); } void clearIdleTimerCallbacks() EXCLUDES(mIdleTimerCallbacksMutex) { std::scoped_lock lock(mIdleTimerCallbacksMutex); mIdleTimerCallbacks.reset(); } void startIdleTimer() { mIdleTimerStarted = true; if (mIdleTimer) { mIdleTimer->start(); } } void stopIdleTimer() { mIdleTimerStarted = false; if (mIdleTimer) { mIdleTimer->stop(); } } void resetKernelIdleTimer() { if (mIdleTimer && mConfig.kernelIdleTimerController) { mIdleTimer->reset(); } } void resetIdleTimer() { if (mIdleTimer) { mIdleTimer->reset(); } } void dump(utils::Dumper&) const EXCLUDES(mLock); std::chrono::milliseconds getIdleTimerTimeout(); bool isVrrDevice() const; std::pair getFrameRateCategoryRates() const { return kFrameRateCategoryRates; } std::vector getSupportedFrameRates() const EXCLUDES(mLock); private: friend struct TestableRefreshRateSelector; void constructAvailableRefreshRates() REQUIRES(mLock); // See mActiveModeOpt for thread safety. const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock); RankedFrameRates getRankedFrameRatesLocked(const std::vector& layers, GlobalSignals signals, Fps pacesetterFps) const REQUIRES(mLock); // Returns number of display frames and remainder when dividing the layer refresh period by // display refresh period. std::pair getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const; // Returns the lowest refresh rate according to the current policy. May change at runtime. Only // uses the primary range, not the app request range. const DisplayModePtr& getMinRefreshRateByPolicyLocked() const REQUIRES(mLock); // Returns the highest refresh rate according to the current policy. May change at runtime. Only // uses the primary range, not the app request range. const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock); struct RefreshRateScoreComparator; enum class RefreshRateOrder { Ascending, Descending, ftl_last = Descending }; typedef std::function RankFrameRatesPredicate; // Rank the frame rates. // Only modes in the primary range for which `predicate` is `true` will be scored. // Does not use the app requested range. FrameRateRanking rankFrameRates( std::optional anchorGroupOpt, RefreshRateOrder refreshRateOrder, std::optional preferredDisplayModeOpt = std::nullopt, const RankFrameRatesPredicate& predicate = [](FrameRateMode) { return true; }) const REQUIRES(mLock); const Policy* getCurrentPolicyLocked() const REQUIRES(mLock); bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1. float calculateDistanceScoreFromMaxLocked(Fps refreshRate) const REQUIRES(mLock); // Returns the refresh rate score based on its distance from the reference rate. float calculateDistanceScoreLocked(Fps referenceRate, Fps refreshRate) const REQUIRES(mLock); // calculates a score for a layer. Used to determine the display refresh rate // and the frame rate override for certains applications. float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate, bool isSeamlessSwitch) const REQUIRES(mLock); float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const REQUIRES(mLock); // Calculates the score for non-exact matching layer that has LayerVoteType::ExplicitDefault. float calculateNonExactMatchingDefaultLayerScoreLocked(nsecs_t displayPeriod, nsecs_t layerPeriod) const REQUIRES(mLock); void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext); void initializeIdleTimer(std::chrono::milliseconds timeout); std::optional getIdleTimerCallbacks() const REQUIRES(mIdleTimerCallbacksMutex) { if (!mIdleTimerCallbacks) return {}; if (mIsVrrDevice) return mIdleTimerCallbacks->vrr; return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel : mIdleTimerCallbacks->platform; } bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) { LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride != Config::FrameRateOverride::AppOverrideNativeRefreshRates, "should only be called when " "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used"); return mAppOverrideNativeRefreshRates.contains(fps); } std::vector createFrameRateModes( const Policy&, std::function&& filterModes, const FpsRange&) const REQUIRES(mLock); // The display modes of the active display. The DisplayModeIterators below are pointers into // this container, so must be invalidated whenever the DisplayModes change. The Policy below // is also dependent, so must be reset as well. DisplayModes mDisplayModes GUARDED_BY(mLock); // Set of supported display refresh rates for easy lookup // when FrameRateOverride::AppOverrideNativeRefreshRates is in use. ftl::SmallMap mAppOverrideNativeRefreshRates; ftl::Optional mActiveModeOpt GUARDED_BY(mLock); DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock); DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock); // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate. std::vector mPrimaryFrameRates GUARDED_BY(mLock); std::vector mAppRequestFrameRates GUARDED_BY(mLock); std::vector mAllFrameRates GUARDED_BY(mLock); // Caches whether the device is VRR-compatible based on the active display mode. std::atomic_bool mIsVrrDevice = false; Policy mDisplayManagerPolicy GUARDED_BY(mLock); std::optional mOverridePolicy GUARDED_BY(mLock); unsigned mNumModeSwitchesInPolicy GUARDED_BY(kMainThreadContext) = 0; mutable std::mutex mLock; // A sorted list of known frame rates that a Heuristic layer will choose // from based on the closest value. const std::vector mKnownFrameRates; const Config mConfig; // A list of known frame rates that favors at least 60Hz if there is no exact match display // refresh rate const std::vector mFrameRatesThatFavorsAtLeast60 = {23.976_Hz, 25_Hz, 29.97_Hz, 50_Hz, 59.94_Hz}; Config::FrameRateOverride mFrameRateOverrideConfig; struct GetRankedFrameRatesCache { std::vector layers; GlobalSignals signals; Fps pacesetterFps; RankedFrameRates result; bool matches(const GetRankedFrameRatesCache& other) const { return layers == other.layers && signals == other.signals && isApproxEqual(pacesetterFps, other.pacesetterFps); } }; mutable std::optional mGetRankedFrameRatesCache GUARDED_BY(mLock); // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed. std::mutex mIdleTimerCallbacksMutex; std::optional mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex); // Used to detect (lack of) frame activity. ftl::Optional mIdleTimer; std::atomic mIdleTimerStarted = false; // Returns the range of supported frame rates. FpsRange getSupportedFrameRateRangeLocked() const REQUIRES(mLock); }; } // namespace android::scheduler