blob: b755798a9277d2f6ca061874004e9bc8e9a3d75d [file] [log] [blame]
Ady Abraham8a82ba62020-01-17 12:43:17 -08001/*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// #define LOG_NDEBUG 0
18
19#include "LayerInfoV2.h"
20
21#include <algorithm>
22#include <utility>
23
24#undef LOG_TAG
25#define LOG_TAG "LayerInfoV2"
26#define ATRACE_TAG ATRACE_TAG_GRAPHICS
27
28namespace android::scheduler {
29
30LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
31 : mHighRefreshRatePeriod(highRefreshRatePeriod),
32 mDefaultVote(defaultVote),
33 mLayerVote({defaultVote, 0.0f}) {}
34
35void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
36 lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
37
38 mLastUpdatedTime = std::max(lastPresentTime, now);
39
40 FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
41
42 mFrameTimes.push_back(frameTime);
43 if (mFrameTimes.size() > HISTORY_SIZE) {
44 mFrameTimes.pop_front();
45 }
46}
47
48// Returns whether the earliest present time is within the active threshold.
49bool LayerInfoV2::isRecentlyActive(nsecs_t now) const {
50 if (mFrameTimes.empty()) {
51 return false;
52 }
53
54 return mFrameTimes.back().queueTime >= getActiveLayerThreshold(now);
55}
56
Ady Abrahama61edcb2020-01-30 18:32:03 -080057bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
58 return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
59 mFrameTimeValidSince.time_since_epoch())
60 .count();
61}
62
Ady Abraham8a82ba62020-01-17 12:43:17 -080063bool LayerInfoV2::isFrequent(nsecs_t now) const {
Ady Abraham4ccdcb42020-02-11 17:34:34 -080064 // Find the first valid frame time
65 auto it = mFrameTimes.begin();
66 for (; it != mFrameTimes.end(); ++it) {
67 if (isFrameTimeValid(*it)) {
68 break;
69 }
70 }
71
Ady Abrahama61edcb2020-01-30 18:32:03 -080072 // If we know nothing about this layer we consider it as frequent as it might be the start
73 // of an animation.
Ady Abraham4ccdcb42020-02-11 17:34:34 -080074 if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
Ady Abrahama61edcb2020-01-30 18:32:03 -080075 return true;
Ady Abraham8a82ba62020-01-17 12:43:17 -080076 }
77
Ady Abraham4ccdcb42020-02-11 17:34:34 -080078 // Find the first active frame
79 for (; it != mFrameTimes.end(); ++it) {
80 if (it->queueTime >= getActiveLayerThreshold(now)) {
81 break;
82 }
Ady Abrahama61edcb2020-01-30 18:32:03 -080083 }
84
Ady Abraham4ccdcb42020-02-11 17:34:34 -080085 const auto numFrames = std::distance(it, mFrameTimes.end()) - 1;
86 if (numFrames <= 0) {
87 return false;
88 }
89
90 // Layer is considered frequent if the average frame rate is higher than the threshold
91 const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
92 return (1e9f * numFrames) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
Ady Abraham8a82ba62020-01-17 12:43:17 -080093}
94
95bool LayerInfoV2::hasEnoughDataForHeuristic() const {
96 // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
Ady Abrahama61edcb2020-01-30 18:32:03 -080097 if (mFrameTimes.size() < 2) {
98 return false;
99 }
100
101 if (!isFrameTimeValid(mFrameTimes.front())) {
102 return false;
103 }
104
Ady Abraham8a82ba62020-01-17 12:43:17 -0800105 if (mFrameTimes.size() < HISTORY_SIZE &&
106 mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
107 return false;
108 }
109
110 return true;
111}
112
113std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
114 static constexpr float MARGIN = 1.0f; // 1Hz
115
116 if (!hasEnoughDataForHeuristic()) {
117 ALOGV("Not enough data");
118 return std::nullopt;
119 }
120
121 // Calculate the refresh rate by finding the average delta between frames
122 nsecs_t totalPresentTimeDeltas = 0;
123 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
124 // If there are no presentation timestamp provided we can't calculate the refresh rate
125 if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
126 return std::nullopt;
127 }
128
129 totalPresentTimeDeltas +=
130 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
131 }
132 const float averageFrameTime =
133 static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
134
135 // Now once we calculated the refresh rate we need to make sure that all the frames we captured
Ady Abrahamf6b77072020-01-30 14:22:54 -0800136 // are evenly distributed and we don't calculate the average across some burst of frames.
Ady Abraham8a82ba62020-01-17 12:43:17 -0800137 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
138 const nsecs_t presentTimeDeltas =
139 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
140 if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
141 return std::nullopt;
142 }
143 }
144
145 const auto refreshRate = 1e9f / averageFrameTime;
146 if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
147 mLastReportedRefreshRate = refreshRate;
148 }
149
150 ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
151 return mLastReportedRefreshRate;
152}
153
154std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
155 if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
156 return {mLayerVote.type, mLayerVote.fps};
157 }
158
159 if (!isFrequent(now)) {
160 return {LayerHistory::LayerVoteType::Min, 0};
161 }
162
163 auto refreshRate = calculateRefreshRateIfPossible();
164 if (refreshRate.has_value()) {
165 return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
166 }
167
168 return {LayerHistory::LayerVoteType::Max, 0};
169}
170
171} // namespace android::scheduler