• 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 #include <gui/TraceUtils.h>
33 
34 #undef LOG_TAG
35 #define LOG_TAG "LayerInfo"
36 
37 namespace android::scheduler {
38 
39 bool LayerInfo::sTraceEnabled = false;
40 
LayerInfo(const std::string & name,uid_t ownerUid,LayerHistory::LayerVoteType defaultVote)41 LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
42                      LayerHistory::LayerVoteType defaultVote)
43       : mName(name),
44         mOwnerUid(ownerUid),
45         mDefaultVote(defaultVote),
46         mLayerVote({defaultVote, Fps()}),
47         mLayerProps(std::make_unique<LayerProps>()),
48         mRefreshRateHistory(name) {
49     ;
50 }
51 
setLastPresentTime(nsecs_t lastPresentTime,nsecs_t now,LayerUpdateType updateType,bool pendingModeChange,const LayerProps & props)52 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
53                                    bool pendingModeChange, const LayerProps& props) {
54     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
55 
56     mLastUpdatedTime = std::max(lastPresentTime, now);
57     *mLayerProps = props;
58     switch (updateType) {
59         case LayerUpdateType::AnimationTX:
60             mLastAnimationTime = std::max(lastPresentTime, now);
61             break;
62         case LayerUpdateType::SetFrameRate:
63         case LayerUpdateType::Buffer:
64             FrameTimeData frameTime = {.presentTime = lastPresentTime,
65                                        .queueTime = mLastUpdatedTime,
66                                        .pendingModeChange = pendingModeChange,
67                                        .isSmallDirty = props.isSmallDirty};
68             mFrameTimes.push_back(frameTime);
69             if (mFrameTimes.size() > HISTORY_SIZE) {
70                 mFrameTimes.pop_front();
71             }
72             break;
73     }
74 }
75 
isFrameTimeValid(const FrameTimeData & frameTime) const76 bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
77     return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
78                                           mFrameTimeValidSince.time_since_epoch())
79                                           .count();
80 }
81 
isFrequent(nsecs_t now) const82 LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const {
83     // If we know nothing about this layer (e.g. after touch event),
84     // we consider it as frequent as it might be the start of an animation.
85     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
86         return {/* isFrequent */ true, /* clearHistory */ false, /* isConclusive */ true};
87     }
88 
89     // Non-active layers are also infrequent
90     if (mLastUpdatedTime < getActiveLayerThreshold(now)) {
91         return {/* isFrequent */ false, /* clearHistory */ false, /* isConclusive */ true};
92     }
93 
94     // We check whether we can classify this layer as frequent or infrequent:
95     //  - frequent: a layer posted kFrequentLayerWindowSize within
96     //              kMaxPeriodForFrequentLayerNs of each other.
97     // -  infrequent: a layer posted kFrequentLayerWindowSize with longer
98     //                gaps than kFrequentLayerWindowSize.
99     // If we can't determine the layer classification yet, we return the last
100     // classification.
101     bool isFrequent = true;
102     bool isInfrequent = true;
103     int32_t smallDirtyCount = 0;
104     const auto n = mFrameTimes.size() - 1;
105     for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) {
106         if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime <
107             kMaxPeriodForFrequentLayerNs.count()) {
108             isInfrequent = false;
109             if (mFrameTimes[n - i].presentTime == 0 && mFrameTimes[n - i].isSmallDirty) {
110                 smallDirtyCount++;
111             }
112         } else {
113             isFrequent = false;
114         }
115     }
116 
117     // Vote the small dirty when a layer contains at least HISTORY_SIZE of small dirty updates.
118     bool isSmallDirty = false;
119     if (smallDirtyCount >= kNumSmallDirtyThreshold) {
120         if (mLastSmallDirtyCount >= HISTORY_SIZE) {
121             isSmallDirty = true;
122         } else {
123             mLastSmallDirtyCount++;
124         }
125     } else {
126         mLastSmallDirtyCount = 0;
127     }
128 
129     if (isFrequent || isInfrequent) {
130         // If the layer was previously inconclusive, we clear
131         // the history as indeterminate layers changed to frequent,
132         // and we should not look at the stale data.
133         return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true,
134                 isSmallDirty};
135     }
136 
137     // If we can't determine whether the layer is frequent or not, we return
138     // the last known classification and mark the layer frequency as inconclusive.
139     isFrequent = !mLastRefreshRate.infrequent;
140 
141     // If the layer was previously tagged as animating, we clear
142     // the history as it is likely the layer just changed its behavior,
143     // and we should not look at stale data.
144     return {isFrequent, isFrequent && mLastRefreshRate.animating, /* isConclusive */ false};
145 }
146 
getFps(nsecs_t now) const147 Fps LayerInfo::getFps(nsecs_t now) const {
148     // Find the first active frame
149     auto it = mFrameTimes.begin();
150     for (; it != mFrameTimes.end(); ++it) {
151         if (it->queueTime >= getActiveLayerThreshold(now)) {
152             break;
153         }
154     }
155 
156     const auto numFrames = std::distance(it, mFrameTimes.end());
157     if (numFrames < kFrequentLayerWindowSize) {
158         return Fps();
159     }
160 
161     // Layer is considered frequent if the average frame rate is higher than the threshold
162     const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
163     return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
164 }
165 
isAnimating(nsecs_t now) const166 bool LayerInfo::isAnimating(nsecs_t now) const {
167     return mLastAnimationTime >= getActiveLayerThreshold(now);
168 }
169 
hasEnoughDataForHeuristic() const170 bool LayerInfo::hasEnoughDataForHeuristic() const {
171     // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
172     if (mFrameTimes.size() < 2) {
173         ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
174         return false;
175     }
176 
177     if (!isFrameTimeValid(mFrameTimes.front())) {
178         ALOGV("stale frames still captured");
179         return false;
180     }
181 
182     const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
183     if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
184         ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
185               totalDuration / 1e9f);
186         return false;
187     }
188 
189     return true;
190 }
191 
calculateAverageFrameTime() const192 std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
193     // Ignore frames captured during a mode change
194     const bool isDuringModeChange =
195             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
196                         [](const auto& frame) { return frame.pendingModeChange; });
197     if (isDuringModeChange) {
198         return std::nullopt;
199     }
200 
201     const bool isMissingPresentTime =
202             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
203                         [](auto frame) { return frame.presentTime == 0; });
204     if (isMissingPresentTime && !mLastRefreshRate.reported.isValid()) {
205         // If there are no presentation timestamps and we haven't calculated
206         // one in the past then we can't calculate the refresh rate
207         return std::nullopt;
208     }
209 
210     // Calculate the average frame time based on presentation timestamps. If those
211     // doesn't exist, we look at the time the buffer was queued only. We can do that only if
212     // we calculated a refresh rate based on presentation timestamps in the past. The reason
213     // we look at the queue time is to handle cases where hwui attaches presentation timestamps
214     // when implementing render ahead for specific refresh rates. When hwui no longer provides
215     // presentation timestamps we look at the queue time to see if the current refresh rate still
216     // matches the content.
217 
218     auto getFrameTime = isMissingPresentTime ? [](FrameTimeData data) { return data.queueTime; }
219                                              : [](FrameTimeData data) { return data.presentTime; };
220 
221     nsecs_t totalDeltas = 0;
222     int numDeltas = 0;
223     int32_t smallDirtyCount = 0;
224     auto prevFrame = mFrameTimes.begin();
225     for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) {
226         const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame);
227         if (currDelta < kMinPeriodBetweenFrames) {
228             // Skip this frame, but count the delta into the next frame
229             continue;
230         }
231 
232         // If this is a small area update, we don't want to consider it for calculating the average
233         // frame time. Instead, we let the bigger frame updates to drive the calculation.
234         if (it->isSmallDirty && currDelta < kMinPeriodBetweenSmallDirtyFrames) {
235             smallDirtyCount++;
236             continue;
237         }
238 
239         prevFrame = it;
240 
241         if (currDelta > kMaxPeriodBetweenFrames) {
242             // Skip this frame and the current delta.
243             continue;
244         }
245 
246         totalDeltas += currDelta;
247         numDeltas++;
248     }
249 
250     if (smallDirtyCount > 0) {
251         ATRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount);
252     }
253 
254     if (numDeltas == 0) {
255         return std::nullopt;
256     }
257 
258     const auto averageFrameTime = static_cast<double>(totalDeltas) / static_cast<double>(numDeltas);
259     return static_cast<nsecs_t>(averageFrameTime);
260 }
261 
calculateRefreshRateIfPossible(const RefreshRateSelector & selector,nsecs_t now)262 std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector,
263                                                              nsecs_t now) {
264     ATRACE_CALL();
265     static constexpr float MARGIN = 1.0f; // 1Hz
266     if (!hasEnoughDataForHeuristic()) {
267         ALOGV("Not enough data");
268         return std::nullopt;
269     }
270 
271     if (const auto averageFrameTime = calculateAverageFrameTime()) {
272         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
273         const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
274         if (refreshRateConsistent) {
275             const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate);
276             using fps_approx_ops::operator!=;
277 
278             // To avoid oscillation, use the last calculated refresh rate if it is close enough.
279             if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
280                         MARGIN &&
281                 mLastRefreshRate.reported != knownRefreshRate) {
282                 mLastRefreshRate.calculated = refreshRate;
283                 mLastRefreshRate.reported = knownRefreshRate;
284             }
285 
286             ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(),
287                   to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
288         } else {
289             ALOGV("%s Not stable (%s) returning last known frame rate %s", mName.c_str(),
290                   to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
291         }
292     }
293 
294     return mLastRefreshRate.reported.isValid() ? std::make_optional(mLastRefreshRate.reported)
295                                                : std::nullopt;
296 }
297 
getRefreshRateVote(const RefreshRateSelector & selector,nsecs_t now)298 LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
299                                                    nsecs_t now) {
300     ATRACE_CALL();
301     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
302         ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
303         return mLayerVote;
304     }
305 
306     if (isAnimating(now)) {
307         ATRACE_FORMAT_INSTANT("animating");
308         ALOGV("%s is animating", mName.c_str());
309         mLastRefreshRate.animating = true;
310         return {LayerHistory::LayerVoteType::Max, Fps()};
311     }
312 
313     const LayerInfo::Frequent frequent = isFrequent(now);
314     mIsFrequencyConclusive = frequent.isConclusive;
315     if (!frequent.isFrequent) {
316         ATRACE_FORMAT_INSTANT("infrequent");
317         ALOGV("%s is infrequent", mName.c_str());
318         mLastRefreshRate.infrequent = true;
319         mLastSmallDirtyCount = 0;
320         // Infrequent layers vote for minimal refresh rate for
321         // battery saving purposes and also to prevent b/135718869.
322         return {LayerHistory::LayerVoteType::Min, Fps()};
323     }
324 
325     if (frequent.clearHistory) {
326         clearHistory(now);
327     }
328 
329     // Return no vote if the recent frames are small dirty.
330     if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) {
331         ATRACE_FORMAT_INSTANT("NoVote (small dirty)");
332         ALOGV("%s is small dirty", mName.c_str());
333         return {LayerHistory::LayerVoteType::NoVote, Fps()};
334     }
335 
336     auto refreshRate = calculateRefreshRateIfPossible(selector, now);
337     if (refreshRate.has_value()) {
338         ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
339         return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
340     }
341 
342     ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
343     return {LayerHistory::LayerVoteType::Max, Fps()};
344 }
345 
getTraceTag(LayerHistory::LayerVoteType type) const346 const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
347     if (mTraceTags.count(type) == 0) {
348         auto tag = "LFPS " + mName + " " + ftl::enum_string(type);
349         mTraceTags.emplace(type, std::move(tag));
350     }
351 
352     return mTraceTags.at(type).c_str();
353 }
354 
getSetFrameRateVote() const355 LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const {
356     return mLayerProps->setFrameRateVote;
357 }
358 
isVisible() const359 bool LayerInfo::isVisible() const {
360     return mLayerProps->visible;
361 }
362 
getFrameRateSelectionPriority() const363 int32_t LayerInfo::getFrameRateSelectionPriority() const {
364     return mLayerProps->frameRateSelectionPriority;
365 }
366 
getBounds() const367 FloatRect LayerInfo::getBounds() const {
368     return mLayerProps->bounds;
369 }
370 
getTransform() const371 ui::Transform LayerInfo::getTransform() const {
372     return mLayerProps->transform;
373 }
374 
375 LayerInfo::RefreshRateHistory::HeuristicTraceTagData
makeHeuristicTraceTagData() const376 LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
377     const std::string prefix = "LFPS ";
378     const std::string suffix = "Heuristic ";
379     return {.min = prefix + mName + suffix + "min",
380             .max = prefix + mName + suffix + "max",
381             .consistent = prefix + mName + suffix + "consistent",
382             .average = prefix + mName + suffix + "average"};
383 }
384 
clear()385 void LayerInfo::RefreshRateHistory::clear() {
386     mRefreshRates.clear();
387 }
388 
add(Fps refreshRate,nsecs_t now)389 bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) {
390     mRefreshRates.push_back({refreshRate, now});
391     while (mRefreshRates.size() >= HISTORY_SIZE ||
392            now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
393         mRefreshRates.pop_front();
394     }
395 
396     if (CC_UNLIKELY(sTraceEnabled)) {
397         if (!mHeuristicTraceTagData.has_value()) {
398             mHeuristicTraceTagData = makeHeuristicTraceTagData();
399         }
400 
401         ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
402     }
403 
404     return isConsistent();
405 }
406 
isConsistent() const407 bool LayerInfo::RefreshRateHistory::isConsistent() const {
408     if (mRefreshRates.empty()) return true;
409 
410     const auto [min, max] =
411             std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
412                                 [](const auto& lhs, const auto& rhs) {
413                                     return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
414                                 });
415 
416     const bool consistent =
417             max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
418 
419     if (CC_UNLIKELY(sTraceEnabled)) {
420         if (!mHeuristicTraceTagData.has_value()) {
421             mHeuristicTraceTagData = makeHeuristicTraceTagData();
422         }
423 
424         ATRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue());
425         ATRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue());
426         ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
427     }
428 
429     return consistent;
430 }
431 
432 } // namespace android::scheduler
433 
434 // TODO(b/129481165): remove the #pragma below and fix conversion issues
435 #pragma clang diagnostic pop // ignored "-Wextra"
436