blob: 82da00785a19d189ba6901b9694e4a0a9d436eb0 [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,
Ady Abraham5def7332020-05-29 16:13:47 -070038 LayerUpdateType updateType, 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);
Ady Abraham5def7332020-05-29 16:13:47 -070042 switch (updateType) {
43 case LayerUpdateType::AnimationTX:
44 mLastAnimationTime = std::max(lastPresentTime, now);
45 break;
46 case LayerUpdateType::SetFrameRate:
47 case LayerUpdateType::Buffer:
48 FrameTimeData frameTime = {.presetTime = lastPresentTime,
49 .queueTime = mLastUpdatedTime,
50 .pendingConfigChange = pendingConfigChange};
51 mFrameTimes.push_back(frameTime);
52 if (mFrameTimes.size() > HISTORY_SIZE) {
53 mFrameTimes.pop_front();
54 }
55 break;
Ady Abraham8a82ba62020-01-17 12:43:17 -080056 }
57}
58
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000059bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
60 return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
61 mFrameTimeValidSince.time_since_epoch())
62 .count();
63}
Ady Abraham1adbb722020-05-15 11:51:48 -070064
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000065bool LayerInfoV2::isFrequent(nsecs_t now) const {
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000066 // If we know nothing about this layer we consider it as frequent as it might be the start
67 // of an animation.
Ady Abraham983e5682020-05-28 16:49:18 -070068 if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000069 return true;
70 }
71
72 // Find the first active frame
Ady Abraham983e5682020-05-28 16:49:18 -070073 auto it = mFrameTimes.begin();
Ady Abrahamdfb63ba2020-05-27 20:05:05 +000074 for (; it != mFrameTimes.end(); ++it) {
75 if (it->queueTime >= getActiveLayerThreshold(now)) {
76 break;
77 }
78 }
79
80 const auto numFrames = std::distance(it, mFrameTimes.end());
81 if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
82 return false;
83 }
84
85 // Layer is considered frequent if the average frame rate is higher than the threshold
86 const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
87 return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
Ady Abraham8a82ba62020-01-17 12:43:17 -080088}
89
Ady Abraham5def7332020-05-29 16:13:47 -070090bool LayerInfoV2::isAnimating(nsecs_t now) const {
91 return mLastAnimationTime >= getActiveLayerThreshold(now);
92}
93
Ady Abraham8a82ba62020-01-17 12:43:17 -080094bool LayerInfoV2::hasEnoughDataForHeuristic() const {
95 // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
Ady Abrahama61edcb2020-01-30 18:32:03 -080096 if (mFrameTimes.size() < 2) {
97 return false;
98 }
99
Ady Abrahamdfb63ba2020-05-27 20:05:05 +0000100 if (!isFrameTimeValid(mFrameTimes.front())) {
101 return false;
102 }
103
Ady Abraham8a82ba62020-01-17 12:43:17 -0800104 if (mFrameTimes.size() < HISTORY_SIZE &&
105 mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
106 return false;
107 }
108
109 return true;
110}
111
Ady Abraham32efd542020-05-19 17:49:26 -0700112std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800113 nsecs_t totalPresentTimeDeltas = 0;
Ady Abrahamc9664832020-05-12 14:16:56 -0700114 nsecs_t totalQueueTimeDeltas = 0;
Ady Abraham32efd542020-05-19 17:49:26 -0700115 bool missingPresentTime = false;
116 int numFrames = 0;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800117 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abraham32efd542020-05-19 17:49:26 -0700118 // Ignore frames captured during a config change
119 if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
120 continue;
121 }
122
Ady Abrahamc9664832020-05-12 14:16:56 -0700123 totalQueueTimeDeltas +=
124 std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
Ady Abraham32efd542020-05-19 17:49:26 -0700125 numFrames++;
Ady Abrahamc9664832020-05-12 14:16:56 -0700126
Ady Abraham8a82ba62020-01-17 12:43:17 -0800127 if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
Ady Abrahamc9664832020-05-12 14:16:56 -0700128 missingPresentTime = true;
129 continue;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800130 }
131
132 totalPresentTimeDeltas +=
133 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
134 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700135
Ady Abrahamc9664832020-05-12 14:16:56 -0700136 // Calculate the average frame time based on presentation timestamps. If those
137 // doesn't exist, we look at the time the buffer was queued only. We can do that only if
138 // we calculated a refresh rate based on presentation timestamps in the past. The reason
139 // we look at the queue time is to handle cases where hwui attaches presentation timestamps
140 // when implementing render ahead for specific refresh rates. When hwui no longer provides
141 // presentation timestamps we look at the queue time to see if the current refresh rate still
142 // matches the content.
Ady Abraham32efd542020-05-19 17:49:26 -0700143 const auto averageFrameTime =
Ady Abrahamc9664832020-05-12 14:16:56 -0700144 static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
Ady Abraham32efd542020-05-19 17:49:26 -0700145 numFrames;
146 return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
147}
Ady Abraham8a82ba62020-01-17 12:43:17 -0800148
Ady Abraham32efd542020-05-19 17:49:26 -0700149bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800150 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abraham32efd542020-05-19 17:49:26 -0700151 // Ignore frames captured during a config change
152 if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
153 continue;
154 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700155 const auto presentTimeDeltas = [&] {
156 const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
157 : (it + 1)->presetTime - it->presetTime;
158 return std::max(delta, mHighRefreshRatePeriod);
159 }();
160
Ady Abraham5f489bd2020-05-12 21:22:02 +0000161 if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
Ady Abraham32efd542020-05-19 17:49:26 -0700162 return false;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800163 }
164 }
165
Ady Abraham32efd542020-05-19 17:49:26 -0700166 return true;
167}
168
169std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
170 static constexpr float MARGIN = 1.0f; // 1Hz
171
172 if (!hasEnoughDataForHeuristic()) {
173 ALOGV("Not enough data");
174 return std::nullopt;
175 }
176
177 const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
178
179 // If there are no presentation timestamps provided we can't calculate the refresh rate
180 if (missingPresentTime && mLastReportedRefreshRate == 0) {
181 return std::nullopt;
182 }
183
184 if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
185 return std::nullopt;
186 }
187
Ady Abraham8a82ba62020-01-17 12:43:17 -0800188 const auto refreshRate = 1e9f / averageFrameTime;
189 if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
190 mLastReportedRefreshRate = refreshRate;
191 }
192
193 ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
194 return mLastReportedRefreshRate;
195}
196
197std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
198 if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700199 ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
Ady Abraham8a82ba62020-01-17 12:43:17 -0800200 return {mLayerVote.type, mLayerVote.fps};
201 }
202
Ady Abraham5def7332020-05-29 16:13:47 -0700203 if (isAnimating(now)) {
204 ALOGV("%s is animating", mName.c_str());
205 return {LayerHistory::LayerVoteType::Max, 0};
206 }
207
Ady Abraham8a82ba62020-01-17 12:43:17 -0800208 if (!isFrequent(now)) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700209 ALOGV("%s is infrequent", mName.c_str());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800210 return {LayerHistory::LayerVoteType::Min, 0};
211 }
212
213 auto refreshRate = calculateRefreshRateIfPossible();
214 if (refreshRate.has_value()) {
Ady Abrahama6b676e2020-05-27 14:29:09 -0700215 ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800216 return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
217 }
218
Ady Abrahama6b676e2020-05-27 14:29:09 -0700219 ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
Ady Abraham8a82ba62020-01-17 12:43:17 -0800220 return {LayerHistory::LayerVoteType::Max, 0};
221}
222
223} // namespace android::scheduler