• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 // TODO(b/129481165): remove the #pragma below and fix conversion issues
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wextra"
20 
21 // #define LOG_NDEBUG 0
22 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
23 
24 #include "LayerInfo.h"
25 
26 #include <algorithm>
27 #include <utility>
28 
29 #include <cutils/compiler.h>
30 #include <cutils/trace.h>
31 #include <ftl/enum.h>
32 
33 #undef LOG_TAG
34 #define LOG_TAG "LayerInfo"
35 
36 namespace android::scheduler {
37 
38 bool LayerInfo::sTraceEnabled = false;
39 
LayerInfo(const std::string & name,uid_t ownerUid,LayerHistory::LayerVoteType defaultVote)40 LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
41                      LayerHistory::LayerVoteType defaultVote)
42       : mName(name),
43         mOwnerUid(ownerUid),
44         mDefaultVote(defaultVote),
45         mLayerVote({defaultVote, Fps()}),
46         mRefreshRateHistory(name) {}
47 
setLastPresentTime(nsecs_t lastPresentTime,nsecs_t now,LayerUpdateType updateType,bool pendingModeChange,LayerProps props)48 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
49                                    bool pendingModeChange, LayerProps props) {
50     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
51 
52     mLastUpdatedTime = std::max(lastPresentTime, now);
53     mLayerProps = props;
54     switch (updateType) {
55         case LayerUpdateType::AnimationTX:
56             mLastAnimationTime = std::max(lastPresentTime, now);
57             break;
58         case LayerUpdateType::SetFrameRate:
59         case LayerUpdateType::Buffer:
60             FrameTimeData frameTime = {.presentTime = lastPresentTime,
61                                        .queueTime = mLastUpdatedTime,
62                                        .pendingModeChange = pendingModeChange};
63             mFrameTimes.push_back(frameTime);
64             if (mFrameTimes.size() > HISTORY_SIZE) {
65                 mFrameTimes.pop_front();
66             }
67             break;
68     }
69 }
70 
isFrameTimeValid(const FrameTimeData & frameTime) const71 bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
72     return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
73                                           mFrameTimeValidSince.time_since_epoch())
74                                           .count();
75 }
76 
isFrequent(nsecs_t now) const77 bool LayerInfo::isFrequent(nsecs_t now) const {
78     using fps_approx_ops::operator>=;
79     // If we know nothing about this layer we consider it as frequent as it might be the start
80     // of an animation.
81     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
82         return true;
83     }
84     return getFps(now) >= kMinFpsForFrequentLayer;
85 }
86 
getFps(nsecs_t now) const87 Fps LayerInfo::getFps(nsecs_t now) const {
88     // Find the first active frame
89     auto it = mFrameTimes.begin();
90     for (; it != mFrameTimes.end(); ++it) {
91         if (it->queueTime >= getActiveLayerThreshold(now)) {
92             break;
93         }
94     }
95 
96     const auto numFrames = std::distance(it, mFrameTimes.end());
97     if (numFrames < kFrequentLayerWindowSize) {
98         return Fps();
99     }
100 
101     // Layer is considered frequent if the average frame rate is higher than the threshold
102     const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
103     return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
104 }
105 
isAnimating(nsecs_t now) const106 bool LayerInfo::isAnimating(nsecs_t now) const {
107     return mLastAnimationTime >= getActiveLayerThreshold(now);
108 }
109 
hasEnoughDataForHeuristic() const110 bool LayerInfo::hasEnoughDataForHeuristic() const {
111     // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
112     if (mFrameTimes.size() < 2) {
113         ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
114         return false;
115     }
116 
117     if (!isFrameTimeValid(mFrameTimes.front())) {
118         ALOGV("stale frames still captured");
119         return false;
120     }
121 
122     const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
123     if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
124         ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
125               totalDuration / 1e9f);
126         return false;
127     }
128 
129     return true;
130 }
131 
calculateAverageFrameTime() const132 std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
133     // Ignore frames captured during a mode change
134     const bool isDuringModeChange =
135             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
136                         [](const auto& frame) { return frame.pendingModeChange; });
137     if (isDuringModeChange) {
138         return std::nullopt;
139     }
140 
141     const bool isMissingPresentTime =
142             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
143                         [](auto frame) { return frame.presentTime == 0; });
144     if (isMissingPresentTime && !mLastRefreshRate.reported.isValid()) {
145         // If there are no presentation timestamps and we haven't calculated
146         // one in the past then we can't calculate the refresh rate
147         return std::nullopt;
148     }
149 
150     // Calculate the average frame time based on presentation timestamps. If those
151     // doesn't exist, we look at the time the buffer was queued only. We can do that only if
152     // we calculated a refresh rate based on presentation timestamps in the past. The reason
153     // we look at the queue time is to handle cases where hwui attaches presentation timestamps
154     // when implementing render ahead for specific refresh rates. When hwui no longer provides
155     // presentation timestamps we look at the queue time to see if the current refresh rate still
156     // matches the content.
157 
158     auto getFrameTime = isMissingPresentTime ? [](FrameTimeData data) { return data.queueTime; }
159                                              : [](FrameTimeData data) { return data.presentTime; };
160 
161     nsecs_t totalDeltas = 0;
162     int numDeltas = 0;
163     auto prevFrame = mFrameTimes.begin();
164     for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) {
165         const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame);
166         if (currDelta < kMinPeriodBetweenFrames) {
167             // Skip this frame, but count the delta into the next frame
168             continue;
169         }
170 
171         prevFrame = it;
172 
173         if (currDelta > kMaxPeriodBetweenFrames) {
174             // Skip this frame and the current delta.
175             continue;
176         }
177 
178         totalDeltas += currDelta;
179         numDeltas++;
180     }
181 
182     if (numDeltas == 0) {
183         return std::nullopt;
184     }
185 
186     const auto averageFrameTime = static_cast<double>(totalDeltas) / static_cast<double>(numDeltas);
187     return static_cast<nsecs_t>(averageFrameTime);
188 }
189 
calculateRefreshRateIfPossible(const RefreshRateConfigs & refreshRateConfigs,nsecs_t now)190 std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(
191         const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) {
192     static constexpr float MARGIN = 1.0f; // 1Hz
193     if (!hasEnoughDataForHeuristic()) {
194         ALOGV("Not enough data");
195         return std::nullopt;
196     }
197 
198     if (const auto averageFrameTime = calculateAverageFrameTime()) {
199         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
200         const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
201         if (refreshRateConsistent) {
202             const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
203             using fps_approx_ops::operator!=;
204 
205             // To avoid oscillation, use the last calculated refresh rate if it is close enough.
206             if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
207                         MARGIN &&
208                 mLastRefreshRate.reported != knownRefreshRate) {
209                 mLastRefreshRate.calculated = refreshRate;
210                 mLastRefreshRate.reported = knownRefreshRate;
211             }
212 
213             ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(),
214                   to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
215         } else {
216             ALOGV("%s Not stable (%s) returning last known frame rate %s", mName.c_str(),
217                   to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
218         }
219     }
220 
221     return mLastRefreshRate.reported.isValid() ? std::make_optional(mLastRefreshRate.reported)
222                                                : std::nullopt;
223 }
224 
getRefreshRateVote(const RefreshRateConfigs & refreshRateConfigs,nsecs_t now)225 LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs,
226                                                    nsecs_t now) {
227     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
228         ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
229         return mLayerVote;
230     }
231 
232     if (isAnimating(now)) {
233         ALOGV("%s is animating", mName.c_str());
234         mLastRefreshRate.animatingOrInfrequent = true;
235         return {LayerHistory::LayerVoteType::Max, Fps()};
236     }
237 
238     if (!isFrequent(now)) {
239         ALOGV("%s is infrequent", mName.c_str());
240         mLastRefreshRate.animatingOrInfrequent = true;
241         // Infrequent layers vote for mininal refresh rate for
242         // battery saving purposes and also to prevent b/135718869.
243         return {LayerHistory::LayerVoteType::Min, Fps()};
244     }
245 
246     // If the layer was previously tagged as animating or infrequent, we clear
247     // the history as it is likely the layer just changed its behavior
248     // and we should not look at stale data
249     if (mLastRefreshRate.animatingOrInfrequent) {
250         clearHistory(now);
251     }
252 
253     auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now);
254     if (refreshRate.has_value()) {
255         ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
256         return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
257     }
258 
259     ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
260     return {LayerHistory::LayerVoteType::Max, Fps()};
261 }
262 
getTraceTag(LayerHistory::LayerVoteType type) const263 const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
264     if (mTraceTags.count(type) == 0) {
265         auto tag = "LFPS " + mName + " " + ftl::enum_string(type);
266         mTraceTags.emplace(type, std::move(tag));
267     }
268 
269     return mTraceTags.at(type).c_str();
270 }
271 
272 LayerInfo::RefreshRateHistory::HeuristicTraceTagData
makeHeuristicTraceTagData() const273 LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
274     const std::string prefix = "LFPS ";
275     const std::string suffix = "Heuristic ";
276     return {.min = prefix + mName + suffix + "min",
277             .max = prefix + mName + suffix + "max",
278             .consistent = prefix + mName + suffix + "consistent",
279             .average = prefix + mName + suffix + "average"};
280 }
281 
clear()282 void LayerInfo::RefreshRateHistory::clear() {
283     mRefreshRates.clear();
284 }
285 
add(Fps refreshRate,nsecs_t now)286 bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) {
287     mRefreshRates.push_back({refreshRate, now});
288     while (mRefreshRates.size() >= HISTORY_SIZE ||
289            now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
290         mRefreshRates.pop_front();
291     }
292 
293     if (CC_UNLIKELY(sTraceEnabled)) {
294         if (!mHeuristicTraceTagData.has_value()) {
295             mHeuristicTraceTagData = makeHeuristicTraceTagData();
296         }
297 
298         ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
299     }
300 
301     return isConsistent();
302 }
303 
isConsistent() const304 bool LayerInfo::RefreshRateHistory::isConsistent() const {
305     if (mRefreshRates.empty()) return true;
306 
307     const auto [min, max] =
308             std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
309                                 [](const auto& lhs, const auto& rhs) {
310                                     return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
311                                 });
312 
313     const bool consistent =
314             max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
315 
316     if (CC_UNLIKELY(sTraceEnabled)) {
317         if (!mHeuristicTraceTagData.has_value()) {
318             mHeuristicTraceTagData = makeHeuristicTraceTagData();
319         }
320 
321         ATRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue());
322         ATRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue());
323         ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
324     }
325 
326     return consistent;
327 }
328 
329 } // namespace android::scheduler
330 
331 // TODO(b/129481165): remove the #pragma below and fix conversion issues
332 #pragma clang diagnostic pop // ignored "-Wextra"
333