blob: b7d0bdd101822e1fc7a4444cf0ae2952511d86e7 [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
Ady Abrahama61edcb2020-01-30 18:32:03 -080048bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
49 return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
50 mFrameTimeValidSince.time_since_epoch())
51 .count();
52}
53
Ady Abraham8a82ba62020-01-17 12:43:17 -080054bool LayerInfoV2::isFrequent(nsecs_t now) const {
Ady Abraham4ccdcb42020-02-11 17:34:34 -080055 // Find the first valid frame time
56 auto it = mFrameTimes.begin();
57 for (; it != mFrameTimes.end(); ++it) {
58 if (isFrameTimeValid(*it)) {
59 break;
60 }
61 }
62
Ady Abrahama61edcb2020-01-30 18:32:03 -080063 // If we know nothing about this layer we consider it as frequent as it might be the start
64 // of an animation.
Ady Abraham4ccdcb42020-02-11 17:34:34 -080065 if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
Ady Abrahama61edcb2020-01-30 18:32:03 -080066 return true;
Ady Abraham8a82ba62020-01-17 12:43:17 -080067 }
68
Ady Abraham4ccdcb42020-02-11 17:34:34 -080069 // Find the first active frame
70 for (; it != mFrameTimes.end(); ++it) {
71 if (it->queueTime >= getActiveLayerThreshold(now)) {
72 break;
73 }
Ady Abrahama61edcb2020-01-30 18:32:03 -080074 }
75
Ady Abraham0d736662020-02-28 18:22:30 -080076 const auto numFrames = std::distance(it, mFrameTimes.end());
77 if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
Ady Abraham4ccdcb42020-02-11 17:34:34 -080078 return false;
79 }
80
81 // Layer is considered frequent if the average frame rate is higher than the threshold
82 const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
Ady Abraham0d736662020-02-28 18:22:30 -080083 return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
Ady Abraham8a82ba62020-01-17 12:43:17 -080084}
85
86bool LayerInfoV2::hasEnoughDataForHeuristic() const {
87 // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
Ady Abrahama61edcb2020-01-30 18:32:03 -080088 if (mFrameTimes.size() < 2) {
89 return false;
90 }
91
92 if (!isFrameTimeValid(mFrameTimes.front())) {
93 return false;
94 }
95
Ady Abraham8a82ba62020-01-17 12:43:17 -080096 if (mFrameTimes.size() < HISTORY_SIZE &&
97 mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
98 return false;
99 }
100
101 return true;
102}
103
104std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
105 static constexpr float MARGIN = 1.0f; // 1Hz
106
107 if (!hasEnoughDataForHeuristic()) {
108 ALOGV("Not enough data");
109 return std::nullopt;
110 }
111
112 // Calculate the refresh rate by finding the average delta between frames
113 nsecs_t totalPresentTimeDeltas = 0;
Ady Abrahamc9664832020-05-12 14:16:56 -0700114 nsecs_t totalQueueTimeDeltas = 0;
115 auto missingPresentTime = false;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800116 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abrahamc9664832020-05-12 14:16:56 -0700117 totalQueueTimeDeltas +=
118 std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
119
Ady Abraham8a82ba62020-01-17 12:43:17 -0800120 if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
Ady Abrahamc9664832020-05-12 14:16:56 -0700121 missingPresentTime = true;
122 continue;
Ady Abraham8a82ba62020-01-17 12:43:17 -0800123 }
124
125 totalPresentTimeDeltas +=
126 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
127 }
Ady Abrahamc9664832020-05-12 14:16:56 -0700128
129 // If there are no presentation timestamps provided we can't calculate the refresh rate
130 if (missingPresentTime && mLastReportedRefreshRate == 0) {
131 return std::nullopt;
132 }
133
134 // Calculate the average frame time based on presentation timestamps. If those
135 // doesn't exist, we look at the time the buffer was queued only. We can do that only if
136 // we calculated a refresh rate based on presentation timestamps in the past. The reason
137 // we look at the queue time is to handle cases where hwui attaches presentation timestamps
138 // when implementing render ahead for specific refresh rates. When hwui no longer provides
139 // presentation timestamps we look at the queue time to see if the current refresh rate still
140 // matches the content.
Ady Abrahamb28c5cc2020-05-12 21:22:32 +0000141 const float averageFrameTime =
Ady Abrahamc9664832020-05-12 14:16:56 -0700142 static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
143 (mFrameTimes.size() - 1);
Ady Abraham8a82ba62020-01-17 12:43:17 -0800144
145 // 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 -0800146 // are evenly distributed and we don't calculate the average across some burst of frames.
Ady Abraham8a82ba62020-01-17 12:43:17 -0800147 for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
Ady Abrahamc9664832020-05-12 14:16:56 -0700148 const auto presentTimeDeltas = [&] {
149 const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
150 : (it + 1)->presetTime - it->presetTime;
151 return std::max(delta, mHighRefreshRatePeriod);
152 }();
153
Ady Abraham5f489bd2020-05-12 21:22:02 +0000154 if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
Ady Abraham8a82ba62020-01-17 12:43:17 -0800155 return std::nullopt;
156 }
157 }
158
159 const auto refreshRate = 1e9f / averageFrameTime;
160 if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
161 mLastReportedRefreshRate = refreshRate;
162 }
163
164 ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
165 return mLastReportedRefreshRate;
166}
167
168std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
169 if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
170 return {mLayerVote.type, mLayerVote.fps};
171 }
172
173 if (!isFrequent(now)) {
174 return {LayerHistory::LayerVoteType::Min, 0};
175 }
176
177 auto refreshRate = calculateRefreshRateIfPossible();
178 if (refreshRate.has_value()) {
179 return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
180 }
181
182 return {LayerHistory::LayerVoteType::Max, 0};
183}
184
185} // namespace android::scheduler