blob: f4bbc376e0dbbdc41db80012baec61cbe1050fb3 [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 Abrahamb1b9d412020-06-01 19:53:52 -070030const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
31
Ady Abrahama6b676e2020-05-27 14:29:09 -070032LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
33 LayerHistory::LayerVoteType defaultVote)
34 : mName(name),
35 mHighRefreshRatePeriod(highRefreshRatePeriod),
Ady Abraham8a82ba62020-01-17 12:43:17 -080036 mDefaultVote(defaultVote),
37 mLayerVote({defaultVote, 0.0f}) {}
38
Ady Abraham32efd542020-05-19 17:49:26 -070039void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
Ady Abraham5def7332020-05-29 16:13:47 -070040 LayerUpdateType updateType, bool pendingConfigChange) {
Ady Abraham8a82ba62020-01-17 12:43:17 -080041 lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
42
43 mLastUpdatedTime = std::max(lastPresentTime, now);
Ady Abraham5def7332020-05-29 16:13:47 -070044 switch (updateType) {
45 case LayerUpdateType::AnimationTX:
46 mLastAnimationTime = std::max(lastPresentTime, now);
47 break;
48 case LayerUpdateType::SetFrameRate:
49 case LayerUpdateType::Buffer:
50 FrameTimeData frameTime = {.presetTime = lastPresentTime,
51 .queueTime = mLastUpdatedTime,
52 .pendingConfigChange = pendingConfigChange};
53 mFrameTimes.push_back(frameTime);
54 if (mFrameTimes.size() > HISTORY_SIZE) {
55 mFrameTimes.pop_front();
56 }
57 break;
Ady Abraham8a82ba62020-01-17 12:43:17 -080058 }
59}
60
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000061bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
62 return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
63 mFrameTimeValidSince.time_since_epoch())
64 .count();
65}
Ady Abraham1adbb722020-05-15 11:51:48 -070066
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000067bool LayerInfoV2::isFrequent(nsecs_t now) const {
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.
Ady Abraham983e5682020-05-28 16:49:18 -070070 if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000071 return true;
72 }
73
74 // Find the first active frame
Ady Abraham983e5682020-05-28 16:49:18 -070075 auto it = mFrameTimes.begin();
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000076 for (; it != mFrameTimes.end(); ++it) {
77 if (it->queueTime >= getActiveLayerThreshold(now)) {
78 break;
79 }
80 }
81
82 const auto numFrames = std::distance(it, mFrameTimes.end());
83 if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
84 return false;
85 }
86
87 // Layer is considered frequent if the average frame rate is higher than the threshold
88 const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
89 return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
Ady Abraham8a82ba62020-01-17 12:43:17 -080090}
91
Ady Abraham5def7332020-05-29 16:13:47 -070092bool LayerInfoV2::isAnimating(nsecs_t now) const {
93 return mLastAnimationTime >= getActiveLayerThreshold(now);
94}
95
Ady Abraham8a82ba62020-01-17 12:43:17 -080096bool LayerInfoV2::hasEnoughDataForHeuristic() const {
97 // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
Ady Abrahama61edcb2020-01-30 18:32:03 -080098 if (mFrameTimes.size() < 2) {
99 return false;
100 }
101
Ady Abrahamdfb63ba2020-05-27 20:05:05 +0000102 if (!isFrameTimeValid(mFrameTimes.front())) {
103 return false;
104 }
105
Ady Abraham8a82ba62020-01-17 12:43:17 -0800106 if (mFrameTimes.size() < HISTORY_SIZE &&
107 mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
108 return false;
109 }
110
111 return true;
112}
113
Ady Abraham32efd542020-05-19 17:49:26 -0700114std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800115 nsecs_t totalPresentTimeDeltas = 0;
Ady Abrahamc9664832020-05-12 14:16:56 -0700116 nsecs_t totalQueueTimeDeltas = 0;
Ady Abraham32efd542020-05-19 17:49:26 -0700117 bool missingPresentTime = false;
118 int numFrames = 0;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800119 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abraham32efd542020-05-19 17:49:26 -0700120 // Ignore frames captured during a config change
121 if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
122 continue;
123 }
124
Ady Abrahamc9664832020-05-12 14:16:56 -0700125 totalQueueTimeDeltas +=
126 std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
Ady Abraham32efd542020-05-19 17:49:26 -0700127 numFrames++;
Ady Abrahamc9664832020-05-12 14:16:56 -0700128
Ady Abraham8a82ba62020-01-17 12:43:17 -0800129 if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
Ady Abrahamc9664832020-05-12 14:16:56 -0700130 missingPresentTime = true;
131 continue;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800132 }
133
134 totalPresentTimeDeltas +=
135 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
136 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700137
Ady Abrahamc9664832020-05-12 14:16:56 -0700138 // Calculate the average frame time based on presentation timestamps. If those
139 // doesn't exist, we look at the time the buffer was queued only. We can do that only if
140 // we calculated a refresh rate based on presentation timestamps in the past. The reason
141 // we look at the queue time is to handle cases where hwui attaches presentation timestamps
142 // when implementing render ahead for specific refresh rates. When hwui no longer provides
143 // presentation timestamps we look at the queue time to see if the current refresh rate still
144 // matches the content.
Ady Abraham32efd542020-05-19 17:49:26 -0700145 const auto averageFrameTime =
Ady Abrahamc9664832020-05-12 14:16:56 -0700146 static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
Ady Abraham32efd542020-05-19 17:49:26 -0700147 numFrames;
148 return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
149}
Ady Abraham8a82ba62020-01-17 12:43:17 -0800150
Ady Abraham32efd542020-05-19 17:49:26 -0700151bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800152 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abraham32efd542020-05-19 17:49:26 -0700153 // Ignore frames captured during a config change
154 if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
155 continue;
156 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700157 const auto presentTimeDeltas = [&] {
158 const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
159 : (it + 1)->presetTime - it->presetTime;
160 return std::max(delta, mHighRefreshRatePeriod);
161 }();
162
Ady Abraham5f489bd2020-05-12 21:22:02 +0000163 if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
Ady Abraham32efd542020-05-19 17:49:26 -0700164 return false;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800165 }
166 }
167
Ady Abraham32efd542020-05-19 17:49:26 -0700168 return true;
169}
170
171std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
172 static constexpr float MARGIN = 1.0f; // 1Hz
Ady Abraham32efd542020-05-19 17:49:26 -0700173 if (!hasEnoughDataForHeuristic()) {
174 ALOGV("Not enough data");
175 return std::nullopt;
176 }
177
178 const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
179
180 // If there are no presentation timestamps provided we can't calculate the refresh rate
Ady Abrahamb1b9d412020-06-01 19:53:52 -0700181 if (missingPresentTime && mLastRefreshRate.reported == 0) {
Ady Abraham32efd542020-05-19 17:49:26 -0700182 return std::nullopt;
183 }
184
185 if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
186 return std::nullopt;
187 }
188
Ady Abraham8a82ba62020-01-17 12:43:17 -0800189 const auto refreshRate = 1e9f / averageFrameTime;
Ady Abrahamb1b9d412020-06-01 19:53:52 -0700190 const auto knownRefreshRate = sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
191 if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
192 mLastRefreshRate.reported != knownRefreshRate) {
193 mLastRefreshRate.calculated = refreshRate;
194 mLastRefreshRate.reported = knownRefreshRate;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800195 }
196
Ady Abrahamb1b9d412020-06-01 19:53:52 -0700197 ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(), refreshRate,
198 mLastRefreshRate.reported);
199 return mLastRefreshRate.reported;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800200}
201
202std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
203 if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700204 ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
Ady Abraham8a82ba62020-01-17 12:43:17 -0800205 return {mLayerVote.type, mLayerVote.fps};
206 }
207
Ady Abraham5def7332020-05-29 16:13:47 -0700208 if (isAnimating(now)) {
209 ALOGV("%s is animating", mName.c_str());
210 return {LayerHistory::LayerVoteType::Max, 0};
211 }
212
Ady Abraham8a82ba62020-01-17 12:43:17 -0800213 if (!isFrequent(now)) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700214 ALOGV("%s is infrequent", mName.c_str());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800215 return {LayerHistory::LayerVoteType::Min, 0};
216 }
217
218 auto refreshRate = calculateRefreshRateIfPossible();
219 if (refreshRate.has_value()) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700220 ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800221 return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
222 }
223
Ady Abrahama6b676e2020-05-27 14:29:09 -0700224 ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800225 return {LayerHistory::LayerVoteType::Max, 0};
226}
227
228} // namespace android::scheduler