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