SF: Polish Fps class
For consistent comparison (which will later be changed to check ULP
distance instead of the magnitude-dependent threshold), express the
inequality operators in terms of equality, and avoid hashing floats.
Add Hz literals and (namespaced) operators.
Bug: 129481165
Bug: 185535769
Test: libsurfaceflinger_unittest
Test: dumpsys SurfaceFlinger --vsync
Change-Id: I79be5d2dd031218c4054774d2645efb337211538
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 8a45b66..314526a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -41,7 +41,7 @@
: mName(name),
mOwnerUid(ownerUid),
mDefaultVote(defaultVote),
- mLayerVote({defaultVote, Fps(0.0f)}),
+ mLayerVote({defaultVote, Fps()}),
mRefreshRateHistory(name) {}
void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
@@ -93,10 +93,11 @@
return false;
}
+ using fps_approx_ops::operator>=;
+
// Layer is considered frequent if the average frame rate is higher than the threshold
const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
- return Fps::fromPeriodNsecs(totalTime / (numFrames - 1))
- .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer);
+ return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)) >= kMinFpsForFrequentLayer;
}
bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -191,17 +192,17 @@
return std::nullopt;
}
- const auto averageFrameTime = calculateAverageFrameTime();
- if (averageFrameTime.has_value()) {
+ if (const auto averageFrameTime = calculateAverageFrameTime()) {
const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
if (refreshRateConsistent) {
const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
- // To avoid oscillation, use the last calculated refresh rate if it is
- // close enough
+ using fps_approx_ops::operator!=;
+
+ // To avoid oscillation, use the last calculated refresh rate if it is close enough.
if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
MARGIN &&
- !mLastRefreshRate.reported.equalsWithMargin(knownRefreshRate)) {
+ mLastRefreshRate.reported != knownRefreshRate) {
mLastRefreshRate.calculated = refreshRate;
mLastRefreshRate.reported = knownRefreshRate;
}
@@ -228,15 +229,15 @@
if (isAnimating(now)) {
ALOGV("%s is animating", mName.c_str());
mLastRefreshRate.animatingOrInfrequent = true;
- return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+ return {LayerHistory::LayerVoteType::Max, Fps()};
}
if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
mLastRefreshRate.animatingOrInfrequent = true;
- // Infrequent layers vote for mininal refresh rate for
+ // Infrequent layers vote for minimal refresh rate for
// battery saving purposes and also to prevent b/135718869.
- return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
+ return {LayerHistory::LayerVoteType::Min, Fps()};
}
// If the layer was previously tagged as animating or infrequent, we clear
@@ -253,7 +254,7 @@
}
ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
- return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+ return {LayerHistory::LayerVoteType::Max, Fps()};
}
const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
@@ -300,9 +301,13 @@
bool LayerInfo::RefreshRateHistory::isConsistent() const {
if (mRefreshRates.empty()) return true;
- const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto consistent =
+ const auto [min, max] =
+ std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
+ });
+
+ const bool consistent =
max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
if (CC_UNLIKELY(sTraceEnabled)) {
@@ -321,4 +326,4 @@
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index ce9783c..92abbae 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -51,7 +51,7 @@
// is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
// favor of a low refresh rate.
static constexpr size_t kFrequentLayerWindowSize = 3;
- static constexpr Fps kMinFpsForFrequentLayer{10.0f};
+ static constexpr Fps kMinFpsForFrequentLayer = 10_Hz;
static constexpr auto kMaxPeriodForFrequentLayerNs =
std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;
@@ -62,7 +62,7 @@
// Holds information about the layer vote
struct LayerVote {
LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
- Fps fps{0.0f};
+ Fps fps;
Seamlessness seamlessness = Seamlessness::Default;
};
@@ -86,19 +86,17 @@
using Seamlessness = scheduler::Seamlessness;
Fps rate;
- FrameRateCompatibility type;
- Seamlessness seamlessness;
+ FrameRateCompatibility type = FrameRateCompatibility::Default;
+ Seamlessness seamlessness = Seamlessness::Default;
- FrameRate()
- : rate(0),
- type(FrameRateCompatibility::Default),
- seamlessness(Seamlessness::Default) {}
+ FrameRate() = default;
+
FrameRate(Fps rate, FrameRateCompatibility type,
Seamlessness seamlessness = Seamlessness::OnlySeamless)
: rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
bool operator==(const FrameRate& other) const {
- return rate.equalsWithMargin(other.rate) && type == other.type &&
+ return isApproxEqual(rate, other.rate) && type == other.type &&
seamlessness == other.seamlessness;
}
@@ -151,7 +149,7 @@
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
// Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
+ void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default}; }
std::string getName() const { return mName; }
@@ -201,9 +199,9 @@
// Holds information about the calculated and reported refresh rate
struct RefreshRateHeuristicData {
// Rate calculated on the layer
- Fps calculated{0.0f};
+ Fps calculated;
// Last reported rate for LayerInfo::getRefreshRate()
- Fps reported{0.0f};
+ Fps reported;
// Whether the last reported rate for LayerInfo::getRefreshRate()
// was due to animation or infrequent updates
bool animatingOrInfrequent = false;
@@ -229,14 +227,8 @@
// Holds the refresh rate when it was calculated
struct RefreshRateData {
- Fps refreshRate{0.0f};
+ Fps refreshRate;
nsecs_t timestamp = 0;
-
- bool operator<(const RefreshRateData& other) const {
- // We don't need comparison with margins since we are using
- // this to find the min and max refresh rates.
- return refreshRate.getValue() < other.refreshRate.getValue();
- }
};
// Holds tracing strings
@@ -268,7 +260,7 @@
// Used for sanitizing the heuristic data. If two frames are less than
// this period apart from each other they'll be considered as duplicates.
- static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
+ static constexpr nsecs_t kMinPeriodBetweenFrames = (240_Hz).getPeriodNsecs();
// Used for sanitizing the heuristic data. If two frames are more than
// this period apart from each other, the interval between them won't be
// taken into account when calculating average frame rate.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c9f00a0..aabd88a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -42,19 +42,18 @@
}
std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
- std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+ std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
knownFrameRates.reserve(knownFrameRates.size() + modes.size());
- // Add all supported refresh rates to the set
+ // Add all supported refresh rates.
for (const auto& mode : modes) {
- const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
- knownFrameRates.emplace_back(refreshRate);
+ knownFrameRates.push_back(Fps::fromPeriodNsecs(mode->getVsyncPeriod()));
}
- // Sort and remove duplicates
- std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
+ // Sort and remove duplicates.
+ std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
- Fps::EqualsWithMargin()),
+ isApproxEqual),
knownFrameRates.end());
return knownFrameRates;
}
@@ -64,6 +63,11 @@
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+bool RefreshRate::inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
+ using fps_approx_ops::operator<=;
+ return minRefreshRate <= getFps() && getFps() <= maxRefreshRate;
+}
+
std::string RefreshRate::toString() const {
return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
getModeId().value(), mode->getHwcId(), getFps().getValue(),
@@ -110,14 +114,14 @@
bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
const RefreshRate& refreshRate) const {
+ using namespace fps_approx_ops;
+
switch (layer.vote) {
case LayerVoteType::ExplicitExactOrMultiple:
case LayerVoteType::Heuristic:
if (mConfig.frameRateMultipleThreshold != 0 &&
- refreshRate.getFps().greaterThanOrEqualWithMargin(
- Fps(mConfig.frameRateMultipleThreshold)) &&
- layer.desiredRefreshRate.lessThanWithMargin(
- Fps(mConfig.frameRateMultipleThreshold / 2))) {
+ refreshRate.getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
+ layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
// Don't vote high refresh rates past the threshold for layers with a low desired
// refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
// 120 Hz, but desired 60 fps should have a vote.
@@ -247,7 +251,7 @@
};
RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ GlobalSignals globalSignals,
GlobalSignals* outSignalsConsidered) const {
std::lock_guard lock(mLock);
@@ -269,7 +273,7 @@
}
std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
GlobalSignals* outSignalsConsidered) const {
const bool sameAsLastCall = lastBestRefreshRateInvocation &&
lastBestRefreshRateInvocation->layerRequirements == layers &&
@@ -286,7 +290,7 @@
}
RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -370,7 +374,7 @@
// move out the of range if layers explicitly request a different refresh
// rate.
const bool primaryRangeIsSingleRate =
- policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
+ isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
if (!globalSignals.touch && globalSignals.idle &&
!(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
@@ -498,8 +502,11 @@
return explicitExact == 0;
}
}();
+
+ using fps_approx_ops::operator<;
+
if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- bestRefreshRate->getFps().lessThanWithMargin(touchRefreshRate.getFps())) {
+ bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
setTouchConsidered();
ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate;
@@ -552,7 +559,8 @@
}
RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
- const std::vector<LayerRequirement>& layers, Fps displayFrameRate, bool touch) const {
+ const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
+ GlobalSignals globalSignals) const {
ATRACE_CALL();
if (!mSupportsFrameRateOverride) return {};
@@ -570,7 +578,7 @@
return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
});
- if (touch && hasExplicitExactOrMultiple) {
+ if (globalSignals.touch && hasExplicitExactOrMultiple) {
continue;
}
@@ -798,8 +806,10 @@
ALOGE("Default mode is not in the primary range.");
return false;
}
- return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) &&
- policy.appRequestRange.max.greaterThanOrEqualWithMargin(policy.primaryRange.max);
+
+ using namespace fps_approx_ops;
+ return policy.appRequestRange.min <= policy.primaryRange.min &&
+ policy.appRequestRange.max >= policy.primaryRange.max;
}
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
@@ -925,19 +935,21 @@
}
Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
- if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
- return *mKnownFrameRates.begin();
+ using namespace fps_approx_ops;
+
+ if (frameRate <= mKnownFrameRates.front()) {
+ return mKnownFrameRates.front();
}
- if (frameRate.greaterThanOrEqualWithMargin(*std::prev(mKnownFrameRates.end()))) {
- return *std::prev(mKnownFrameRates.end());
+ if (frameRate >= mKnownFrameRates.back()) {
+ return mKnownFrameRates.back();
}
auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
- Fps::comparesLess);
+ isStrictlyLess);
- const auto distance1 = std::abs((frameRate.getValue() - lowerBound->getValue()));
- const auto distance2 = std::abs((frameRate.getValue() - std::prev(lowerBound)->getValue()));
+ const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
+ const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}
@@ -956,7 +968,7 @@
}
if (minByPolicy == maxByPolicy) {
// when min primary range in display manager policy is below device min turn on the timer.
- if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
+ if (isApproxLess(currentPolicy->primaryRange.min, deviceMin.getFps())) {
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
@@ -982,14 +994,14 @@
}
bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
- if (smaller.getValue() > bigger.getValue()) {
+ if (isStrictlyLess(bigger, smaller)) {
return isFractionalPairOrMultiple(bigger, smaller);
}
const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
constexpr float kCoef = 1000.f / 1001.f;
- return bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier / kCoef)) ||
- bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier * kCoef));
+ return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
+ isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
}
void RefreshRateConfigs::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 3abf83d..53472ef 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -76,19 +76,15 @@
// Checks whether the fps of this RefreshRate struct is within a given min and max refresh
// rate passed in. Margin of error is applied to the boundaries for approximation.
- bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
- return minRefreshRate.lessThanOrEqualWithMargin(getFps()) &&
- getFps().lessThanOrEqualWithMargin(maxRefreshRate);
- }
+ bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const;
- bool operator!=(const RefreshRate& other) const { return mode != other.mode; }
+ bool operator==(const RefreshRate& other) const { return mode == other.mode; }
+ bool operator!=(const RefreshRate& other) const { return !operator==(other); }
bool operator<(const RefreshRate& other) const {
- return getFps().getValue() < other.getFps().getValue();
+ return isStrictlyLess(getFps(), other.getFps());
}
- bool operator==(const RefreshRate& other) const { return !(*this != other); }
-
std::string toString() const;
friend std::ostream& operator<<(std::ostream& os, const RefreshRate& refreshRate) {
return os << refreshRate.toString();
@@ -105,11 +101,11 @@
std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
struct FpsRange {
- Fps min{0.0f};
- Fps max{std::numeric_limits<float>::max()};
+ Fps min = Fps::fromValue(0.f);
+ Fps max = Fps::fromValue(std::numeric_limits<float>::max());
bool operator==(const FpsRange& other) const {
- return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+ return isApproxEqual(min, other.min) && isApproxEqual(max, other.max);
}
bool operator!=(const FpsRange& other) const { return !(*this == other); }
@@ -221,7 +217,7 @@
// Layer vote type.
LayerVoteType vote = LayerVoteType::NoVote;
// Layer's desired refresh rate, if applicable.
- Fps desiredRefreshRate{0.0f};
+ 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
@@ -232,7 +228,7 @@
bool operator==(const LayerRequirement& other) const {
return name == other.name && vote == other.vote &&
- desiredRefreshRate.equalsWithMargin(other.desiredRefreshRate) &&
+ isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
seamlessness == other.seamlessness && weight == other.weight &&
focused == other.focused;
}
@@ -247,18 +243,14 @@
// True if the system hasn't seen any buffers posted to layers recently.
bool idle = false;
- bool operator==(const GlobalSignals& other) const {
+ bool operator==(GlobalSignals other) const {
return touch == other.touch && idle == other.idle;
}
};
- // Returns the refresh rate that fits best to the given layers.
- // layers - The layer requirements to consider.
- // globalSignals - global state of touch and idle
- // outSignalsConsidered - An output param that tells the caller whether the refresh rate was
- // chosen based on touch boost and/or idle timer.
- RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ // Returns the refresh rate that best fits the given layers. outSignalsConsidered returns
+ // whether the refresh rate was chosen based on touch boost and/or idle timer.
+ RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>&, GlobalSignals,
GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
@@ -349,13 +341,10 @@
static bool isFractionalPairOrMultiple(Fps, Fps);
using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
// Returns the frame rate override for each uid.
- //
- // @param layers list of visible layers
- // @param displayFrameRate the display frame rate
- // @param touch whether touch timer is active (i.e. user touched the screen recently)
- UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
- Fps displayFrameRate, bool touch) const
+ UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
+ Fps displayFrameRate, GlobalSignals) const
EXCLUDES(mLock);
bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; }
@@ -396,13 +385,12 @@
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
- std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>&,
+ GlobalSignals,
GlobalSignals* outSignalsConsidered) const
REQUIRES(mLock);
- RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>&, GlobalSignals,
GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
// Returns the refresh rate with the highest score in the collection specified from begin
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 208a767..80aa96f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -16,15 +16,17 @@
#pragma once
+#include <chrono>
#include <numeric>
+#include <android-base/stringprintf.h>
+#include <ftl/small_map.h>
+#include <utils/Timers.h>
+
#include "Fps.h"
#include "Scheduler/SchedulerUtils.h"
#include "TimeStats/TimeStats.h"
-#include "android-base/stringprintf.h"
-#include "utils/Timers.h"
-
namespace android::scheduler {
/**
@@ -40,6 +42,7 @@
static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
public:
+ // TODO(b/185535769): Inject clock to avoid sleeping in tests.
RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate,
android::hardware::graphics::composer::hal::PowerMode currentPowerMode)
: mTimeStats(timeStats),
@@ -58,7 +61,7 @@
// Sets config mode. If the mode has changed, it records how much time was spent in the previous
// mode.
void setRefreshRate(Fps currRefreshRate) {
- if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
+ if (isApproxEqual(mCurrentRefreshRate, currRefreshRate)) {
return;
}
mTimeStats.incrementRefreshRateSwitches();
@@ -66,25 +69,26 @@
mCurrentRefreshRate = currRefreshRate;
}
- // Returns a map between human readable refresh rate and number of seconds the device spent in
- // that mode.
- std::unordered_map<std::string, int64_t> getTotalTimes() {
+ // Maps stringified refresh rate to total time spent in that mode.
+ using TotalTimes = ftl::SmallMap<std::string, std::chrono::milliseconds, 3>;
+
+ TotalTimes getTotalTimes() {
// If the power mode is on, then we are probably switching between the config modes. If
// it's not then the screen is probably off. Make sure to flush times before printing
// them.
flushTime();
- std::unordered_map<std::string, int64_t> totalTime;
- // Multiple configs may map to the same name, e.g. "60fps". Add the
- // times for such configs together.
- for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[to_string(configId)] = 0;
+ TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
+ const auto zero = std::chrono::milliseconds::zero();
+
+ // Sum the times for modes that map to the same name, e.g. "60 Hz".
+ for (const auto& [fps, time] : mFpsTotalTimes) {
+ const auto string = to_string(fps);
+ const auto total = std::as_const(totalTimes).get(string).value_or(std::cref(zero));
+ totalTimes.emplace_or_replace(string, total.get() + time);
}
- for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[to_string(configId)] += time;
- }
- totalTime["ScreenOff"] = mScreenOffTime;
- return totalTime;
+
+ return totalTimes;
}
// Traverses through the map of config modes and returns how long they've been running in easy
@@ -102,28 +106,32 @@
// Calculates the time that passed in ms between the last time we recorded time and the time
// this method was called.
void flushTime() {
- nsecs_t currentTime = systemTime();
- nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
- int64_t timeElapsedMs = ns2ms(timeElapsed);
+ const nsecs_t currentTime = systemTime();
+ const nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
mPreviousRecordedTime = currentTime;
+ const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
+ const auto zero = std::chrono::milliseconds::zero();
+
uint32_t fps = 0;
+
if (mCurrentPowerMode == android::hardware::graphics::composer::hal::PowerMode::ON) {
// Normal power mode is counted under different config modes.
- if (mConfigModesTotalTime.find(mCurrentRefreshRate) == mConfigModesTotalTime.end()) {
- mConfigModesTotalTime[mCurrentRefreshRate] = 0;
- }
- mConfigModesTotalTime[mCurrentRefreshRate] += timeElapsedMs;
+ const auto total = std::as_const(mFpsTotalTimes)
+ .get(mCurrentRefreshRate)
+ .value_or(std::cref(zero));
+ mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
+
fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
} else {
- mScreenOffTime += timeElapsedMs;
+ mScreenOffTime += duration;
}
mTimeStats.recordRefreshRate(fps, timeElapsed);
}
// Formats the time in milliseconds into easy to read format.
- static std::string getDateFormatFromMs(int64_t timeMs) {
- auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+ static std::string getDateFormatFromMs(std::chrono::milliseconds time) {
+ auto [days, dayRemainderMs] = std::div(static_cast<int64_t>(time.count()), MS_PER_DAY);
auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
@@ -138,9 +146,8 @@
Fps mCurrentRefreshRate;
android::hardware::graphics::composer::hal::PowerMode mCurrentPowerMode;
- std::unordered_map<Fps, int64_t /* duration in ms */, std::hash<Fps>, Fps::EqualsInBuckets>
- mConfigModesTotalTime;
- int64_t mScreenOffTime = 0;
+ ftl::SmallMap<Fps, std::chrono::milliseconds, 2, FpsApproxEqual> mFpsTotalTimes;
+ std::chrono::milliseconds mScreenOffTime = std::chrono::milliseconds::zero();
nsecs_t mPreviousRecordedTime = systemTime();
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index c6a19de..47b3568 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -206,14 +206,14 @@
{
const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
if (iter != mFrameRateOverridesFromBackdoor.end()) {
- return std::make_optional<Fps>(iter->second);
+ return iter->second;
}
}
{
const auto iter = mFrameRateOverridesByContent.find(uid);
if (iter != mFrameRateOverridesByContent.end()) {
- return std::make_optional<Fps>(iter->second);
+ return iter->second;
}
}
@@ -697,15 +697,16 @@
return mRefreshRateConfigs->getCurrentRefreshRate();
}();
- constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER{65.0f};
- if (state == TimerState::Reset &&
- refreshRate.getFps().greaterThanWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
+ constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
+ using namespace fps_approx_ops;
+
+ if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
} else if (state == TimerState::Expired &&
- refreshRate.getFps().lessThanOrEqualWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
+ refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
// need to update the VsyncController model anyway.
@@ -781,13 +782,12 @@
if (!consideredSignals.idle) {
const auto frameRateOverrides =
refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements,
- displayRefreshRate,
- consideredSignals.touch);
+ displayRefreshRate, consideredSignals);
std::lock_guard lock(mFrameRateOverridesLock);
if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
frameRateOverrides.begin(), frameRateOverrides.end(),
- [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
- return a.first == b.first && a.second.equalsWithMargin(b.second);
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
})) {
mFrameRateOverridesByContent = frameRateOverrides;
return true;
@@ -912,7 +912,8 @@
std::lock_guard lock(mFrameRateOverridesLock);
if (frameRateOverride.frameRateHz != 0.f) {
- mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+ mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
+ Fps::fromValue(frameRateOverride.frameRateHz);
} else {
mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
}
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 43e0297..ff31651 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -48,14 +48,12 @@
}
PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
- const auto iter = mOffsetsCache.find(fps);
- if (iter != mOffsetsCache.end()) {
- return iter->second;
+ if (const auto offsets = mOffsetsCache.get(fps)) {
+ return offsets->get();
}
- const auto offset = constructOffsets(fps.getPeriodNsecs());
- mOffsetsCache[fps] = offset;
- return offset;
+ const auto [it, _] = mOffsetsCache.try_emplace(fps, constructOffsets(fps.getPeriodNsecs()));
+ return it->second;
}
void VsyncConfiguration::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 3e53b3f..8447512 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -17,10 +17,10 @@
#pragma once
#include <mutex>
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
+#include <optional>
+#include <string>
+#include <ftl/small_map.h>
#include <utils/Timers.h>
#include "Fps.h"
@@ -88,9 +88,8 @@
VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
- mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
- mOffsetsCache GUARDED_BY(mLock);
- std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+ mutable ftl::SmallMap<Fps, VsyncConfigSet, 2, FpsApproxEqual> mOffsetsCache GUARDED_BY(mLock);
+ Fps mRefreshRateFps GUARDED_BY(mLock);
mutable std::mutex mLock;
};