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