blob: 6051e8935d968fa36454cba3613e5032710a9d90 [file] [log] [blame]
/*
* 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 <type_traits>
#include <utility>
#include <variant>
#include <ftl/concat.h>
#include <ftl/optional.h>
#include <ftl/unit.h>
#include <gui/DisplayEventReceiver.h>
#include <scheduler/Fps.h>
#include <scheduler/FrameRateMode.h>
#include <scheduler/Seamlessness.h>
#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;
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;
Policy() = default;
Policy(DisplayModeId defaultMode, FpsRange range,
bool allowGroupSwitching = kAllowGroupSwitchingDefault)
: Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range},
allowGroupSwitching) {}
Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges,
bool allowGroupSwitching = kAllowGroupSwitchingDefault)
: defaultMode(defaultMode),
allowGroupSwitching(allowGroupSwitching),
primaryRanges(primaryRanges),
appRequestRanges(appRequestRanges) {}
bool operator==(const Policy& other) const {
using namespace fps_approx_ops;
return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges &&
appRequestRanges == other.appRequestRanges &&
allowGroupSwitching == other.allowGroupSwitching;
}
bool operator!=(const Policy& other) const { return !(*this == other); }
bool primaryRangeIsSingleRate() const {
return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max);
}
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(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<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 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 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<ScoredFrameRate>;
struct RankedFrameRates {
FrameRateRanking ranking; // Ordered by descending score.
GlobalSignals consideredSignals;
bool operator==(const RankedFrameRates& other) const {
return ranking == other.ranking && consideredSignals == other.consideredSignals;
}
};
RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
return {mMinRefreshRateModeIt->second->getPeakFps(),
mMaxRefreshRateModeIt->second->getPeakFps()};
}
ftl::Optional<FrameRateMode> onKernelTimerChanged(ftl::Optional<DisplayModeId> 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 idleTimerTimeout = 0ms;
// The controller representing how the kernel idle timer will be configured
// either on the HWC api or sysprop.
ftl::Optional<KernelIdleTimerController> kernelIdleTimerController;
};
RefreshRateSelector(
DisplayModes, DisplayModeId activeModeId,
Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled,
.frameRateMultipleThreshold = 0,
.idleTimerTimeout = 0ms,
.kernelIdleTimerController = {}});
RefreshRateSelector(const RefreshRateSelector&) = delete;
RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
const DisplayModes& displayModes() const { 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<uid_t, Fps>;
// Returns the frame rate override for each uid.
UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
Fps displayFrameRate, GlobalSignals) const
EXCLUDES(mLock);
// Gets the FpsRange that the FrameRateCategory represents.
static FpsRange getFrameRateCategoryRange(FrameRateCategory category);
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 resetKernelIdleTimer() {
if (mIdleTimer && mConfig.kernelIdleTimerController) {
mIdleTimer->reset();
}
}
void resetIdleTimer() {
if (mIdleTimer) {
mIdleTimer->reset();
}
}
void dump(utils::Dumper&) const EXCLUDES(mLock);
std::chrono::milliseconds getIdleTimerTimeout();
private:
friend struct TestableRefreshRateSelector;
void constructAvailableRefreshRates() REQUIRES(mLock);
// See mActiveModeOpt for thread safety.
const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) 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,
ftl_last = Descending
};
typedef std::function<bool(const FrameRateMode)> 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<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
std::optional<DisplayModeId> 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::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
REQUIRES(mIdleTimerCallbacksMutex) {
if (!mIdleTimerCallbacks) return {};
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<FrameRateMode> createFrameRateModes(
const Policy&, std::function<bool(const DisplayMode&)>&& 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<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
ftl::Optional<FrameRateMode> 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<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
std::vector<FrameRateMode> mAppRequestFrameRates 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;
// A list of known frame rates that favors at least 60Hz if there is no exact match display
// refresh rate
const std::vector<Fps> mFrameRatesThatFavorsAtLeast60 = {23.976_Hz, 25_Hz, 29.97_Hz, 50_Hz,
59.94_Hz};
Config::FrameRateOverride mFrameRateOverrideConfig;
struct GetRankedFrameRatesCache {
std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
RankedFrameRates result;
};
mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache 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.
ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
};
} // namespace android::scheduler