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