SF: Rename RefreshRateConfigs
"Configs" is vague and overloaded, and collections thereof cannot simply
pluralize the name. Also, update the stale class comment.
Bug: 241285191
Test: Build
Change-Id: I3b6d2259dcaa390f44c07caa07c05361c6cb428b
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
new file mode 100644
index 0000000..bff16d3
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -0,0 +1,473 @@
+/*
+ * 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 <algorithm>
+#include <numeric>
+#include <optional>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include <ftl/concat.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
+#include "DisplayHardware/DisplayMode.h"
+#include "DisplayHardware/HWComposer.h"
+#include "Scheduler/OneShotTimer.h"
+#include "Scheduler/StrongTyping.h"
+#include "ThreadContext.h"
+#include "Utils/Dumper.h"
+
+namespace android::scheduler {
+
+using namespace std::chrono_literals;
+
+enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
+
+inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
+ using T = std::underlying_type_t<DisplayModeEvent>;
+ return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+}
+
+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();
+
+ 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 range represents display manager's general guidance on the
+ // display modes we'll consider when switching refresh rates. Unless we get an explicit
+ // signal from an app, we should stay within this range.
+ FpsRange primaryRange;
+ // The app request refresh rate range allows us to consider more display modes when
+ // switching refresh rates. Although we should generally stay within the primary range,
+ // specific considerations, such as layer frame rate settings specified via the
+ // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
+ // app request range. The app request range will be greater than or equal to the primary
+ // refresh rate range, never smaller.
+ FpsRange appRequestRange;
+
+ Policy() = default;
+
+ Policy(DisplayModeId defaultMode, FpsRange range)
+ : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
+
+ Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range)
+ : Policy(defaultMode, allowGroupSwitching, range, range) {}
+
+ Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange)
+ : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
+
+ Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange,
+ FpsRange appRequestRange)
+ : defaultMode(defaultMode),
+ allowGroupSwitching(allowGroupSwitching),
+ primaryRange(primaryRange),
+ appRequestRange(appRequestRange) {}
+
+ bool operator==(const Policy& other) const {
+ using namespace fps_approx_ops;
+ return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
+ appRequestRange == other.appRequestRange &&
+ allowGroupSwitching == other.allowGroupSwitching;
+ }
+
+ bool operator!=(const Policy& other) const { return !(*this == other); }
+ 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<DisplayManagerPolicy, OverridePolicy, NoOverridePolicy>;
+
+ 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(DisplayModeId) 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
+
+ ftl_last = ExplicitExact
+ };
+
+ // 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<uid_t>(-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'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;
+ }
+
+ bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+ };
+
+ // 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 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 ScoredRefreshRate {
+ DisplayModePtr modePtr;
+ float score = 0.0f;
+
+ bool operator==(const ScoredRefreshRate& other) const {
+ return modePtr == other.modePtr && 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 ScoredRefreshRate& lhs, const ScoredRefreshRate& rhs) const {
+ return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
+ }
+ };
+ };
+
+ using RefreshRateRanking = std::vector<ScoredRefreshRate>;
+
+ struct RankedRefreshRates {
+ RefreshRateRanking ranking; // Ordered by descending score.
+ GlobalSignals consideredSignals;
+
+ bool operator==(const RankedRefreshRates& other) const {
+ return ranking == other.ranking && consideredSignals == other.consideredSignals;
+ }
+ };
+
+ RankedRefreshRates getRankedRefreshRates(const std::vector<LayerRequirement>&,
+ GlobalSignals) const EXCLUDES(mLock);
+
+ FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
+ }
+
+ std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
+ bool timerExpired) const EXCLUDES(mLock);
+
+ void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+
+ // See mActiveModeIt for thread safety.
+ DisplayModePtr getActiveModePtr() const EXCLUDES(mLock);
+ const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext);
+
+ // 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 {
+ bool enableFrameRateOverride = false;
+
+ // 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 idleTimerTimeout = 0ms;
+
+ // The controller representing how the kernel idle timer will be configured
+ // either on the HWC api or sysprop.
+ std::optional<KernelIdleTimerController> kernelIdleTimerController;
+ };
+
+ RefreshRateSelector(DisplayModes, DisplayModeId activeModeId,
+ Config config = {.enableFrameRateOverride = false,
+ .frameRateMultipleThreshold = 0,
+ .idleTimerTimeout = 0ms,
+ .kernelIdleTimerController = {}});
+
+ RefreshRateSelector(const RefreshRateSelector&) = delete;
+ RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
+
+ // 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.
+ bool canSwitch() const EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return mDisplayModes.size() > 1;
+ }
+
+ // 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 supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
+
+ // 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<uid_t, Fps>;
+
+ // Returns the frame rate override for each uid.
+ UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
+ Fps displayFrameRate, GlobalSignals) const
+ EXCLUDES(mLock);
+
+ std::optional<KernelIdleTimerController> kernelIdleTimerController() {
+ return mConfig.kernelIdleTimerController;
+ }
+
+ struct IdleTimerCallbacks {
+ struct Callbacks {
+ std::function<void()> onReset;
+ std::function<void()> onExpired;
+ };
+
+ Callbacks platform;
+ Callbacks kernel;
+ };
+
+ 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() {
+ if (mIdleTimer) {
+ mIdleTimer->start();
+ }
+ }
+
+ void stopIdleTimer() {
+ if (mIdleTimer) {
+ mIdleTimer->stop();
+ }
+ }
+
+ void resetIdleTimer(bool kernelOnly) {
+ if (!mIdleTimer) {
+ return;
+ }
+ if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) {
+ return;
+ }
+ mIdleTimer->reset();
+ }
+
+ void dump(utils::Dumper&) const EXCLUDES(mLock);
+
+ std::chrono::milliseconds getIdleTimerTimeout();
+
+private:
+ friend struct TestableRefreshRateSelector;
+
+ void constructAvailableRefreshRates() REQUIRES(mLock);
+
+ // See mActiveModeIt for thread safety.
+ DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
+
+ RankedRefreshRates getRankedRefreshRatesLocked(const std::vector<LayerRequirement>&,
+ GlobalSignals) const REQUIRES(mLock);
+
+ // Returns number of display frames and remainder when dividing the layer refresh period by
+ // display refresh period.
+ std::pair<nsecs_t, nsecs_t> 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 };
+
+ // Only uses the primary range, not the app request range.
+ RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt =
+ std::nullopt) 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 calculateRefreshRateScoreForFps(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);
+
+ void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
+ REQUIRES(kMainThreadContext);
+
+ void initializeIdleTimer();
+
+ std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
+ REQUIRES(mIdleTimerCallbacksMutex) {
+ if (!mIdleTimerCallbacks) return {};
+ return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
+ : mIdleTimerCallbacks->platform;
+ }
+
+ // 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);
+
+ // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
+ // need not be under mLock.
+ DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
+
+ 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<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
+ std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
+
+ Policy mDisplayManagerPolicy GUARDED_BY(mLock);
+ std::optional<Policy> 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<Fps> mKnownFrameRates;
+
+ const Config mConfig;
+ bool mSupportsFrameRateOverrideByContent;
+
+ struct GetRankedRefreshRatesCache {
+ std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
+ RankedRefreshRates result;
+ };
+ mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
+
+ // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
+ std::mutex mIdleTimerCallbacksMutex;
+ std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
+ // Used to detect (lack of) frame activity.
+ std::optional<scheduler::OneShotTimer> mIdleTimer;
+};
+
+} // namespace android::scheduler