xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/Scheduler/LayerInfo.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2020 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker // TODO(b/129481165): remove the #pragma below and fix conversion issues
18*38e8c45fSAndroid Build Coastguard Worker #pragma clang diagnostic push
19*38e8c45fSAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wextra"
20*38e8c45fSAndroid Build Coastguard Worker 
21*38e8c45fSAndroid Build Coastguard Worker // #define LOG_NDEBUG 0
22*38e8c45fSAndroid Build Coastguard Worker #define ATRACE_TAG ATRACE_TAG_GRAPHICS
23*38e8c45fSAndroid Build Coastguard Worker 
24*38e8c45fSAndroid Build Coastguard Worker #include "LayerInfo.h"
25*38e8c45fSAndroid Build Coastguard Worker 
26*38e8c45fSAndroid Build Coastguard Worker #include <algorithm>
27*38e8c45fSAndroid Build Coastguard Worker #include <utility>
28*38e8c45fSAndroid Build Coastguard Worker 
29*38e8c45fSAndroid Build Coastguard Worker #include <android/native_window.h>
30*38e8c45fSAndroid Build Coastguard Worker #include <common/trace.h>
31*38e8c45fSAndroid Build Coastguard Worker #include <cutils/compiler.h>
32*38e8c45fSAndroid Build Coastguard Worker #include <cutils/trace.h>
33*38e8c45fSAndroid Build Coastguard Worker #include <ftl/enum.h>
34*38e8c45fSAndroid Build Coastguard Worker #include <system/window.h>
35*38e8c45fSAndroid Build Coastguard Worker 
36*38e8c45fSAndroid Build Coastguard Worker #undef LOG_TAG
37*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "LayerInfo"
38*38e8c45fSAndroid Build Coastguard Worker 
39*38e8c45fSAndroid Build Coastguard Worker namespace android::scheduler {
40*38e8c45fSAndroid Build Coastguard Worker 
41*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::sTraceEnabled = false;
42*38e8c45fSAndroid Build Coastguard Worker 
LayerInfo(const std::string & name,uid_t ownerUid,LayerHistory::LayerVoteType defaultVote)43*38e8c45fSAndroid Build Coastguard Worker LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
44*38e8c45fSAndroid Build Coastguard Worker                      LayerHistory::LayerVoteType defaultVote)
45*38e8c45fSAndroid Build Coastguard Worker       : mName(name),
46*38e8c45fSAndroid Build Coastguard Worker         mOwnerUid(ownerUid),
47*38e8c45fSAndroid Build Coastguard Worker         mDefaultVote(defaultVote),
48*38e8c45fSAndroid Build Coastguard Worker         mLayerVote({defaultVote, Fps()}),
49*38e8c45fSAndroid Build Coastguard Worker         mLayerProps(std::make_unique<LayerProps>()),
50*38e8c45fSAndroid Build Coastguard Worker         mRefreshRateHistory(name) {
51*38e8c45fSAndroid Build Coastguard Worker     ;
52*38e8c45fSAndroid Build Coastguard Worker }
53*38e8c45fSAndroid Build Coastguard Worker 
setLastPresentTime(nsecs_t lastPresentTime,nsecs_t now,LayerUpdateType updateType,bool pendingModeChange,const LayerProps & props)54*38e8c45fSAndroid Build Coastguard Worker void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
55*38e8c45fSAndroid Build Coastguard Worker                                    bool pendingModeChange, const LayerProps& props) {
56*38e8c45fSAndroid Build Coastguard Worker     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
57*38e8c45fSAndroid Build Coastguard Worker 
58*38e8c45fSAndroid Build Coastguard Worker     *mLayerProps = props;
59*38e8c45fSAndroid Build Coastguard Worker     switch (updateType) {
60*38e8c45fSAndroid Build Coastguard Worker         case LayerUpdateType::AnimationTX:
61*38e8c45fSAndroid Build Coastguard Worker             mLastUpdatedTime = std::max(lastPresentTime, now);
62*38e8c45fSAndroid Build Coastguard Worker             mLastAnimationTime = std::max(lastPresentTime, now);
63*38e8c45fSAndroid Build Coastguard Worker             break;
64*38e8c45fSAndroid Build Coastguard Worker         case LayerUpdateType::SetFrameRate:
65*38e8c45fSAndroid Build Coastguard Worker             if (FlagManager::getInstance().vrr_config()) {
66*38e8c45fSAndroid Build Coastguard Worker                 break;
67*38e8c45fSAndroid Build Coastguard Worker             }
68*38e8c45fSAndroid Build Coastguard Worker             FALLTHROUGH_INTENDED;
69*38e8c45fSAndroid Build Coastguard Worker         case LayerUpdateType::Buffer:
70*38e8c45fSAndroid Build Coastguard Worker             mLastUpdatedTime = std::max(lastPresentTime, now);
71*38e8c45fSAndroid Build Coastguard Worker             FrameTimeData frameTime = {.presentTime = lastPresentTime,
72*38e8c45fSAndroid Build Coastguard Worker                                        .queueTime = mLastUpdatedTime,
73*38e8c45fSAndroid Build Coastguard Worker                                        .pendingModeChange = pendingModeChange,
74*38e8c45fSAndroid Build Coastguard Worker                                        .isSmallDirty = props.isSmallDirty};
75*38e8c45fSAndroid Build Coastguard Worker             mFrameTimes.push_back(frameTime);
76*38e8c45fSAndroid Build Coastguard Worker             if (mFrameTimes.size() > HISTORY_SIZE) {
77*38e8c45fSAndroid Build Coastguard Worker                 mFrameTimes.pop_front();
78*38e8c45fSAndroid Build Coastguard Worker             }
79*38e8c45fSAndroid Build Coastguard Worker             break;
80*38e8c45fSAndroid Build Coastguard Worker     }
81*38e8c45fSAndroid Build Coastguard Worker }
82*38e8c45fSAndroid Build Coastguard Worker 
setProperties(const android::scheduler::LayerProps & properties)83*38e8c45fSAndroid Build Coastguard Worker void LayerInfo::setProperties(const android::scheduler::LayerProps& properties) {
84*38e8c45fSAndroid Build Coastguard Worker     *mLayerProps = properties;
85*38e8c45fSAndroid Build Coastguard Worker }
86*38e8c45fSAndroid Build Coastguard Worker 
isFrameTimeValid(const FrameTimeData & frameTime) const87*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
88*38e8c45fSAndroid Build Coastguard Worker     return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
89*38e8c45fSAndroid Build Coastguard Worker                                           mFrameTimeValidSince.time_since_epoch())
90*38e8c45fSAndroid Build Coastguard Worker                                           .count();
91*38e8c45fSAndroid Build Coastguard Worker }
92*38e8c45fSAndroid Build Coastguard Worker 
isFrequent(nsecs_t now) const93*38e8c45fSAndroid Build Coastguard Worker LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const {
94*38e8c45fSAndroid Build Coastguard Worker     // If we know nothing about this layer (e.g. after touch event),
95*38e8c45fSAndroid Build Coastguard Worker     // we consider it as frequent as it might be the start of an animation.
96*38e8c45fSAndroid Build Coastguard Worker     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
97*38e8c45fSAndroid Build Coastguard Worker         return {/* isFrequent */ true, /* clearHistory */ false, /* isConclusive */ true};
98*38e8c45fSAndroid Build Coastguard Worker     }
99*38e8c45fSAndroid Build Coastguard Worker 
100*38e8c45fSAndroid Build Coastguard Worker     // Non-active layers are also infrequent
101*38e8c45fSAndroid Build Coastguard Worker     if (mLastUpdatedTime < getActiveLayerThreshold(now)) {
102*38e8c45fSAndroid Build Coastguard Worker         return {/* isFrequent */ false, /* clearHistory */ false, /* isConclusive */ true};
103*38e8c45fSAndroid Build Coastguard Worker     }
104*38e8c45fSAndroid Build Coastguard Worker 
105*38e8c45fSAndroid Build Coastguard Worker     // We check whether we can classify this layer as frequent or infrequent:
106*38e8c45fSAndroid Build Coastguard Worker     //  - frequent: a layer posted kFrequentLayerWindowSize within
107*38e8c45fSAndroid Build Coastguard Worker     //              kMaxPeriodForFrequentLayerNs of each other.
108*38e8c45fSAndroid Build Coastguard Worker     // -  infrequent: a layer posted kFrequentLayerWindowSize with longer
109*38e8c45fSAndroid Build Coastguard Worker     //                gaps than kFrequentLayerWindowSize.
110*38e8c45fSAndroid Build Coastguard Worker     // If we can't determine the layer classification yet, we return the last
111*38e8c45fSAndroid Build Coastguard Worker     // classification.
112*38e8c45fSAndroid Build Coastguard Worker     bool isFrequent = true;
113*38e8c45fSAndroid Build Coastguard Worker     bool isInfrequent = true;
114*38e8c45fSAndroid Build Coastguard Worker     int32_t smallDirtyCount = 0;
115*38e8c45fSAndroid Build Coastguard Worker     const auto n = mFrameTimes.size() - 1;
116*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) {
117*38e8c45fSAndroid Build Coastguard Worker         if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime <
118*38e8c45fSAndroid Build Coastguard Worker             kMaxPeriodForFrequentLayerNs.count()) {
119*38e8c45fSAndroid Build Coastguard Worker             isInfrequent = false;
120*38e8c45fSAndroid Build Coastguard Worker             if (mFrameTimes[n - i].presentTime == 0 && mFrameTimes[n - i].isSmallDirty) {
121*38e8c45fSAndroid Build Coastguard Worker                 smallDirtyCount++;
122*38e8c45fSAndroid Build Coastguard Worker             }
123*38e8c45fSAndroid Build Coastguard Worker         } else {
124*38e8c45fSAndroid Build Coastguard Worker             isFrequent = false;
125*38e8c45fSAndroid Build Coastguard Worker         }
126*38e8c45fSAndroid Build Coastguard Worker     }
127*38e8c45fSAndroid Build Coastguard Worker 
128*38e8c45fSAndroid Build Coastguard Worker     // Vote the small dirty when a layer contains at least HISTORY_SIZE of small dirty updates.
129*38e8c45fSAndroid Build Coastguard Worker     bool isSmallDirty = false;
130*38e8c45fSAndroid Build Coastguard Worker     if (smallDirtyCount >= kNumSmallDirtyThreshold) {
131*38e8c45fSAndroid Build Coastguard Worker         if (mLastSmallDirtyCount >= HISTORY_SIZE) {
132*38e8c45fSAndroid Build Coastguard Worker             isSmallDirty = true;
133*38e8c45fSAndroid Build Coastguard Worker         } else {
134*38e8c45fSAndroid Build Coastguard Worker             mLastSmallDirtyCount++;
135*38e8c45fSAndroid Build Coastguard Worker         }
136*38e8c45fSAndroid Build Coastguard Worker     } else {
137*38e8c45fSAndroid Build Coastguard Worker         mLastSmallDirtyCount = 0;
138*38e8c45fSAndroid Build Coastguard Worker     }
139*38e8c45fSAndroid Build Coastguard Worker 
140*38e8c45fSAndroid Build Coastguard Worker     if (isFrequent || isInfrequent) {
141*38e8c45fSAndroid Build Coastguard Worker         // If the layer was previously inconclusive, we clear
142*38e8c45fSAndroid Build Coastguard Worker         // the history as indeterminate layers changed to frequent,
143*38e8c45fSAndroid Build Coastguard Worker         // and we should not look at the stale data.
144*38e8c45fSAndroid Build Coastguard Worker         return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true,
145*38e8c45fSAndroid Build Coastguard Worker                 isSmallDirty};
146*38e8c45fSAndroid Build Coastguard Worker     }
147*38e8c45fSAndroid Build Coastguard Worker 
148*38e8c45fSAndroid Build Coastguard Worker     // If we can't determine whether the layer is frequent or not, we return
149*38e8c45fSAndroid Build Coastguard Worker     // the last known classification and mark the layer frequency as inconclusive.
150*38e8c45fSAndroid Build Coastguard Worker     isFrequent = !mLastRefreshRate.infrequent;
151*38e8c45fSAndroid Build Coastguard Worker 
152*38e8c45fSAndroid Build Coastguard Worker     // If the layer was previously tagged as animating, we clear
153*38e8c45fSAndroid Build Coastguard Worker     // the history as it is likely the layer just changed its behavior,
154*38e8c45fSAndroid Build Coastguard Worker     // and we should not look at stale data.
155*38e8c45fSAndroid Build Coastguard Worker     return {isFrequent, isFrequent && mLastRefreshRate.animating, /* isConclusive */ false};
156*38e8c45fSAndroid Build Coastguard Worker }
157*38e8c45fSAndroid Build Coastguard Worker 
getFps(nsecs_t now) const158*38e8c45fSAndroid Build Coastguard Worker Fps LayerInfo::getFps(nsecs_t now) const {
159*38e8c45fSAndroid Build Coastguard Worker     // Find the first active frame
160*38e8c45fSAndroid Build Coastguard Worker     auto it = mFrameTimes.begin();
161*38e8c45fSAndroid Build Coastguard Worker     for (; it != mFrameTimes.end(); ++it) {
162*38e8c45fSAndroid Build Coastguard Worker         if (it->queueTime >= getActiveLayerThreshold(now)) {
163*38e8c45fSAndroid Build Coastguard Worker             break;
164*38e8c45fSAndroid Build Coastguard Worker         }
165*38e8c45fSAndroid Build Coastguard Worker     }
166*38e8c45fSAndroid Build Coastguard Worker 
167*38e8c45fSAndroid Build Coastguard Worker     const auto numFrames = std::distance(it, mFrameTimes.end());
168*38e8c45fSAndroid Build Coastguard Worker     if (numFrames < kFrequentLayerWindowSize) {
169*38e8c45fSAndroid Build Coastguard Worker         return Fps();
170*38e8c45fSAndroid Build Coastguard Worker     }
171*38e8c45fSAndroid Build Coastguard Worker 
172*38e8c45fSAndroid Build Coastguard Worker     // Layer is considered frequent if the average frame rate is higher than the threshold
173*38e8c45fSAndroid Build Coastguard Worker     const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
174*38e8c45fSAndroid Build Coastguard Worker     return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
175*38e8c45fSAndroid Build Coastguard Worker }
176*38e8c45fSAndroid Build Coastguard Worker 
isAnimating(nsecs_t now) const177*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::isAnimating(nsecs_t now) const {
178*38e8c45fSAndroid Build Coastguard Worker     return mLastAnimationTime >= getActiveLayerThreshold(now);
179*38e8c45fSAndroid Build Coastguard Worker }
180*38e8c45fSAndroid Build Coastguard Worker 
hasEnoughDataForHeuristic() const181*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::hasEnoughDataForHeuristic() const {
182*38e8c45fSAndroid Build Coastguard Worker     // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
183*38e8c45fSAndroid Build Coastguard Worker     if (mFrameTimes.size() < 2) {
184*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s fewer than 2 frames recorded: %zu", mName.c_str(), mFrameTimes.size());
185*38e8c45fSAndroid Build Coastguard Worker         return false;
186*38e8c45fSAndroid Build Coastguard Worker     }
187*38e8c45fSAndroid Build Coastguard Worker 
188*38e8c45fSAndroid Build Coastguard Worker     if (!isFrameTimeValid(mFrameTimes.front())) {
189*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s stale frames still captured", mName.c_str());
190*38e8c45fSAndroid Build Coastguard Worker         return false;
191*38e8c45fSAndroid Build Coastguard Worker     }
192*38e8c45fSAndroid Build Coastguard Worker 
193*38e8c45fSAndroid Build Coastguard Worker     const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
194*38e8c45fSAndroid Build Coastguard Worker     if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
195*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s not enough frames captured: %zu | %.2f seconds", mName.c_str(),
196*38e8c45fSAndroid Build Coastguard Worker               mFrameTimes.size(), totalDuration / 1e9f);
197*38e8c45fSAndroid Build Coastguard Worker         return false;
198*38e8c45fSAndroid Build Coastguard Worker     }
199*38e8c45fSAndroid Build Coastguard Worker 
200*38e8c45fSAndroid Build Coastguard Worker     return true;
201*38e8c45fSAndroid Build Coastguard Worker }
202*38e8c45fSAndroid Build Coastguard Worker 
calculateAverageFrameTime() const203*38e8c45fSAndroid Build Coastguard Worker std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
204*38e8c45fSAndroid Build Coastguard Worker     // Ignore frames captured during a mode change
205*38e8c45fSAndroid Build Coastguard Worker     const bool isDuringModeChange =
206*38e8c45fSAndroid Build Coastguard Worker             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
207*38e8c45fSAndroid Build Coastguard Worker                         [](const auto& frame) { return frame.pendingModeChange; });
208*38e8c45fSAndroid Build Coastguard Worker     if (isDuringModeChange) {
209*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
210*38e8c45fSAndroid Build Coastguard Worker     }
211*38e8c45fSAndroid Build Coastguard Worker 
212*38e8c45fSAndroid Build Coastguard Worker     const bool isMissingPresentTime =
213*38e8c45fSAndroid Build Coastguard Worker             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
214*38e8c45fSAndroid Build Coastguard Worker                         [](auto frame) { return frame.presentTime == 0; });
215*38e8c45fSAndroid Build Coastguard Worker     if (isMissingPresentTime && !mLastRefreshRate.reported.isValid()) {
216*38e8c45fSAndroid Build Coastguard Worker         // If there are no presentation timestamps and we haven't calculated
217*38e8c45fSAndroid Build Coastguard Worker         // one in the past then we can't calculate the refresh rate
218*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
219*38e8c45fSAndroid Build Coastguard Worker     }
220*38e8c45fSAndroid Build Coastguard Worker 
221*38e8c45fSAndroid Build Coastguard Worker     // Calculate the average frame time based on presentation timestamps. If those
222*38e8c45fSAndroid Build Coastguard Worker     // doesn't exist, we look at the time the buffer was queued only. We can do that only if
223*38e8c45fSAndroid Build Coastguard Worker     // we calculated a refresh rate based on presentation timestamps in the past. The reason
224*38e8c45fSAndroid Build Coastguard Worker     // we look at the queue time is to handle cases where hwui attaches presentation timestamps
225*38e8c45fSAndroid Build Coastguard Worker     // when implementing render ahead for specific refresh rates. When hwui no longer provides
226*38e8c45fSAndroid Build Coastguard Worker     // presentation timestamps we look at the queue time to see if the current refresh rate still
227*38e8c45fSAndroid Build Coastguard Worker     // matches the content.
228*38e8c45fSAndroid Build Coastguard Worker 
229*38e8c45fSAndroid Build Coastguard Worker     auto getFrameTime = isMissingPresentTime ? [](FrameTimeData data) { return data.queueTime; }
230*38e8c45fSAndroid Build Coastguard Worker                                              : [](FrameTimeData data) { return data.presentTime; };
231*38e8c45fSAndroid Build Coastguard Worker 
232*38e8c45fSAndroid Build Coastguard Worker     nsecs_t totalDeltas = 0;
233*38e8c45fSAndroid Build Coastguard Worker     int numDeltas = 0;
234*38e8c45fSAndroid Build Coastguard Worker     int32_t smallDirtyCount = 0;
235*38e8c45fSAndroid Build Coastguard Worker     auto prevFrame = mFrameTimes.begin();
236*38e8c45fSAndroid Build Coastguard Worker     for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) {
237*38e8c45fSAndroid Build Coastguard Worker         const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame);
238*38e8c45fSAndroid Build Coastguard Worker         if (currDelta < kMinPeriodBetweenFrames) {
239*38e8c45fSAndroid Build Coastguard Worker             // Skip this frame, but count the delta into the next frame
240*38e8c45fSAndroid Build Coastguard Worker             continue;
241*38e8c45fSAndroid Build Coastguard Worker         }
242*38e8c45fSAndroid Build Coastguard Worker 
243*38e8c45fSAndroid Build Coastguard Worker         // If this is a small area update, we don't want to consider it for calculating the average
244*38e8c45fSAndroid Build Coastguard Worker         // frame time. Instead, we let the bigger frame updates to drive the calculation.
245*38e8c45fSAndroid Build Coastguard Worker         if (it->isSmallDirty && currDelta < kMinPeriodBetweenSmallDirtyFrames) {
246*38e8c45fSAndroid Build Coastguard Worker             smallDirtyCount++;
247*38e8c45fSAndroid Build Coastguard Worker             continue;
248*38e8c45fSAndroid Build Coastguard Worker         }
249*38e8c45fSAndroid Build Coastguard Worker 
250*38e8c45fSAndroid Build Coastguard Worker         prevFrame = it;
251*38e8c45fSAndroid Build Coastguard Worker 
252*38e8c45fSAndroid Build Coastguard Worker         if (currDelta > kMaxPeriodBetweenFrames) {
253*38e8c45fSAndroid Build Coastguard Worker             // Skip this frame and the current delta.
254*38e8c45fSAndroid Build Coastguard Worker             continue;
255*38e8c45fSAndroid Build Coastguard Worker         }
256*38e8c45fSAndroid Build Coastguard Worker 
257*38e8c45fSAndroid Build Coastguard Worker         totalDeltas += currDelta;
258*38e8c45fSAndroid Build Coastguard Worker         numDeltas++;
259*38e8c45fSAndroid Build Coastguard Worker     }
260*38e8c45fSAndroid Build Coastguard Worker 
261*38e8c45fSAndroid Build Coastguard Worker     if (smallDirtyCount > 0) {
262*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount);
263*38e8c45fSAndroid Build Coastguard Worker     }
264*38e8c45fSAndroid Build Coastguard Worker 
265*38e8c45fSAndroid Build Coastguard Worker     if (numDeltas == 0) {
266*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
267*38e8c45fSAndroid Build Coastguard Worker     }
268*38e8c45fSAndroid Build Coastguard Worker 
269*38e8c45fSAndroid Build Coastguard Worker     const auto averageFrameTime = static_cast<double>(totalDeltas) / static_cast<double>(numDeltas);
270*38e8c45fSAndroid Build Coastguard Worker     return static_cast<nsecs_t>(averageFrameTime);
271*38e8c45fSAndroid Build Coastguard Worker }
272*38e8c45fSAndroid Build Coastguard Worker 
calculateRefreshRateIfPossible(const RefreshRateSelector & selector,nsecs_t now)273*38e8c45fSAndroid Build Coastguard Worker std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector,
274*38e8c45fSAndroid Build Coastguard Worker                                                              nsecs_t now) {
275*38e8c45fSAndroid Build Coastguard Worker     SFTRACE_CALL();
276*38e8c45fSAndroid Build Coastguard Worker     static constexpr float MARGIN = 1.0f; // 1Hz
277*38e8c45fSAndroid Build Coastguard Worker     if (!hasEnoughDataForHeuristic()) {
278*38e8c45fSAndroid Build Coastguard Worker         ALOGV("Not enough data");
279*38e8c45fSAndroid Build Coastguard Worker         return std::nullopt;
280*38e8c45fSAndroid Build Coastguard Worker     }
281*38e8c45fSAndroid Build Coastguard Worker 
282*38e8c45fSAndroid Build Coastguard Worker     if (const auto averageFrameTime = calculateAverageFrameTime()) {
283*38e8c45fSAndroid Build Coastguard Worker         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
284*38e8c45fSAndroid Build Coastguard Worker         const auto closestKnownRefreshRate = mRefreshRateHistory.add(refreshRate, now, selector);
285*38e8c45fSAndroid Build Coastguard Worker         if (closestKnownRefreshRate.isValid()) {
286*38e8c45fSAndroid Build Coastguard Worker             using fps_approx_ops::operator!=;
287*38e8c45fSAndroid Build Coastguard Worker 
288*38e8c45fSAndroid Build Coastguard Worker             // To avoid oscillation, use the last calculated refresh rate if it is close enough.
289*38e8c45fSAndroid Build Coastguard Worker             if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
290*38e8c45fSAndroid Build Coastguard Worker                         MARGIN &&
291*38e8c45fSAndroid Build Coastguard Worker                 mLastRefreshRate.reported != closestKnownRefreshRate) {
292*38e8c45fSAndroid Build Coastguard Worker                 mLastRefreshRate.calculated = refreshRate;
293*38e8c45fSAndroid Build Coastguard Worker                 mLastRefreshRate.reported = closestKnownRefreshRate;
294*38e8c45fSAndroid Build Coastguard Worker             }
295*38e8c45fSAndroid Build Coastguard Worker 
296*38e8c45fSAndroid Build Coastguard Worker             ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(),
297*38e8c45fSAndroid Build Coastguard Worker                   to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
298*38e8c45fSAndroid Build Coastguard Worker         } else {
299*38e8c45fSAndroid Build Coastguard Worker             ALOGV("%s Not stable (%s) returning last known frame rate %s", mName.c_str(),
300*38e8c45fSAndroid Build Coastguard Worker                   to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str());
301*38e8c45fSAndroid Build Coastguard Worker         }
302*38e8c45fSAndroid Build Coastguard Worker     }
303*38e8c45fSAndroid Build Coastguard Worker 
304*38e8c45fSAndroid Build Coastguard Worker     return mLastRefreshRate.reported.isValid() ? std::make_optional(mLastRefreshRate.reported)
305*38e8c45fSAndroid Build Coastguard Worker                                                : std::nullopt;
306*38e8c45fSAndroid Build Coastguard Worker }
307*38e8c45fSAndroid Build Coastguard Worker 
getRefreshRateVote(const RefreshRateSelector & selector,nsecs_t now)308*38e8c45fSAndroid Build Coastguard Worker LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
309*38e8c45fSAndroid Build Coastguard Worker                                                           nsecs_t now) {
310*38e8c45fSAndroid Build Coastguard Worker     SFTRACE_CALL();
311*38e8c45fSAndroid Build Coastguard Worker     LayerInfo::RefreshRateVotes votes;
312*38e8c45fSAndroid Build Coastguard Worker 
313*38e8c45fSAndroid Build Coastguard Worker     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
314*38e8c45fSAndroid Build Coastguard Worker         if (mLayerVote.category != FrameRateCategory::Default) {
315*38e8c45fSAndroid Build Coastguard Worker             const auto voteType = mLayerVote.type == LayerHistory::LayerVoteType::NoVote
316*38e8c45fSAndroid Build Coastguard Worker                     ? LayerHistory::LayerVoteType::NoVote
317*38e8c45fSAndroid Build Coastguard Worker                     : LayerHistory::LayerVoteType::ExplicitCategory;
318*38e8c45fSAndroid Build Coastguard Worker             SFTRACE_FORMAT_INSTANT("Vote %s (category=%s)", ftl::enum_string(voteType).c_str(),
319*38e8c45fSAndroid Build Coastguard Worker                                    ftl::enum_string(mLayerVote.category).c_str());
320*38e8c45fSAndroid Build Coastguard Worker             ALOGV("%s voted %s with category: %s", mName.c_str(),
321*38e8c45fSAndroid Build Coastguard Worker                   ftl::enum_string(voteType).c_str(),
322*38e8c45fSAndroid Build Coastguard Worker                   ftl::enum_string(mLayerVote.category).c_str());
323*38e8c45fSAndroid Build Coastguard Worker             votes.push_back({voteType, Fps(), Seamlessness::Default, mLayerVote.category,
324*38e8c45fSAndroid Build Coastguard Worker                              mLayerVote.categorySmoothSwitchOnly});
325*38e8c45fSAndroid Build Coastguard Worker         }
326*38e8c45fSAndroid Build Coastguard Worker 
327*38e8c45fSAndroid Build Coastguard Worker         if (mLayerVote.fps.isValid() ||
328*38e8c45fSAndroid Build Coastguard Worker             mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) {
329*38e8c45fSAndroid Build Coastguard Worker             SFTRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str());
330*38e8c45fSAndroid Build Coastguard Worker             ALOGV("%s voted %d", mName.c_str(), static_cast<int>(mLayerVote.type));
331*38e8c45fSAndroid Build Coastguard Worker             votes.push_back({mLayerVote.type, mLayerVote.fps, mLayerVote.seamlessness,
332*38e8c45fSAndroid Build Coastguard Worker                              FrameRateCategory::Default, mLayerVote.categorySmoothSwitchOnly});
333*38e8c45fSAndroid Build Coastguard Worker         }
334*38e8c45fSAndroid Build Coastguard Worker 
335*38e8c45fSAndroid Build Coastguard Worker         return votes;
336*38e8c45fSAndroid Build Coastguard Worker     }
337*38e8c45fSAndroid Build Coastguard Worker 
338*38e8c45fSAndroid Build Coastguard Worker     if (isAnimating(now)) {
339*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("animating");
340*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s is animating", mName.c_str());
341*38e8c45fSAndroid Build Coastguard Worker         mLastRefreshRate.animating = true;
342*38e8c45fSAndroid Build Coastguard Worker         votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
343*38e8c45fSAndroid Build Coastguard Worker         return votes;
344*38e8c45fSAndroid Build Coastguard Worker     }
345*38e8c45fSAndroid Build Coastguard Worker 
346*38e8c45fSAndroid Build Coastguard Worker     // Vote for max refresh rate whenever we're front-buffered.
347*38e8c45fSAndroid Build Coastguard Worker     if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) {
348*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("front buffered");
349*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s is front-buffered", mName.c_str());
350*38e8c45fSAndroid Build Coastguard Worker         votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
351*38e8c45fSAndroid Build Coastguard Worker         return votes;
352*38e8c45fSAndroid Build Coastguard Worker     }
353*38e8c45fSAndroid Build Coastguard Worker 
354*38e8c45fSAndroid Build Coastguard Worker     const LayerInfo::Frequent frequent = isFrequent(now);
355*38e8c45fSAndroid Build Coastguard Worker     mIsFrequencyConclusive = frequent.isConclusive;
356*38e8c45fSAndroid Build Coastguard Worker     if (!frequent.isFrequent) {
357*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("infrequent");
358*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s is infrequent", mName.c_str());
359*38e8c45fSAndroid Build Coastguard Worker         mLastRefreshRate.infrequent = true;
360*38e8c45fSAndroid Build Coastguard Worker         mLastSmallDirtyCount = 0;
361*38e8c45fSAndroid Build Coastguard Worker         // Infrequent layers vote for minimal refresh rate for
362*38e8c45fSAndroid Build Coastguard Worker         // battery saving purposes and also to prevent b/135718869.
363*38e8c45fSAndroid Build Coastguard Worker         votes.push_back({LayerHistory::LayerVoteType::Min, Fps()});
364*38e8c45fSAndroid Build Coastguard Worker         return votes;
365*38e8c45fSAndroid Build Coastguard Worker     }
366*38e8c45fSAndroid Build Coastguard Worker 
367*38e8c45fSAndroid Build Coastguard Worker     if (frequent.clearHistory) {
368*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("frequent.clearHistory");
369*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s frequent.clearHistory", mName.c_str());
370*38e8c45fSAndroid Build Coastguard Worker         clearHistory(now);
371*38e8c45fSAndroid Build Coastguard Worker     }
372*38e8c45fSAndroid Build Coastguard Worker 
373*38e8c45fSAndroid Build Coastguard Worker     // Return no vote if the recent frames are small dirty.
374*38e8c45fSAndroid Build Coastguard Worker     if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) {
375*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("NoVote (small dirty)");
376*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s is small dirty", mName.c_str());
377*38e8c45fSAndroid Build Coastguard Worker         votes.push_back({LayerHistory::LayerVoteType::NoVote, Fps()});
378*38e8c45fSAndroid Build Coastguard Worker         return votes;
379*38e8c45fSAndroid Build Coastguard Worker     }
380*38e8c45fSAndroid Build Coastguard Worker 
381*38e8c45fSAndroid Build Coastguard Worker     auto refreshRate = calculateRefreshRateIfPossible(selector, now);
382*38e8c45fSAndroid Build Coastguard Worker     if (refreshRate.has_value()) {
383*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_FORMAT_INSTANT("calculated (%s)", to_string(*refreshRate).c_str());
384*38e8c45fSAndroid Build Coastguard Worker         ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
385*38e8c45fSAndroid Build Coastguard Worker         votes.push_back({LayerHistory::LayerVoteType::Heuristic, refreshRate.value()});
386*38e8c45fSAndroid Build Coastguard Worker         return votes;
387*38e8c45fSAndroid Build Coastguard Worker     }
388*38e8c45fSAndroid Build Coastguard Worker 
389*38e8c45fSAndroid Build Coastguard Worker     SFTRACE_FORMAT_INSTANT("Max (can't resolve refresh rate)");
390*38e8c45fSAndroid Build Coastguard Worker     ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
391*38e8c45fSAndroid Build Coastguard Worker     votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
392*38e8c45fSAndroid Build Coastguard Worker     return votes;
393*38e8c45fSAndroid Build Coastguard Worker }
394*38e8c45fSAndroid Build Coastguard Worker 
getTraceTag(LayerHistory::LayerVoteType type) const395*38e8c45fSAndroid Build Coastguard Worker const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
396*38e8c45fSAndroid Build Coastguard Worker     if (mTraceTags.count(type) == 0) {
397*38e8c45fSAndroid Build Coastguard Worker         auto tag = "LFPS " + mName + " " + ftl::enum_string(type);
398*38e8c45fSAndroid Build Coastguard Worker         mTraceTags.emplace(type, std::move(tag));
399*38e8c45fSAndroid Build Coastguard Worker     }
400*38e8c45fSAndroid Build Coastguard Worker 
401*38e8c45fSAndroid Build Coastguard Worker     return mTraceTags.at(type).c_str();
402*38e8c45fSAndroid Build Coastguard Worker }
403*38e8c45fSAndroid Build Coastguard Worker 
getSetFrameRateVote() const404*38e8c45fSAndroid Build Coastguard Worker LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const {
405*38e8c45fSAndroid Build Coastguard Worker     return mLayerProps->setFrameRateVote;
406*38e8c45fSAndroid Build Coastguard Worker }
407*38e8c45fSAndroid Build Coastguard Worker 
isVisible() const408*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::isVisible() const {
409*38e8c45fSAndroid Build Coastguard Worker     return mLayerProps->visible;
410*38e8c45fSAndroid Build Coastguard Worker }
411*38e8c45fSAndroid Build Coastguard Worker 
getFrameRateSelectionPriority() const412*38e8c45fSAndroid Build Coastguard Worker int32_t LayerInfo::getFrameRateSelectionPriority() const {
413*38e8c45fSAndroid Build Coastguard Worker     return mLayerProps->frameRateSelectionPriority;
414*38e8c45fSAndroid Build Coastguard Worker }
415*38e8c45fSAndroid Build Coastguard Worker 
isFrontBuffered() const416*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::isFrontBuffered() const {
417*38e8c45fSAndroid Build Coastguard Worker     return mLayerProps->isFrontBuffered;
418*38e8c45fSAndroid Build Coastguard Worker }
419*38e8c45fSAndroid Build Coastguard Worker 
getBounds() const420*38e8c45fSAndroid Build Coastguard Worker FloatRect LayerInfo::getBounds() const {
421*38e8c45fSAndroid Build Coastguard Worker     return mLayerProps->bounds;
422*38e8c45fSAndroid Build Coastguard Worker }
423*38e8c45fSAndroid Build Coastguard Worker 
getTransform() const424*38e8c45fSAndroid Build Coastguard Worker ui::Transform LayerInfo::getTransform() const {
425*38e8c45fSAndroid Build Coastguard Worker     return mLayerProps->transform;
426*38e8c45fSAndroid Build Coastguard Worker }
427*38e8c45fSAndroid Build Coastguard Worker 
428*38e8c45fSAndroid Build Coastguard Worker LayerInfo::RefreshRateHistory::HeuristicTraceTagData
makeHeuristicTraceTagData() const429*38e8c45fSAndroid Build Coastguard Worker LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
430*38e8c45fSAndroid Build Coastguard Worker     const std::string prefix = "LFPS ";
431*38e8c45fSAndroid Build Coastguard Worker     const std::string suffix = "Heuristic ";
432*38e8c45fSAndroid Build Coastguard Worker     return {.min = prefix + mName + suffix + "min",
433*38e8c45fSAndroid Build Coastguard Worker             .max = prefix + mName + suffix + "max",
434*38e8c45fSAndroid Build Coastguard Worker             .consistent = prefix + mName + suffix + "consistent",
435*38e8c45fSAndroid Build Coastguard Worker             .average = prefix + mName + suffix + "average"};
436*38e8c45fSAndroid Build Coastguard Worker }
437*38e8c45fSAndroid Build Coastguard Worker 
clear()438*38e8c45fSAndroid Build Coastguard Worker void LayerInfo::RefreshRateHistory::clear() {
439*38e8c45fSAndroid Build Coastguard Worker     mRefreshRates.clear();
440*38e8c45fSAndroid Build Coastguard Worker }
441*38e8c45fSAndroid Build Coastguard Worker 
add(Fps refreshRate,nsecs_t now,const RefreshRateSelector & selector)442*38e8c45fSAndroid Build Coastguard Worker Fps LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now,
443*38e8c45fSAndroid Build Coastguard Worker                                        const RefreshRateSelector& selector) {
444*38e8c45fSAndroid Build Coastguard Worker     mRefreshRates.push_back({refreshRate, now});
445*38e8c45fSAndroid Build Coastguard Worker     while (mRefreshRates.size() >= HISTORY_SIZE ||
446*38e8c45fSAndroid Build Coastguard Worker            now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
447*38e8c45fSAndroid Build Coastguard Worker         mRefreshRates.pop_front();
448*38e8c45fSAndroid Build Coastguard Worker     }
449*38e8c45fSAndroid Build Coastguard Worker 
450*38e8c45fSAndroid Build Coastguard Worker     if (CC_UNLIKELY(sTraceEnabled)) {
451*38e8c45fSAndroid Build Coastguard Worker         if (!mHeuristicTraceTagData.has_value()) {
452*38e8c45fSAndroid Build Coastguard Worker             mHeuristicTraceTagData = makeHeuristicTraceTagData();
453*38e8c45fSAndroid Build Coastguard Worker         }
454*38e8c45fSAndroid Build Coastguard Worker 
455*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
456*38e8c45fSAndroid Build Coastguard Worker     }
457*38e8c45fSAndroid Build Coastguard Worker 
458*38e8c45fSAndroid Build Coastguard Worker     return selectRefreshRate(selector);
459*38e8c45fSAndroid Build Coastguard Worker }
460*38e8c45fSAndroid Build Coastguard Worker 
selectRefreshRate(const RefreshRateSelector & selector) const461*38e8c45fSAndroid Build Coastguard Worker Fps LayerInfo::RefreshRateHistory::selectRefreshRate(const RefreshRateSelector& selector) const {
462*38e8c45fSAndroid Build Coastguard Worker     if (mRefreshRates.empty()) return Fps();
463*38e8c45fSAndroid Build Coastguard Worker 
464*38e8c45fSAndroid Build Coastguard Worker     const auto [min, max] =
465*38e8c45fSAndroid Build Coastguard Worker             std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
466*38e8c45fSAndroid Build Coastguard Worker                                 [](const auto& lhs, const auto& rhs) {
467*38e8c45fSAndroid Build Coastguard Worker                                     return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
468*38e8c45fSAndroid Build Coastguard Worker                                 });
469*38e8c45fSAndroid Build Coastguard Worker 
470*38e8c45fSAndroid Build Coastguard Worker     const auto maxClosestRate = selector.findClosestKnownFrameRate(max->refreshRate);
471*38e8c45fSAndroid Build Coastguard Worker     const bool consistent = [&](Fps maxFps, Fps minFps) {
472*38e8c45fSAndroid Build Coastguard Worker         if (FlagManager::getInstance().use_known_refresh_rate_for_fps_consistency()) {
473*38e8c45fSAndroid Build Coastguard Worker             if (maxFps.getValue() - minFps.getValue() <
474*38e8c45fSAndroid Build Coastguard Worker                 MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE) {
475*38e8c45fSAndroid Build Coastguard Worker                 const auto minClosestRate = selector.findClosestKnownFrameRate(minFps);
476*38e8c45fSAndroid Build Coastguard Worker                 using fps_approx_ops::operator==;
477*38e8c45fSAndroid Build Coastguard Worker                 return maxClosestRate == minClosestRate;
478*38e8c45fSAndroid Build Coastguard Worker             }
479*38e8c45fSAndroid Build Coastguard Worker             return false;
480*38e8c45fSAndroid Build Coastguard Worker         }
481*38e8c45fSAndroid Build Coastguard Worker         return maxFps.getValue() - minFps.getValue() < MARGIN_CONSISTENT_FPS;
482*38e8c45fSAndroid Build Coastguard Worker     }(max->refreshRate, min->refreshRate);
483*38e8c45fSAndroid Build Coastguard Worker 
484*38e8c45fSAndroid Build Coastguard Worker     if (CC_UNLIKELY(sTraceEnabled)) {
485*38e8c45fSAndroid Build Coastguard Worker         if (!mHeuristicTraceTagData.has_value()) {
486*38e8c45fSAndroid Build Coastguard Worker             mHeuristicTraceTagData = makeHeuristicTraceTagData();
487*38e8c45fSAndroid Build Coastguard Worker         }
488*38e8c45fSAndroid Build Coastguard Worker 
489*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue());
490*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue());
491*38e8c45fSAndroid Build Coastguard Worker         SFTRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
492*38e8c45fSAndroid Build Coastguard Worker     }
493*38e8c45fSAndroid Build Coastguard Worker 
494*38e8c45fSAndroid Build Coastguard Worker     return consistent ? maxClosestRate : Fps();
495*38e8c45fSAndroid Build Coastguard Worker }
496*38e8c45fSAndroid Build Coastguard Worker 
convertCompatibility(int8_t compatibility)497*38e8c45fSAndroid Build Coastguard Worker FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
498*38e8c45fSAndroid Build Coastguard Worker     switch (compatibility) {
499*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
500*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::Default;
501*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
502*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::ExactOrMultiple;
503*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_EXACT:
504*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::Exact;
505*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_MIN:
506*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::Min;
507*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE:
508*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::Gte;
509*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
510*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::NoVote;
511*38e8c45fSAndroid Build Coastguard Worker         default:
512*38e8c45fSAndroid Build Coastguard Worker             LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
513*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCompatibility::Default;
514*38e8c45fSAndroid Build Coastguard Worker     }
515*38e8c45fSAndroid Build Coastguard Worker }
516*38e8c45fSAndroid Build Coastguard Worker 
convertChangeFrameRateStrategy(int8_t strategy)517*38e8c45fSAndroid Build Coastguard Worker Seamlessness LayerInfo::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
518*38e8c45fSAndroid Build Coastguard Worker     switch (strategy) {
519*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
520*38e8c45fSAndroid Build Coastguard Worker             return Seamlessness::OnlySeamless;
521*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
522*38e8c45fSAndroid Build Coastguard Worker             return Seamlessness::SeamedAndSeamless;
523*38e8c45fSAndroid Build Coastguard Worker         default:
524*38e8c45fSAndroid Build Coastguard Worker             LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
525*38e8c45fSAndroid Build Coastguard Worker             return Seamlessness::Default;
526*38e8c45fSAndroid Build Coastguard Worker     }
527*38e8c45fSAndroid Build Coastguard Worker }
528*38e8c45fSAndroid Build Coastguard Worker 
convertCategory(int8_t category)529*38e8c45fSAndroid Build Coastguard Worker FrameRateCategory LayerInfo::FrameRate::convertCategory(int8_t category) {
530*38e8c45fSAndroid Build Coastguard Worker     switch (category) {
531*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT:
532*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::Default;
533*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE:
534*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::NoPreference;
535*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_CATEGORY_LOW:
536*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::Low;
537*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL:
538*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::Normal;
539*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT:
540*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::HighHint;
541*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH:
542*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::High;
543*38e8c45fSAndroid Build Coastguard Worker         default:
544*38e8c45fSAndroid Build Coastguard Worker             LOG_ALWAYS_FATAL("Invalid frame rate category value %d", category);
545*38e8c45fSAndroid Build Coastguard Worker             return FrameRateCategory::Default;
546*38e8c45fSAndroid Build Coastguard Worker     }
547*38e8c45fSAndroid Build Coastguard Worker }
548*38e8c45fSAndroid Build Coastguard Worker 
convertFrameRateSelectionStrategy(int8_t strategy)549*38e8c45fSAndroid Build Coastguard Worker LayerInfo::FrameRateSelectionStrategy LayerInfo::convertFrameRateSelectionStrategy(
550*38e8c45fSAndroid Build Coastguard Worker         int8_t strategy) {
551*38e8c45fSAndroid Build Coastguard Worker     switch (strategy) {
552*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE:
553*38e8c45fSAndroid Build Coastguard Worker             return FrameRateSelectionStrategy::Propagate;
554*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN:
555*38e8c45fSAndroid Build Coastguard Worker             return FrameRateSelectionStrategy::OverrideChildren;
556*38e8c45fSAndroid Build Coastguard Worker         case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF:
557*38e8c45fSAndroid Build Coastguard Worker             return FrameRateSelectionStrategy::Self;
558*38e8c45fSAndroid Build Coastguard Worker         default:
559*38e8c45fSAndroid Build Coastguard Worker             LOG_ALWAYS_FATAL("Invalid frame rate selection strategy value %d", strategy);
560*38e8c45fSAndroid Build Coastguard Worker             return FrameRateSelectionStrategy::Self;
561*38e8c45fSAndroid Build Coastguard Worker     }
562*38e8c45fSAndroid Build Coastguard Worker }
563*38e8c45fSAndroid Build Coastguard Worker 
isNoVote() const564*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::FrameRate::isNoVote() const {
565*38e8c45fSAndroid Build Coastguard Worker     // A desired frame rate greater than or equal to 0 is treated as NoVote.
566*38e8c45fSAndroid Build Coastguard Worker     bool isNoVoteGte = FlagManager::getInstance().arr_setframerate_gte_enum() &&
567*38e8c45fSAndroid Build Coastguard Worker             vote.type == FrameRateCompatibility::Gte && !vote.rate.isValid();
568*38e8c45fSAndroid Build Coastguard Worker     return vote.type == FrameRateCompatibility::NoVote || isNoVoteGte;
569*38e8c45fSAndroid Build Coastguard Worker }
570*38e8c45fSAndroid Build Coastguard Worker 
isValuelessType() const571*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::FrameRate::isValuelessType() const {
572*38e8c45fSAndroid Build Coastguard Worker     // For a valueless frame rate compatibility (type), the frame rate should be unspecified (0 Hz).
573*38e8c45fSAndroid Build Coastguard Worker     if (!isApproxEqual(vote.rate, 0_Hz)) {
574*38e8c45fSAndroid Build Coastguard Worker         return false;
575*38e8c45fSAndroid Build Coastguard Worker     }
576*38e8c45fSAndroid Build Coastguard Worker     switch (vote.type) {
577*38e8c45fSAndroid Build Coastguard Worker         case FrameRateCompatibility::Min:
578*38e8c45fSAndroid Build Coastguard Worker         case FrameRateCompatibility::NoVote:
579*38e8c45fSAndroid Build Coastguard Worker             return true;
580*38e8c45fSAndroid Build Coastguard Worker         case FrameRateCompatibility::Default:
581*38e8c45fSAndroid Build Coastguard Worker         case FrameRateCompatibility::ExactOrMultiple:
582*38e8c45fSAndroid Build Coastguard Worker         case FrameRateCompatibility::Exact:
583*38e8c45fSAndroid Build Coastguard Worker             return false;
584*38e8c45fSAndroid Build Coastguard Worker         case FrameRateCompatibility::Gte:
585*38e8c45fSAndroid Build Coastguard Worker             if (isNoVote()) {
586*38e8c45fSAndroid Build Coastguard Worker                 // Special case: GTE 0 is same as NoVote.
587*38e8c45fSAndroid Build Coastguard Worker                 return true;
588*38e8c45fSAndroid Build Coastguard Worker             }
589*38e8c45fSAndroid Build Coastguard Worker             return false;
590*38e8c45fSAndroid Build Coastguard Worker     }
591*38e8c45fSAndroid Build Coastguard Worker }
592*38e8c45fSAndroid Build Coastguard Worker 
isValid() const593*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::FrameRate::isValid() const {
594*38e8c45fSAndroid Build Coastguard Worker     return isValuelessType() || vote.rate.isValid() || category != FrameRateCategory::Default;
595*38e8c45fSAndroid Build Coastguard Worker }
596*38e8c45fSAndroid Build Coastguard Worker 
isVoteValidForMrr(bool isVrrDevice) const597*38e8c45fSAndroid Build Coastguard Worker bool LayerInfo::FrameRate::isVoteValidForMrr(bool isVrrDevice) const {
598*38e8c45fSAndroid Build Coastguard Worker     if (isVrrDevice || FlagManager::getInstance().frame_rate_category_mrr()) {
599*38e8c45fSAndroid Build Coastguard Worker         return true;
600*38e8c45fSAndroid Build Coastguard Worker     }
601*38e8c45fSAndroid Build Coastguard Worker 
602*38e8c45fSAndroid Build Coastguard Worker     if (category == FrameRateCategory::Default) {
603*38e8c45fSAndroid Build Coastguard Worker         return true;
604*38e8c45fSAndroid Build Coastguard Worker     }
605*38e8c45fSAndroid Build Coastguard Worker 
606*38e8c45fSAndroid Build Coastguard Worker     if (category == FrameRateCategory::NoPreference && vote.rate.isValid() &&
607*38e8c45fSAndroid Build Coastguard Worker         vote.type == FrameRateCompatibility::ExactOrMultiple) {
608*38e8c45fSAndroid Build Coastguard Worker         return true;
609*38e8c45fSAndroid Build Coastguard Worker     }
610*38e8c45fSAndroid Build Coastguard Worker 
611*38e8c45fSAndroid Build Coastguard Worker     return false;
612*38e8c45fSAndroid Build Coastguard Worker }
613*38e8c45fSAndroid Build Coastguard Worker 
operator <<(std::ostream & stream,const LayerInfo::FrameRate & rate)614*38e8c45fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& stream, const LayerInfo::FrameRate& rate) {
615*38e8c45fSAndroid Build Coastguard Worker     return stream << "{rate=" << rate.vote.rate << " type=" << ftl::enum_string(rate.vote.type)
616*38e8c45fSAndroid Build Coastguard Worker                   << " seamlessness=" << ftl::enum_string(rate.vote.seamlessness) << '}';
617*38e8c45fSAndroid Build Coastguard Worker }
618*38e8c45fSAndroid Build Coastguard Worker 
619*38e8c45fSAndroid Build Coastguard Worker } // namespace android::scheduler
620*38e8c45fSAndroid Build Coastguard Worker 
621*38e8c45fSAndroid Build Coastguard Worker // TODO(b/129481165): remove the #pragma below and fix conversion issues
622*38e8c45fSAndroid Build Coastguard Worker #pragma clang diagnostic pop // ignored "-Wextra"
623