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[mDisplayPresentProfile] = DisplayPresentRecord();
48 }
49 
getPowerOffDurationNs() const50 uint64_t VariableRefreshRateStatistic::getPowerOffDurationNs() const {
51     if (isPowerModeOffNowLocked()) {
52         const auto& item = mStatistics.find(mDisplayPresentProfile);
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 DisplayPresentStatistics VariableRefreshRateStatistic::getStatistics() {
69     updateIdleStats();
70     std::scoped_lock lock(mMutex);
71     return mStatistics;
72 }
73 
getUpdatedStatistics()74 DisplayPresentStatistics VariableRefreshRateStatistic::getUpdatedStatistics() {
75     updateIdleStats();
76     std::scoped_lock lock(mMutex);
77     DisplayPresentStatistics updatedStatistics;
78     for (auto& it : mStatistics) {
79         if (it.second.mUpdated) {
80             if (it.first.mNumVsync < 0) {
81                 it.second.mAccumulatedTimeNs = getPowerOffDurationNs();
82             }
83             updatedStatistics[it.first] = it.second;
84             it.second.mUpdated = false;
85         }
86     }
87     if (isPowerModeOffNowLocked()) {
88         mStatistics[mDisplayPresentProfile].mUpdated = true;
89     }
90     return std::move(updatedStatistics);
91 }
92 
onPowerStateChange(int from,int to)93 void VariableRefreshRateStatistic::onPowerStateChange(int from, int to) {
94     if (from == to) {
95         return;
96     }
97     if (mDisplayPresentProfile.mCurrentDisplayConfig.mPowerMode != from) {
98         ALOGE("%s Power mode mismatch between storing state(%d) and actual mode(%d)", __func__,
99               mDisplayPresentProfile.mCurrentDisplayConfig.mPowerMode, from);
100     }
101     updateIdleStats();
102     std::scoped_lock lock(mMutex);
103     if (isPowerModeOff(to)) {
104         // Currently the for power stats both |HWC_POWER_MODE_OFF| and |HWC_POWER_MODE_DOZE_SUSPEND|
105         // are classified as "off" states in power statistics. Consequently,we assign the value of
106         // |HWC_POWER_MODE_OFF| to |mPowerMode| when it is |HWC_POWER_MODE_DOZE_SUSPEND|.
107         mDisplayPresentProfile.mCurrentDisplayConfig.mPowerMode = HWC_POWER_MODE_OFF;
108 
109         auto& record = mStatistics[mDisplayPresentProfile];
110         ++record.mCount;
111         record.mLastTimeStampInBootClockNs = getBootClockTimeNs();
112         record.mUpdated = true;
113 
114         mLastPresentTimeInBootClockNs = kDefaultInvalidPresentTimeNs;
115     } else {
116         if (isPowerModeOff(from)) {
117             mPowerOffDurationNs +=
118                     (getBootClockTimeNs() -
119                      mStatistics[mDisplayPresentProfile].mLastTimeStampInBootClockNs);
120         }
121         mDisplayPresentProfile.mCurrentDisplayConfig.mPowerMode = to;
122         if (to == HWC_POWER_MODE_DOZE) {
123             mDisplayPresentProfile.mNumVsync = mTeFrequency;
124             auto& record = mStatistics[mDisplayPresentProfile];
125             ++record.mCount;
126             record.mLastTimeStampInBootClockNs = getBootClockTimeNs();
127             record.mUpdated = true;
128         }
129     }
130 }
131 
onPresent(int64_t presentTimeNs,int flag)132 void VariableRefreshRateStatistic::onPresent(int64_t presentTimeNs, int flag) {
133     int64_t presentTimeInBootClockNs = steadyClockTimeToBootClockTimeNs(presentTimeNs);
134     if (mLastPresentTimeInBootClockNs == kDefaultInvalidPresentTimeNs) {
135         mLastPresentTimeInBootClockNs = presentTimeInBootClockNs;
136         updateCurrentDisplayStatus();
137         // Ignore first present after resume
138         return;
139     }
140     updateIdleStats(presentTimeInBootClockNs);
141     updateCurrentDisplayStatus();
142     if (hasPresentFrameFlag(flag, PresentFrameFlag::kPresentingWhenDoze)) {
143         // In low power mode, panel boost to 30 Hz while presenting new frame.
144         mDisplayPresentProfile.mNumVsync = mTeFrequency / kFrameRateWhenPresentAtLpMode;
145         mLastPresentTimeInBootClockNs =
146                 presentTimeInBootClockNs + (std::nano::den / kFrameRateWhenPresentAtLpMode);
147     } else {
148         int numVsync = roundDivide((presentTimeInBootClockNs - mLastPresentTimeInBootClockNs),
149                                    mTeIntervalNs);
150         numVsync = std::max(1, std::min(mTeFrequency, numVsync));
151         mDisplayPresentProfile.mNumVsync = numVsync;
152         mLastPresentTimeInBootClockNs = presentTimeInBootClockNs;
153     }
154     {
155         std::scoped_lock lock(mMutex);
156 
157         auto& record = mStatistics[mDisplayPresentProfile];
158         ++record.mCount;
159         record.mAccumulatedTimeNs += (mTeIntervalNs * mDisplayPresentProfile.mNumVsync);
160         record.mLastTimeStampInBootClockNs = presentTimeInBootClockNs;
161         record.mUpdated = true;
162         if (hasPresentFrameFlag(flag, PresentFrameFlag::kPresentingWhenDoze)) {
163             // After presenting a frame in AOD, we revert back to 1 Hz operation.
164             mDisplayPresentProfile.mNumVsync = mTeFrequency;
165             auto& record = mStatistics[mDisplayPresentProfile];
166             ++record.mCount;
167             record.mLastTimeStampInBootClockNs = mLastPresentTimeInBootClockNs;
168             record.mUpdated = true;
169         }
170     }
171 }
172 
setActiveVrrConfiguration(int activeConfigId,int teFrequency)173 void VariableRefreshRateStatistic::setActiveVrrConfiguration(int activeConfigId, int teFrequency) {
174     updateIdleStats();
175     mDisplayPresentProfile.mCurrentDisplayConfig.mActiveConfigId = activeConfigId;
176     mTeFrequency = teFrequency;
177     if (mTeFrequency % mMaxFrameRate != 0) {
178         ALOGW("%s TE frequency does not align with the maximum frame rate as a multiplier.",
179               __func__);
180     }
181     mTeIntervalNs = roundDivide(std::nano::den, static_cast<int64_t>(mTeFrequency));
182     // TODO(b/333204544): how can we handle the case if mTeFrequency % mMinimumRefreshRate != 0?
183     if ((mMinimumRefreshRate > 0) && (mTeFrequency % mMinimumRefreshRate != 0)) {
184         ALOGW("%s TE frequency does not align with the lowest frame rate as a multiplier.",
185               __func__);
186     }
187 }
188 
setFixedRefreshRate(uint32_t rate)189 void VariableRefreshRateStatistic::setFixedRefreshRate(uint32_t rate) {
190     if (mMinimumRefreshRate != rate) {
191         updateIdleStats();
192         mMinimumRefreshRate = rate;
193         if (mMinimumRefreshRate > 1) {
194             mMaximumFrameIntervalNs =
195                     roundDivide(std::nano::den, static_cast<int64_t>(mMinimumRefreshRate));
196             // TODO(b/333204544): how can we handle the case if mTeFrequency % mMinimumRefreshRate
197             // != 0?
198             if (mTeFrequency % mMinimumRefreshRate != 0) {
199                 ALOGW("%s TE frequency does not align with the lowest frame rate as a multiplier.",
200                       __func__);
201             }
202         } else {
203             mMaximumFrameIntervalNs = kMaxPresentIntervalNs;
204         }
205     }
206 }
207 
isPowerModeOffNowLocked() const208 bool VariableRefreshRateStatistic::isPowerModeOffNowLocked() const {
209     return isPowerModeOff(mDisplayPresentProfile.mCurrentDisplayConfig.mPowerMode);
210 }
211 
updateCurrentDisplayStatus()212 void VariableRefreshRateStatistic::updateCurrentDisplayStatus() {
213     mDisplayPresentProfile.mCurrentDisplayConfig.mBrightnessMode =
214             mDisplayContextProvider->getBrightnessMode();
215     if (mDisplayPresentProfile.mCurrentDisplayConfig.mBrightnessMode ==
216         BrightnessMode::kInvalidBrightnessMode) {
217         mDisplayPresentProfile.mCurrentDisplayConfig.mBrightnessMode =
218                 BrightnessMode::kNormalBrightnessMode;
219     }
220 }
221 
updateIdleStats(int64_t endTimeStampInBootClockNs)222 void VariableRefreshRateStatistic::updateIdleStats(int64_t endTimeStampInBootClockNs) {
223     if (mDisplayPresentProfile.isOff()) return;
224     if (mLastPresentTimeInBootClockNs == kDefaultInvalidPresentTimeNs) return;
225 
226     endTimeStampInBootClockNs =
227             endTimeStampInBootClockNs < 0 ? getBootClockTimeNs() : endTimeStampInBootClockNs;
228     auto durationFromLastPresentNs = endTimeStampInBootClockNs - mLastPresentTimeInBootClockNs;
229     durationFromLastPresentNs = durationFromLastPresentNs < 0 ? 0 : durationFromLastPresentNs;
230     if (mDisplayPresentProfile.mCurrentDisplayConfig.mPowerMode == HWC_POWER_MODE_DOZE) {
231         mDisplayPresentProfile.mNumVsync = mTeFrequency;
232 
233         std::scoped_lock lock(mMutex);
234 
235         auto& record = mStatistics[mDisplayPresentProfile];
236         record.mAccumulatedTimeNs += durationFromLastPresentNs;
237         record.mLastTimeStampInBootClockNs = mLastPresentTimeInBootClockNs;
238         mLastPresentTimeInBootClockNs = endTimeStampInBootClockNs;
239         record.mUpdated = true;
240     } else {
241         int numVsync = roundDivide(durationFromLastPresentNs, mTeIntervalNs);
242         mDisplayPresentProfile.mNumVsync =
243                 (mMinimumRefreshRate > 1 ? (mTeFrequency / mMinimumRefreshRate) : mTeFrequency);
244         if (numVsync <= mDisplayPresentProfile.mNumVsync) return;
245 
246         // Ensure that the last vsync should not be included now, since it would be processed for
247         // next update or |onPresent|
248         auto count = (numVsync - 1) / mDisplayPresentProfile.mNumVsync;
249         auto alignedDurationNs = mMaximumFrameIntervalNs * count;
250         {
251             std::scoped_lock lock(mMutex);
252 
253             auto& record = mStatistics[mDisplayPresentProfile];
254             record.mCount += count;
255             record.mAccumulatedTimeNs += alignedDurationNs;
256             mLastPresentTimeInBootClockNs += alignedDurationNs;
257             record.mLastTimeStampInBootClockNs = mLastPresentTimeInBootClockNs;
258             record.mUpdated = true;
259         }
260     }
261 }
262 
263 #ifdef DEBUG_VRR_STATISTICS
updateStatistic()264 int VariableRefreshRateStatistic::updateStatistic() {
265     updateIdleStats();
266     for (const auto& it : mStatistics) {
267         const auto& key = it.first;
268         const auto& value = it.second;
269         ALOGD("%s: power mode = %d, id = %d, birghtness mode = %d, vsync "
270               "= %d : count = %ld, last entry time =  %ld",
271               __func__, key.mCurrentDisplayConfig.mPowerMode,
272               key.mCurrentDisplayConfig.mActiveConfigId, key.mCurrentDisplayConfig.mBrightnessMode,
273               key.mNumVsync, value.mCount, value.mLastTimeStampInBootClockNs);
274     }
275     // Post next update statistics event.
276     mUpdateEvent.mWhenNs = getSteadyClockTimeNs() + mUpdatePeriodNs;
277     mEventQueue->mPriorityQueue.emplace(mUpdateEvent);
278 
279     return NO_ERROR;
280 }
281 #endif
282 
283 } // namespace android::hardware::graphics::composer
284