SF: Improve LayerInfo::calculateAverageFrameTime
This refactors and improves LayerInfo::calculateAverageFrameTime.
The behaviour is changed in two ways:
* if two consecutive frames are too close to each other we count
them as one frame and consider the delta between them in
the total. This gives a better estimation for the average
refresh rate. See CalculateAverageFrameTimeTest::ignoresSmallPeriods
which was failing with the previous implementation.
* if two consecutive frames are too far apart we discard the delta
between them. This is covered by the test "ignoresLargePeriods".
Fixes: 170476958
Test: atest CalculateAverageFrameTimeTest
Change-Id: If98199bb8198f74c93e93c9996107c021f1bc7ba
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 0fa71f1..4324855 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -54,7 +54,7 @@
break;
case LayerUpdateType::SetFrameRate:
case LayerUpdateType::Buffer:
- FrameTimeData frameTime = {.presetTime = lastPresentTime,
+ FrameTimeData frameTime = {.presentTime = lastPresentTime,
.queueTime = mLastUpdatedTime,
.pendingConfigChange = pendingConfigChange};
mFrameTimes.push_back(frameTime);
@@ -74,7 +74,7 @@
bool LayerInfo::isFrequent(nsecs_t now) const {
// If we know nothing about this layer we consider it as frequent as it might be the start
// of an animation.
- if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+ if (mFrameTimes.size() < kFrequentLayerWindowSize) {
return true;
}
@@ -87,14 +87,14 @@
}
const auto numFrames = std::distance(it, mFrameTimes.end());
- if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
+ if (numFrames < kFrequentLayerWindowSize) {
return false;
}
// 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(MIN_FPS_FOR_FREQUENT_LAYER);
+ .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer);
}
bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -124,32 +124,21 @@
}
std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
- nsecs_t totalPresentTimeDeltas = 0;
- nsecs_t totalQueueTimeDeltas = 0;
- bool missingPresentTime = false;
- int numFrames = 0;
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- // Ignore frames captured during a config change
- if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
- return std::nullopt;
- }
+ // Ignore frames captured during a config change
+ const bool isDuringConfigChange =
+ std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
+ [](auto frame) { return frame.pendingConfigChange; });
+ if (isDuringConfigChange) {
+ return std::nullopt;
+ }
- totalQueueTimeDeltas +=
- std::max(((it + 1)->queueTime - it->queueTime), kMinPeriodBetweenFrames);
- numFrames++;
-
- if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
- missingPresentTime = true;
- // If there are no presentation timestamps and we haven't calculated
- // one in the past then we can't calculate the refresh rate
- if (!mLastRefreshRate.reported.isValid()) {
- return std::nullopt;
- }
- continue;
- }
-
- totalPresentTimeDeltas +=
- std::max(((it + 1)->presetTime - it->presetTime), kMinPeriodBetweenFrames);
+ const bool isMissingPresentTime =
+ std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
+ [](auto frame) { return frame.presentTime == 0; });
+ if (isMissingPresentTime && !mLastRefreshRate.reported.isValid()) {
+ // If there are no presentation timestamps and we haven't calculated
+ // one in the past then we can't calculate the refresh rate
+ return std::nullopt;
}
// Calculate the average frame time based on presentation timestamps. If those
@@ -160,9 +149,35 @@
// presentation timestamps we look at the queue time to see if the current refresh rate still
// matches the content.
- const auto averageFrameTime =
- static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
- numFrames;
+ auto getFrameTime = isMissingPresentTime ? [](FrameTimeData data) { return data.queueTime; }
+ : [](FrameTimeData data) { return data.presentTime; };
+
+ nsecs_t totalDeltas = 0;
+ int numDeltas = 0;
+ auto prevFrame = mFrameTimes.begin();
+ for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) {
+ const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame);
+ if (currDelta < kMinPeriodBetweenFrames) {
+ // Skip this frame, but count the delta into the next frame
+ continue;
+ }
+
+ prevFrame = it;
+
+ if (currDelta > kMaxPeriodBetweenFrames) {
+ // Skip this frame and the current delta.
+ continue;
+ }
+
+ totalDeltas += currDelta;
+ numDeltas++;
+ }
+
+ if (numDeltas == 0) {
+ return std::nullopt;
+ }
+
+ const auto averageFrameTime = static_cast<double>(totalDeltas) / static_cast<double>(numDeltas);
return static_cast<nsecs_t>(averageFrameTime);
}