xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/Scheduler/RefreshRateStats.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2019 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 #pragma once
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include <chrono>
20*38e8c45fSAndroid Build Coastguard Worker #include <cinttypes>
21*38e8c45fSAndroid Build Coastguard Worker #include <cstdlib>
22*38e8c45fSAndroid Build Coastguard Worker #include <string>
23*38e8c45fSAndroid Build Coastguard Worker 
24*38e8c45fSAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <ftl/algorithm.h>
26*38e8c45fSAndroid Build Coastguard Worker #include <ftl/small_map.h>
27*38e8c45fSAndroid Build Coastguard Worker #include <utils/Timers.h>
28*38e8c45fSAndroid Build Coastguard Worker 
29*38e8c45fSAndroid Build Coastguard Worker #include <scheduler/Fps.h>
30*38e8c45fSAndroid Build Coastguard Worker 
31*38e8c45fSAndroid Build Coastguard Worker #include "DisplayHardware/Hal.h"
32*38e8c45fSAndroid Build Coastguard Worker #include "TimeStats/TimeStats.h"
33*38e8c45fSAndroid Build Coastguard Worker 
34*38e8c45fSAndroid Build Coastguard Worker namespace android::scheduler {
35*38e8c45fSAndroid Build Coastguard Worker 
36*38e8c45fSAndroid Build Coastguard Worker /**
37*38e8c45fSAndroid Build Coastguard Worker  * Class to encapsulate statistics about refresh rates that the display is using. When the power
38*38e8c45fSAndroid Build Coastguard Worker  * mode is set to HWC_POWER_MODE_NORMAL, SF is switching between refresh rates that are stored in
39*38e8c45fSAndroid Build Coastguard Worker  * the device's configs. Otherwise, we assume the HWC is running in power saving mode under the
40*38e8c45fSAndroid Build Coastguard Worker  * hood (eg. the device is in DOZE, or screen off mode).
41*38e8c45fSAndroid Build Coastguard Worker  */
42*38e8c45fSAndroid Build Coastguard Worker class RefreshRateStats {
43*38e8c45fSAndroid Build Coastguard Worker     static constexpr int64_t MS_PER_S = 1000;
44*38e8c45fSAndroid Build Coastguard Worker     static constexpr int64_t MS_PER_MIN = 60 * MS_PER_S;
45*38e8c45fSAndroid Build Coastguard Worker     static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN;
46*38e8c45fSAndroid Build Coastguard Worker     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
47*38e8c45fSAndroid Build Coastguard Worker 
48*38e8c45fSAndroid Build Coastguard Worker     using PowerMode = android::hardware::graphics::composer::hal::PowerMode;
49*38e8c45fSAndroid Build Coastguard Worker 
50*38e8c45fSAndroid Build Coastguard Worker public:
51*38e8c45fSAndroid Build Coastguard Worker     // TODO(b/185535769): Inject clock to avoid sleeping in tests.
RefreshRateStats(TimeStats & timeStats,Fps currentRefreshRate)52*38e8c45fSAndroid Build Coastguard Worker     RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate)
53*38e8c45fSAndroid Build Coastguard Worker           : mTimeStats(timeStats),
54*38e8c45fSAndroid Build Coastguard Worker             mCurrentRefreshRate(currentRefreshRate),
55*38e8c45fSAndroid Build Coastguard Worker             mCurrentPowerMode(PowerMode::OFF) {}
56*38e8c45fSAndroid Build Coastguard Worker 
setPowerMode(PowerMode mode)57*38e8c45fSAndroid Build Coastguard Worker     void setPowerMode(PowerMode mode) {
58*38e8c45fSAndroid Build Coastguard Worker         if (mCurrentPowerMode == mode) {
59*38e8c45fSAndroid Build Coastguard Worker             return;
60*38e8c45fSAndroid Build Coastguard Worker         }
61*38e8c45fSAndroid Build Coastguard Worker         flushTime();
62*38e8c45fSAndroid Build Coastguard Worker         mCurrentPowerMode = mode;
63*38e8c45fSAndroid Build Coastguard Worker     }
64*38e8c45fSAndroid Build Coastguard Worker 
65*38e8c45fSAndroid Build Coastguard Worker     // Sets config mode. If the mode has changed, it records how much time was spent in the previous
66*38e8c45fSAndroid Build Coastguard Worker     // mode.
setRefreshRate(Fps currRefreshRate)67*38e8c45fSAndroid Build Coastguard Worker     void setRefreshRate(Fps currRefreshRate) {
68*38e8c45fSAndroid Build Coastguard Worker         if (isApproxEqual(mCurrentRefreshRate, currRefreshRate)) {
69*38e8c45fSAndroid Build Coastguard Worker             return;
70*38e8c45fSAndroid Build Coastguard Worker         }
71*38e8c45fSAndroid Build Coastguard Worker         mTimeStats.incrementRefreshRateSwitches();
72*38e8c45fSAndroid Build Coastguard Worker         flushTime();
73*38e8c45fSAndroid Build Coastguard Worker         mCurrentRefreshRate = currRefreshRate;
74*38e8c45fSAndroid Build Coastguard Worker     }
75*38e8c45fSAndroid Build Coastguard Worker 
76*38e8c45fSAndroid Build Coastguard Worker     // Maps stringified refresh rate to total time spent in that mode.
77*38e8c45fSAndroid Build Coastguard Worker     using TotalTimes = ftl::SmallMap<std::string, std::chrono::milliseconds, 3>;
78*38e8c45fSAndroid Build Coastguard Worker 
getTotalTimes()79*38e8c45fSAndroid Build Coastguard Worker     TotalTimes getTotalTimes() {
80*38e8c45fSAndroid Build Coastguard Worker         // If the power mode is on, then we are probably switching between the config modes. If
81*38e8c45fSAndroid Build Coastguard Worker         // it's not then the screen is probably off. Make sure to flush times before printing
82*38e8c45fSAndroid Build Coastguard Worker         // them.
83*38e8c45fSAndroid Build Coastguard Worker         flushTime();
84*38e8c45fSAndroid Build Coastguard Worker 
85*38e8c45fSAndroid Build Coastguard Worker         TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
86*38e8c45fSAndroid Build Coastguard Worker 
87*38e8c45fSAndroid Build Coastguard Worker         // Sum the times for modes that map to the same name, e.g. "60 Hz".
88*38e8c45fSAndroid Build Coastguard Worker         for (const auto& [fps, time] : mFpsTotalTimes) {
89*38e8c45fSAndroid Build Coastguard Worker             const auto string = to_string(fps);
90*38e8c45fSAndroid Build Coastguard Worker             const auto total = std::as_const(totalTimes)
91*38e8c45fSAndroid Build Coastguard Worker                                        .get(string)
92*38e8c45fSAndroid Build Coastguard Worker                                        .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
93*38e8c45fSAndroid Build Coastguard Worker                                            using namespace std::chrono_literals;
94*38e8c45fSAndroid Build Coastguard Worker                                            return 0ms;
95*38e8c45fSAndroid Build Coastguard Worker                                        }))
96*38e8c45fSAndroid Build Coastguard Worker                                        .value();
97*38e8c45fSAndroid Build Coastguard Worker 
98*38e8c45fSAndroid Build Coastguard Worker             totalTimes.emplace_or_replace(string, total.get() + time);
99*38e8c45fSAndroid Build Coastguard Worker         }
100*38e8c45fSAndroid Build Coastguard Worker 
101*38e8c45fSAndroid Build Coastguard Worker         return totalTimes;
102*38e8c45fSAndroid Build Coastguard Worker     }
103*38e8c45fSAndroid Build Coastguard Worker 
104*38e8c45fSAndroid Build Coastguard Worker     // Traverses through the map of config modes and returns how long they've been running in easy
105*38e8c45fSAndroid Build Coastguard Worker     // to read format.
dump(std::string & result)106*38e8c45fSAndroid Build Coastguard Worker     void dump(std::string& result) const {
107*38e8c45fSAndroid Build Coastguard Worker         std::ostringstream stream("+  Refresh rate: running time in seconds\n");
108*38e8c45fSAndroid Build Coastguard Worker 
109*38e8c45fSAndroid Build Coastguard Worker         for (const auto& [name, time] : const_cast<RefreshRateStats*>(this)->getTotalTimes()) {
110*38e8c45fSAndroid Build Coastguard Worker             stream << name << ": " << getDateFormatFromMs(time) << '\n';
111*38e8c45fSAndroid Build Coastguard Worker         }
112*38e8c45fSAndroid Build Coastguard Worker         result.append(stream.str());
113*38e8c45fSAndroid Build Coastguard Worker     }
114*38e8c45fSAndroid Build Coastguard Worker 
115*38e8c45fSAndroid Build Coastguard Worker private:
116*38e8c45fSAndroid Build Coastguard Worker     // Calculates the time that passed in ms between the last time we recorded time and the time
117*38e8c45fSAndroid Build Coastguard Worker     // this method was called.
flushTime()118*38e8c45fSAndroid Build Coastguard Worker     void flushTime() {
119*38e8c45fSAndroid Build Coastguard Worker         const nsecs_t currentTime = systemTime();
120*38e8c45fSAndroid Build Coastguard Worker         const nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
121*38e8c45fSAndroid Build Coastguard Worker         mPreviousRecordedTime = currentTime;
122*38e8c45fSAndroid Build Coastguard Worker 
123*38e8c45fSAndroid Build Coastguard Worker         const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
124*38e8c45fSAndroid Build Coastguard Worker         uint32_t fps = 0;
125*38e8c45fSAndroid Build Coastguard Worker 
126*38e8c45fSAndroid Build Coastguard Worker         if (mCurrentPowerMode == PowerMode::ON) {
127*38e8c45fSAndroid Build Coastguard Worker             // Normal power mode is counted under different config modes.
128*38e8c45fSAndroid Build Coastguard Worker             const auto total = std::as_const(mFpsTotalTimes)
129*38e8c45fSAndroid Build Coastguard Worker                                        .get(mCurrentRefreshRate)
130*38e8c45fSAndroid Build Coastguard Worker                                        .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
131*38e8c45fSAndroid Build Coastguard Worker                                            using namespace std::chrono_literals;
132*38e8c45fSAndroid Build Coastguard Worker                                            return 0ms;
133*38e8c45fSAndroid Build Coastguard Worker                                        }))
134*38e8c45fSAndroid Build Coastguard Worker                                        .value();
135*38e8c45fSAndroid Build Coastguard Worker 
136*38e8c45fSAndroid Build Coastguard Worker             mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
137*38e8c45fSAndroid Build Coastguard Worker 
138*38e8c45fSAndroid Build Coastguard Worker             fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
139*38e8c45fSAndroid Build Coastguard Worker         } else {
140*38e8c45fSAndroid Build Coastguard Worker             mScreenOffTime += duration;
141*38e8c45fSAndroid Build Coastguard Worker         }
142*38e8c45fSAndroid Build Coastguard Worker         mTimeStats.recordRefreshRate(fps, timeElapsed);
143*38e8c45fSAndroid Build Coastguard Worker     }
144*38e8c45fSAndroid Build Coastguard Worker 
145*38e8c45fSAndroid Build Coastguard Worker     // Formats the time in milliseconds into easy to read format.
getDateFormatFromMs(std::chrono::milliseconds time)146*38e8c45fSAndroid Build Coastguard Worker     static std::string getDateFormatFromMs(std::chrono::milliseconds time) {
147*38e8c45fSAndroid Build Coastguard Worker         auto [days, dayRemainderMs] = std::div(static_cast<int64_t>(time.count()), MS_PER_DAY);
148*38e8c45fSAndroid Build Coastguard Worker         auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
149*38e8c45fSAndroid Build Coastguard Worker         auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
150*38e8c45fSAndroid Build Coastguard Worker         auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
151*38e8c45fSAndroid Build Coastguard Worker         return base::StringPrintf("%" PRId64 "d%02" PRId64 ":%02" PRId64 ":%02" PRId64
152*38e8c45fSAndroid Build Coastguard Worker                                   ".%03" PRId64,
153*38e8c45fSAndroid Build Coastguard Worker                                   days, hours, mins, sec, secRemainderMs);
154*38e8c45fSAndroid Build Coastguard Worker     }
155*38e8c45fSAndroid Build Coastguard Worker 
156*38e8c45fSAndroid Build Coastguard Worker     // Aggregate refresh rate statistics for telemetry.
157*38e8c45fSAndroid Build Coastguard Worker     TimeStats& mTimeStats;
158*38e8c45fSAndroid Build Coastguard Worker 
159*38e8c45fSAndroid Build Coastguard Worker     Fps mCurrentRefreshRate;
160*38e8c45fSAndroid Build Coastguard Worker     PowerMode mCurrentPowerMode;
161*38e8c45fSAndroid Build Coastguard Worker 
162*38e8c45fSAndroid Build Coastguard Worker     ftl::SmallMap<Fps, std::chrono::milliseconds, 2, FpsApproxEqual> mFpsTotalTimes;
163*38e8c45fSAndroid Build Coastguard Worker     std::chrono::milliseconds mScreenOffTime = std::chrono::milliseconds::zero();
164*38e8c45fSAndroid Build Coastguard Worker 
165*38e8c45fSAndroid Build Coastguard Worker     nsecs_t mPreviousRecordedTime = systemTime();
166*38e8c45fSAndroid Build Coastguard Worker };
167*38e8c45fSAndroid Build Coastguard Worker 
168*38e8c45fSAndroid Build Coastguard Worker } // namespace android::scheduler
169