blob: 276afd8f0968038b54591079675c890dbac21c8d [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
Ady Abrahama6b676e2020-05-27 14:29:09 -070030LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
31 LayerHistory::LayerVoteType defaultVote)
32 : mName(name),
33 mHighRefreshRatePeriod(highRefreshRatePeriod),
Ady Abraham8a82ba62020-01-17 12:43:17 -080034 mDefaultVote(defaultVote),
35 mLayerVote({defaultVote, 0.0f}) {}
36
Ady Abraham32efd542020-05-19 17:49:26 -070037void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
38 bool pendingConfigChange) {
Ady Abraham8a82ba62020-01-17 12:43:17 -080039 lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
40
41 mLastUpdatedTime = std::max(lastPresentTime, now);
42
Ady Abraham32efd542020-05-19 17:49:26 -070043 FrameTimeData frameTime = {.presetTime = lastPresentTime,
44 .queueTime = mLastUpdatedTime,
45 .pendingConfigChange = pendingConfigChange};
Ady Abraham8a82ba62020-01-17 12:43:17 -080046
47 mFrameTimes.push_back(frameTime);
48 if (mFrameTimes.size() > HISTORY_SIZE) {
49 mFrameTimes.pop_front();
50 }
51}
52
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000053bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
54 return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
55 mFrameTimeValidSince.time_since_epoch())
56 .count();
57}
Ady Abraham1adbb722020-05-15 11:51:48 -070058
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000059bool LayerInfoV2::isFrequent(nsecs_t now) const {
60 // Find the first valid frame time
61 auto it = mFrameTimes.begin();
62 for (; it != mFrameTimes.end(); ++it) {
63 if (isFrameTimeValid(*it)) {
64 break;
Ady Abrahamaf6d8a42020-05-27 19:56:15 +000065 }
66 }
Ady Abraham4ccdcb42020-02-11 17:34:34 -080067
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000068 // If we know nothing about this layer we consider it as frequent as it might be the start
69 // of an animation.
70 if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
71 return true;
72 }
73
74 // Find the first active frame
75 for (; it != mFrameTimes.end(); ++it) {
76 if (it->queueTime >= getActiveLayerThreshold(now)) {
77 break;
78 }
79 }
80
81 const auto numFrames = std::distance(it, mFrameTimes.end());
82 if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
83 return false;
84 }
85
86 // Layer is considered frequent if the average frame rate is higher than the threshold
87 const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
88 return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
Ady Abraham8a82ba62020-01-17 12:43:17 -080089}
90
91bool LayerInfoV2::hasEnoughDataForHeuristic() const {
92 // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
Ady Abrahama61edcb2020-01-30 18:32:03 -080093 if (mFrameTimes.size() < 2) {
94 return false;
95 }
96
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000097 if (!isFrameTimeValid(mFrameTimes.front())) {
98 return false;
99 }
100
Ady Abraham8a82ba62020-01-17 12:43:17 -0800101 if (mFrameTimes.size() < HISTORY_SIZE &&
102 mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
103 return false;
104 }
105
106 return true;
107}
108
Ady Abraham32efd542020-05-19 17:49:26 -0700109std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800110 nsecs_t totalPresentTimeDeltas = 0;
Ady Abrahamc9664832020-05-12 14:16:56 -0700111 nsecs_t totalQueueTimeDeltas = 0;
Ady Abraham32efd542020-05-19 17:49:26 -0700112 bool missingPresentTime = false;
113 int numFrames = 0;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800114 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abraham32efd542020-05-19 17:49:26 -0700115 // Ignore frames captured during a config change
116 if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
117 continue;
118 }
119
Ady Abrahamc9664832020-05-12 14:16:56 -0700120 totalQueueTimeDeltas +=
121 std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
Ady Abraham32efd542020-05-19 17:49:26 -0700122 numFrames++;
Ady Abrahamc9664832020-05-12 14:16:56 -0700123
Ady Abraham8a82ba62020-01-17 12:43:17 -0800124 if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
Ady Abrahamc9664832020-05-12 14:16:56 -0700125 missingPresentTime = true;
126 continue;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800127 }
128
129 totalPresentTimeDeltas +=
130 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
131 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700132
Ady Abrahamc9664832020-05-12 14:16:56 -0700133 // Calculate the average frame time based on presentation timestamps. If those
134 // doesn't exist, we look at the time the buffer was queued only. We can do that only if
135 // we calculated a refresh rate based on presentation timestamps in the past. The reason
136 // we look at the queue time is to handle cases where hwui attaches presentation timestamps
137 // when implementing render ahead for specific refresh rates. When hwui no longer provides
138 // presentation timestamps we look at the queue time to see if the current refresh rate still
139 // matches the content.
Ady Abraham32efd542020-05-19 17:49:26 -0700140 const auto averageFrameTime =
Ady Abrahamc9664832020-05-12 14:16:56 -0700141 static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
Ady Abraham32efd542020-05-19 17:49:26 -0700142 numFrames;
143 return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
144}
Ady Abraham8a82ba62020-01-17 12:43:17 -0800145
Ady Abraham32efd542020-05-19 17:49:26 -0700146bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800147 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abraham32efd542020-05-19 17:49:26 -0700148 // Ignore frames captured during a config change
149 if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
150 continue;
151 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700152 const auto presentTimeDeltas = [&] {
153 const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
154 : (it + 1)->presetTime - it->presetTime;
155 return std::max(delta, mHighRefreshRatePeriod);
156 }();
157
Ady Abraham5f489bd2020-05-12 21:22:02 +0000158 if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
Ady Abraham32efd542020-05-19 17:49:26 -0700159 return false;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800160 }
161 }
162
Ady Abraham32efd542020-05-19 17:49:26 -0700163 return true;
164}
165
166std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
167 static constexpr float MARGIN = 1.0f; // 1Hz
168
169 if (!hasEnoughDataForHeuristic()) {
170 ALOGV("Not enough data");
171 return std::nullopt;
172 }
173
174 const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
175
176 // If there are no presentation timestamps provided we can't calculate the refresh rate
177 if (missingPresentTime && mLastReportedRefreshRate == 0) {
178 return std::nullopt;
179 }
180
181 if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
182 return std::nullopt;
183 }
184
Ady Abraham8a82ba62020-01-17 12:43:17 -0800185 const auto refreshRate = 1e9f / averageFrameTime;
186 if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
187 mLastReportedRefreshRate = refreshRate;
188 }
189
190 ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
191 return mLastReportedRefreshRate;
192}
193
194std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
195 if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700196 ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
Ady Abraham8a82ba62020-01-17 12:43:17 -0800197 return {mLayerVote.type, mLayerVote.fps};
198 }
199
200 if (!isFrequent(now)) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700201 ALOGV("%s is infrequent", mName.c_str());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800202 return {LayerHistory::LayerVoteType::Min, 0};
203 }
204
205 auto refreshRate = calculateRefreshRateIfPossible();
206 if (refreshRate.has_value()) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700207 ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800208 return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
209 }
210
Ady Abrahama6b676e2020-05-27 14:29:09 -0700211 ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800212 return {LayerHistory::LayerVoteType::Max, 0};
213}
214
215} // namespace android::scheduler