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