1 /*
2  * Copyright (C) 2024 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 #include "VariableRefreshRateStatistic.h"
18 
19 namespace android::hardware::graphics::composer {
20 
VariableRefreshRateStatistic(CommonDisplayContextProvider * displayContextProvider,EventQueue * eventQueue,int maxFrameRate,int maxTeFrequency,int64_t updatePeriodNs)21 VariableRefreshRateStatistic::VariableRefreshRateStatistic(
22         CommonDisplayContextProvider* displayContextProvider, EventQueue* eventQueue,
23         int maxFrameRate, int maxTeFrequency, int64_t updatePeriodNs)
24       : mDisplayContextProvider(displayContextProvider),
25         mEventQueue(eventQueue),
26         mMaxFrameRate(maxFrameRate),
27         mMaxTeFrequency(maxTeFrequency),
28         mMinFrameIntervalNs(roundDivide(std::nano::den, static_cast<int64_t>(maxFrameRate))),
29         mTeFrequency(maxFrameRate),
30         mTeIntervalNs(roundDivide(std::nano::den, static_cast<int64_t>(mTeFrequency))),
31         mUpdatePeriodNs(updatePeriodNs) {
32     mStartStatisticTimeNs = getBootClockTimeNs();
33 
34     // For debugging purposes, this will only be triggered when DEBUG_VRR_STATISTICS is defined.
35 #ifdef DEBUG_VRR_STATISTICS
36     auto configs = mDisplayContextProvider->getDisplayConfigs();
37     for (const auto& config : *configs) {
38         ALOGI("VariableRefreshRateStatistic: config id = %d : %s", config.first,
39               config.second.toString().c_str());
40     }
41     mUpdateEvent.mEventType = VrrControllerEventType::kStaticticUpdate;
42     mUpdateEvent.mFunctor =
43             std::move(std::bind(&VariableRefreshRateStatistic::updateStatistic, this));
44     mUpdateEvent.mWhenNs = getSteadyClockTimeNs() + mUpdatePeriodNs;
45     mEventQueue->mPriorityQueue.emplace(mUpdateEvent);
46 #endif
47     mStatistics[mDisplayRefreshProfile] = DisplayRefreshRecord();
48 }
49 
getPowerOffDurationNs() const50 uint64_t VariableRefreshRateStatistic::getPowerOffDurationNs() const {
51     if (isPowerModeOffNowLocked()) {
52         const auto& item = mStatistics.find(mDisplayRefreshProfile);
53         if (item == mStatistics.end()) {
54             ALOGE("%s We should have inserted power-off item in constructor.", __func__);
55             return 0;
56         }
57         return mPowerOffDurationNs +
58                 (getBootClockTimeNs() - item->second.mLastTimeStampInBootClockNs);
59     } else {
60         return mPowerOffDurationNs;
61     }
62 }
63 
getStartStatisticTimeNs() const64 uint64_t VariableRefreshRateStatistic::getStartStatisticTimeNs() const {
65     return mStartStatisticTimeNs;
66 }
67 
getStatistics()68 DisplayRefreshStatistics VariableRefreshRateStatistic::getStatistics() {
69     updateIdleStats();
70     std::scoped_lock lock(mMutex);
71     return mStatistics;
72 }
73 
getUpdatedStatistics()74 DisplayRefreshStatistics VariableRefreshRateStatistic::getUpdatedStatistics() {
75     updateIdleStats();
76     std::scoped_lock lock(mMutex);
77     DisplayRefreshStatistics updatedStatistics;
78     for (auto& it : mStatistics) {
79         if (it.second.mUpdated) {
80             if (it.first.mNumVsync < 0) {
81                 it.second.mAccumulatedTimeNs = getPowerOffDurationNs();
82             }
83         }
84         // need all mStatistics to be able to do aggregation and bucketing accurately
85         updatedStatistics[it.first] = it.second;
86     }
87     if (isPowerModeOffNowLocked()) {
88         mStatistics[mDisplayRefreshProfile].mUpdated = true;
89     }
90 
91     return std::move(updatedStatistics);
92 }
93 
dumpStatistics(bool getUpdatedOnly,RefreshSource refreshSource,const std::string & delimiter)94 std::string VariableRefreshRateStatistic::dumpStatistics(bool getUpdatedOnly,
95                                                          RefreshSource refreshSource,
96                                                          const std::string& delimiter) {
97     std::string res;
98     updateIdleStats();
99     std::scoped_lock lock(mMutex);
100     for (auto& it : mStatistics) {
101         if ((!getUpdatedOnly) || (it.second.mUpdated)) {
102             if (it.first.mRefreshSource & refreshSource) {
103                 if (it.first.mNumVsync < 0) {
104                     it.second.mAccumulatedTimeNs = getPowerOffDurationNs();
105                 }
106                 res += "[";
107                 res += it.first.toString();
108                 res += " , ";
109                 res += it.second.toString();
110                 res += "]";
111                 res += delimiter;
112             }
113         }
114     }
115     return res;
116 }
117 
normalizeString(const std::string & input)118 std::string VariableRefreshRateStatistic::normalizeString(const std::string& input) {
119     static constexpr int kDesiredLength = 30;
120     static constexpr int kSpaceWidth = 1;
121     int extraSpacesNeeded = std::max(0, (kDesiredLength - static_cast<int>(input.length())));
122     return input + std::string(extraSpacesNeeded, ' ');
123 }
124 
dump(String8 & result,const std::vector<std::string> & args)125 void VariableRefreshRateStatistic::dump(String8& result, const std::vector<std::string>& args) {
126     bool hasDelta = false;
127 
128     if (!args.empty()) {
129         for (const auto& arg : args) {
130             std::string lowercaseArg = arg;
131             std::transform(lowercaseArg.begin(), lowercaseArg.end(), lowercaseArg.begin(),
132                            [](unsigned char c) { return std::tolower(c); });
133 
134             if (lowercaseArg.find("delta") != std::string::npos) {
135                 hasDelta = true;
136             }
137         }
138     }
139 
140     auto updatedStatistics = getUpdatedStatistics();
141     auto curTime = getSteadyClockTimeNs();
142     std::map<std::string, DisplayRefreshRecord, StateNameComparator> aggregatedStats;
143     std::map<std::string, DisplayRefreshRecord> aggregatedStatsSnapshot;
144     // Aggregating lastSnapshot dumpsys to calculate delta
145     for (const auto& it : mStatisticsSnapshot) {
146         PowerStatsProfile profile = it.first.toPowerStatsProfile(false);
147         std::string stateName = mPowerStatsProfileTokenGenerator.generateStateName(&profile, false);
148         aggregatedStatsSnapshot[stateName] += it.second;
149     }
150 
151     for (const auto& it : updatedStatistics) {
152         PowerStatsProfile profile = it.first.toPowerStatsProfile(false);
153         std::string stateName = mPowerStatsProfileTokenGenerator.generateStateName(&profile, false);
154         aggregatedStats[stateName] += it.second;
155     }
156 
157     if (hasDelta) {
158         result.appendFormat("Elapsed Time: %" PRId64 " \n", (curTime - mLastDumpsysTime) / 1000000);
159     }
160 
161     std::string headerString = hasDelta ? normalizeString("StateName") + "\t" +
162                     normalizeString("Total Time (ms)") + "\t" + normalizeString("Delta") + "\t" +
163                     normalizeString("Total Entries") + "\t" + normalizeString("Delta") + "\t" +
164                     normalizeString("Last Entry TStamp (ms)") + "\t" + normalizeString("Delta")
165                                         : normalizeString("StateName") + "\t" +
166                     normalizeString("Total Time (ms)") + "\t" + normalizeString("Total Entries") +
167                     "\t" + normalizeString("Last Entry TStamp (ms)");
168 
169     result.appendFormat("%s \n", headerString.c_str());
170 
171     for (const auto& it : aggregatedStats) {
172         uint64_t countDelta = 0;
173         uint64_t accumulatedTimeNsDelta = 0;
174         uint64_t lastTimeStampInBootClockNsDelta = 0;
175 
176         auto agIt = aggregatedStatsSnapshot.find(it.first);
177         if (agIt != aggregatedStatsSnapshot.end()) {
178             countDelta = it.second.mCount - agIt->second.mCount;
179             accumulatedTimeNsDelta = it.second.mAccumulatedTimeNs - agIt->second.mAccumulatedTimeNs;
180             lastTimeStampInBootClockNsDelta = it.second.mLastTimeStampInBootClockNs -
181                     agIt->second.mLastTimeStampInBootClockNs;
182         }
183 
184         std::string statsString = hasDelta
185                 ? normalizeString(it.first) + "\t" +
186                         normalizeString(std::to_string(it.second.mAccumulatedTimeNs / 1000000)) +
187                         "\t" + normalizeString(std::to_string(accumulatedTimeNsDelta / 1000000)) +
188                         "\t" + normalizeString(std::to_string(it.second.mCount)) + "\t" +
189                         normalizeString(std::to_string(countDelta)) + "\t" +
190                         normalizeString(
191                                 std::to_string(it.second.mLastTimeStampInBootClockNs / 1000000)) +
192                         "\t" +
193                         normalizeString(std::to_string(lastTimeStampInBootClockNsDelta / 1000000))
194                 :
195 
196                 normalizeString(it.first) + "\t" +
197                         normalizeString(std::to_string(it.second.mAccumulatedTimeNs / 1000000)) +
198                         "\t" + normalizeString(std::to_string(it.second.mCount)) + "\t" +
199                         normalizeString(
200                                 std::to_string(it.second.mLastTimeStampInBootClockNs / 1000000));
201 
202         result.appendFormat("%s \n", statsString.c_str());
203     }
204 
205     // Take a snapshot of updatedStatistics and time
206     mLastDumpsysTime = curTime;
207     mStatisticsSnapshot = DisplayRefreshStatistics(updatedStatistics);
208 }
209 
onPowerStateChange(int from,int to)210 void VariableRefreshRateStatistic::onPowerStateChange(int from, int to) {
211     if (from == to) {
212         return;
213     }
214     if (mDisplayRefreshProfile.mCurrentDisplayConfig.mPowerMode != from) {
215         ALOGE("%s Power mode mismatch between storing state(%d) and actual mode(%d)", __func__,
216               mDisplayRefreshProfile.mCurrentDisplayConfig.mPowerMode, from);
217     }
218     updateIdleStats();
219     std::scoped_lock lock(mMutex);
220     if (isPowerModeOff(to)) {
221         // Currently the for power stats both |HWC_POWER_MODE_OFF| and |HWC_POWER_MODE_DOZE_SUSPEND|
222         // are classified as "off" states in power statistics. Consequently,we assign the value of
223         // |HWC_POWER_MODE_OFF| to |mPowerMode| when it is |HWC_POWER_MODE_DOZE_SUSPEND|.
224         mDisplayRefreshProfile.mCurrentDisplayConfig.mPowerMode = HWC_POWER_MODE_OFF;
225 
226         auto& record = mStatistics[mDisplayRefreshProfile];
227         ++record.mCount;
228         record.mLastTimeStampInBootClockNs = getBootClockTimeNs();
229         record.mUpdated = true;
230 
231         mLastRefreshTimeInBootClockNs = kDefaultInvalidPresentTimeNs;
232     } else {
233         if (isPowerModeOff(from)) {
234             mPowerOffDurationNs +=
235                     (getBootClockTimeNs() -
236                      mStatistics[mDisplayRefreshProfile].mLastTimeStampInBootClockNs);
237         }
238         mDisplayRefreshProfile.mCurrentDisplayConfig.mPowerMode = to;
239         if (to == HWC_POWER_MODE_DOZE) {
240             mDisplayRefreshProfile.mNumVsync = mTeFrequency;
241             auto& record = mStatistics[mDisplayRefreshProfile];
242             ++record.mCount;
243             record.mLastTimeStampInBootClockNs = getBootClockTimeNs();
244             record.mUpdated = true;
245         }
246     }
247 }
248 
onPresent(int64_t presentTimeNs,int flag)249 void VariableRefreshRateStatistic::onPresent(int64_t presentTimeNs, int flag) {
250     onRefreshInternal(presentTimeNs, flag, RefreshSource::kRefreshSourceActivePresent);
251 }
252 
onNonPresentRefresh(int64_t refreshTimeNs,RefreshSource refreshSource)253 void VariableRefreshRateStatistic::onNonPresentRefresh(int64_t refreshTimeNs,
254                                                        RefreshSource refreshSource) {
255     onRefreshInternal(refreshTimeNs, 0, refreshSource);
256 }
257 
onRefreshInternal(int64_t refreshTimeNs,int flag,RefreshSource refreshSource)258 void VariableRefreshRateStatistic::onRefreshInternal(int64_t refreshTimeNs, int flag,
259                                                      RefreshSource refreshSource) {
260     int64_t presentTimeInBootClockNs = steadyClockTimeToBootClockTimeNs(refreshTimeNs);
261     if (mLastRefreshTimeInBootClockNs == kDefaultInvalidPresentTimeNs) {
262         mLastRefreshTimeInBootClockNs = presentTimeInBootClockNs;
263         updateCurrentDisplayStatus();
264         // Ignore first refresh after resume
265         return;
266     }
267     updateIdleStats(presentTimeInBootClockNs);
268     updateCurrentDisplayStatus();
269     if (hasPresentFrameFlag(flag, PresentFrameFlag::kPresentingWhenDoze)) {
270         // In low power mode, panel boost to 30 Hz while presenting new frame.
271         mDisplayRefreshProfile.mNumVsync = mTeFrequency / kFrameRateWhenPresentAtLpMode;
272         mLastRefreshTimeInBootClockNs =
273                 presentTimeInBootClockNs + (std::nano::den / kFrameRateWhenPresentAtLpMode);
274     } else {
275         int numVsync = roundDivide((presentTimeInBootClockNs - mLastRefreshTimeInBootClockNs),
276                                    mTeIntervalNs);
277         // TODO(b/353976456): Implement a scheduler to avoid conflicts between present and
278         // non-present refresh. Currently, If a conflict occurs, both present and non-present
279         // refresh may request to take effect simultaneously, resulting in a zero duration between
280         // them. To address this, we avoid including statistics with zero duration. This issue
281         // should be resolved once the scheduler is implemented.
282         if (numVsync == 0) return;
283         numVsync = std::max(1, std::min(mTeFrequency, numVsync));
284         mDisplayRefreshProfile.mNumVsync = numVsync;
285         mLastRefreshTimeInBootClockNs = presentTimeInBootClockNs;
286         mDisplayRefreshProfile.mRefreshSource = refreshSource;
287     }
288     {
289         std::scoped_lock lock(mMutex);
290 
291         auto& record = mStatistics[mDisplayRefreshProfile];
292         ++record.mCount;
293         record.mAccumulatedTimeNs += (mTeIntervalNs * mDisplayRefreshProfile.mNumVsync);
294         record.mLastTimeStampInBootClockNs = presentTimeInBootClockNs;
295         record.mUpdated = true;
296         if (hasPresentFrameFlag(flag, PresentFrameFlag::kPresentingWhenDoze)) {
297             // After presenting a frame in AOD, we revert back to 1 Hz operation.
298             mDisplayRefreshProfile.mNumVsync = mTeFrequency;
299             auto& record = mStatistics[mDisplayRefreshProfile];
300             ++record.mCount;
301             record.mLastTimeStampInBootClockNs = mLastRefreshTimeInBootClockNs;
302             record.mUpdated = true;
303         }
304     }
305 }
306 
setActiveVrrConfiguration(int activeConfigId,int teFrequency)307 void VariableRefreshRateStatistic::setActiveVrrConfiguration(int activeConfigId, int teFrequency) {
308     updateIdleStats();
309     mDisplayRefreshProfile.mCurrentDisplayConfig.mActiveConfigId = activeConfigId;
310     mDisplayRefreshProfile.mWidth = mDisplayContextProvider->getWidth(activeConfigId);
311     mDisplayRefreshProfile.mHeight = mDisplayContextProvider->getHeight(activeConfigId);
312     mDisplayRefreshProfile.mTeFrequency = mDisplayContextProvider->getTeFrequency(activeConfigId);
313     mTeFrequency = teFrequency;
314     if (mTeFrequency % mMaxFrameRate != 0) {
315         ALOGW("%s TE frequency does not align with the maximum frame rate as a multiplier.",
316               __func__);
317     }
318     mTeIntervalNs = roundDivide(std::nano::den, static_cast<int64_t>(mTeFrequency));
319     // TODO(b/333204544): how can we handle the case if mTeFrequency % mMinimumRefreshRate != 0?
320     if ((mMinimumRefreshRate > 0) && (mTeFrequency % mMinimumRefreshRate != 0)) {
321         ALOGW("%s TE frequency does not align with the lowest frame rate as a multiplier.",
322               __func__);
323     }
324 }
325 
setFixedRefreshRate(uint32_t rate)326 void VariableRefreshRateStatistic::setFixedRefreshRate(uint32_t rate) {
327     if (mMinimumRefreshRate != rate) {
328         updateIdleStats();
329         mMinimumRefreshRate = rate;
330         if (mMinimumRefreshRate > 1) {
331             mMaximumFrameIntervalNs =
332                     roundDivide(std::nano::den, static_cast<int64_t>(mMinimumRefreshRate));
333             // TODO(b/333204544): how can we handle the case if mTeFrequency % mMinimumRefreshRate
334             // != 0?
335             if (mTeFrequency % mMinimumRefreshRate != 0) {
336                 ALOGW("%s TE frequency does not align with the lowest frame rate as a multiplier.",
337                       __func__);
338             }
339         } else {
340             mMaximumFrameIntervalNs = kMaxRefreshIntervalNs;
341         }
342     }
343 }
344 
isPowerModeOffNowLocked() const345 bool VariableRefreshRateStatistic::isPowerModeOffNowLocked() const {
346     return isPowerModeOff(mDisplayRefreshProfile.mCurrentDisplayConfig.mPowerMode);
347 }
348 
updateCurrentDisplayStatus()349 void VariableRefreshRateStatistic::updateCurrentDisplayStatus() {
350     mDisplayRefreshProfile.mCurrentDisplayConfig.mBrightnessMode =
351             mDisplayContextProvider->getBrightnessMode();
352     if (mDisplayRefreshProfile.mCurrentDisplayConfig.mBrightnessMode ==
353         BrightnessMode::kInvalidBrightnessMode) {
354         mDisplayRefreshProfile.mCurrentDisplayConfig.mBrightnessMode =
355                 BrightnessMode::kNormalBrightnessMode;
356     }
357 }
358 
updateIdleStats(int64_t endTimeStampInBootClockNs)359 void VariableRefreshRateStatistic::updateIdleStats(int64_t endTimeStampInBootClockNs) {
360     if (mDisplayRefreshProfile.isOff()) return;
361     if (mLastRefreshTimeInBootClockNs == kDefaultInvalidPresentTimeNs) return;
362 
363     endTimeStampInBootClockNs =
364             endTimeStampInBootClockNs < 0 ? getBootClockTimeNs() : endTimeStampInBootClockNs;
365     auto durationFromLastPresentNs = endTimeStampInBootClockNs - mLastRefreshTimeInBootClockNs;
366     durationFromLastPresentNs = durationFromLastPresentNs < 0 ? 0 : durationFromLastPresentNs;
367     if (mDisplayRefreshProfile.mCurrentDisplayConfig.mPowerMode == HWC_POWER_MODE_DOZE) {
368         mDisplayRefreshProfile.mNumVsync = mTeFrequency;
369 
370         std::scoped_lock lock(mMutex);
371 
372         auto& record = mStatistics[mDisplayRefreshProfile];
373         record.mAccumulatedTimeNs += durationFromLastPresentNs;
374         record.mLastTimeStampInBootClockNs = mLastRefreshTimeInBootClockNs;
375         mLastRefreshTimeInBootClockNs = endTimeStampInBootClockNs;
376         record.mUpdated = true;
377     } else {
378         if ((mMinimumRefreshRate > 1) &&
379             (!isPresentRefresh(mDisplayRefreshProfile.mRefreshSource))) {
380             ALOGE("%s We should not have non-present refresh when the minimum refresh rate is set, "
381                   "as it should use auto mode.",
382                   __func__);
383             return;
384         }
385         mDisplayRefreshProfile.mRefreshSource = RefreshSource::kRefreshSourceIdlePresent;
386 
387         int numVsync = roundDivide(durationFromLastPresentNs, mTeIntervalNs);
388         mDisplayRefreshProfile.mNumVsync =
389                 (mMinimumRefreshRate > 1 ? (mTeFrequency / mMinimumRefreshRate) : mTeFrequency);
390         if (numVsync <= mDisplayRefreshProfile.mNumVsync) return;
391 
392         // Ensure that the last vsync should not be included now, since it would be processed for
393         // next update or |onPresent|
394         auto count = (numVsync - 1) / mDisplayRefreshProfile.mNumVsync;
395         auto alignedDurationNs = mMaximumFrameIntervalNs * count;
396         {
397             std::scoped_lock lock(mMutex);
398 
399             auto& record = mStatistics[mDisplayRefreshProfile];
400             record.mCount += count;
401             record.mAccumulatedTimeNs += alignedDurationNs;
402             mLastRefreshTimeInBootClockNs += alignedDurationNs;
403             record.mLastTimeStampInBootClockNs = mLastRefreshTimeInBootClockNs;
404             record.mUpdated = true;
405         }
406     }
407 }
408 
409 #ifdef DEBUG_VRR_STATISTICS
updateStatistic()410 int VariableRefreshRateStatistic::updateStatistic() {
411     updateIdleStats();
412     for (const auto& it : mStatistics) {
413         const auto& key = it.first;
414         const auto& value = it.second;
415         ALOGD("%s: power mode = %d, id = %d, birghtness mode = %d, vsync "
416               "= %d : count = %ld, last entry time =  %ld",
417               __func__, key.mCurrentDisplayConfig.mPowerMode,
418               key.mCurrentDisplayConfig.mActiveConfigId, key.mCurrentDisplayConfig.mBrightnessMode,
419               key.mNumVsync, value.mCount, value.mLastTimeStampInBootClockNs);
420     }
421     // Post next update statistics event.
422     mUpdateEvent.mWhenNs = getSteadyClockTimeNs() + mUpdatePeriodNs;
423     mEventQueue->mPriorityQueue.emplace(mUpdateEvent);
424 
425     return NO_ERROR;
426 }
427 #endif
428 
429 } // namespace android::hardware::graphics::composer
430