blob: 05a8692f517d15e07013f30137a7780ef1dabb3c [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 <algorithm>
#include <numeric>
#include <optional>
#include <type_traits>
#include <utility>
#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"
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;
/**
* 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:
// Margin used when matching refresh rates to the content desired ones.
static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
std::chrono::nanoseconds(800us).count();
struct Policy {
private:
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;
};
// Return code set*Policy() to indicate the current policy is unchanged.
static constexpr int CURRENT_POLICY_UNCHANGED = 1;
// 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.
// Sets the display manager policy to choose refresh rates. The return value will be:
// - A negative value if the policy is invalid or another error occurred.
// - NO_ERROR if the policy was successfully updated, and the current policy is different from
// what it was before the call.
// - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
// is the same as it was before the call.
status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
// Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
// 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;
bool operator==(GlobalSignals other) const {
return touch == other.touch && idle == other.idle;
}
};
// Returns the refresh rate that best fits the given layers, and whether the refresh rate was
// chosen based on touch boost and/or idle timer.
std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRate(
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);
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
void setActiveModeId(DisplayModeId) EXCLUDES(mLock);
DisplayModePtr 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 {
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;
};
RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId,
Config config = {.enableFrameRateOverride = false,
.frameRateMultipleThreshold = 0,
.idleTimerTimeout = 0ms,
.kernelIdleTimerController = {}});
RefreshRateConfigs(const RefreshRateConfigs&) = delete;
RefreshRateConfigs& operator=(const RefreshRateConfigs&) = 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(std::string& result) const EXCLUDES(mLock);
std::chrono::milliseconds getIdleTimerTimeout();
private:
friend struct TestableRefreshRateConfigs;
void constructAvailableRefreshRates() REQUIRES(mLock);
std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked(
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);
const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
return getMaxRefreshRateByPolicyLocked(mActiveModeIt->second->getGroup());
}
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
// Returns whether the layer is allowed to vote for the given refresh rate.
bool isVoteAllowed(const LayerRequirement&, Fps) const;
// 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);
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);
DisplayModeIterator mActiveModeIt 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<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
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 GetBestRefreshRateCache {
std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
std::pair<DisplayModePtr, GlobalSignals> result;
};
mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache 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