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