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 <stdlib.h>
18
19 #include "DisplayStateResidencyProvider.h"
20
21 namespace android::hardware::graphics::composer {
22
23 // Currently, the FPS ranges from [1, |kMaxFrameRate| = 120], and the maximum TE
24 // frequency(|kMaxTefrequency|) = 240. We express fps by dividing the maximum TE by the number of
25 // vsync. Here, the numerator is set to |kMaxTefrequency|, fraction reduction is not needed here.
26 const std::set<Fraction<int>> DisplayStateResidencyProvider::kFpsMappingTable =
27 {{240, 240}, {240, 120}, {240, 24}, {240, 10}, {240, 8}, {240, 7},
28 {240, 6}, {240, 5}, {240, 4}, {240, 3}, {240, 2}};
29
30 const std::unordered_set<int> DisplayStateResidencyProvider::kFpsLowPowerModeMappingTable = {1, 30};
31
32 const std::unordered_set<int> DisplayStateResidencyProvider::kActivePowerModes =
33 {HWC2_POWER_MODE_DOZE, HWC2_POWER_MODE_ON};
34
35 namespace {
36
37 static constexpr uint64_t MilliToNano = 1000000;
38
39 }
40
DisplayStateResidencyProvider(std::shared_ptr<CommonDisplayContextProvider> displayContextProvider,std::shared_ptr<StatisticsProvider> statisticsProvider)41 DisplayStateResidencyProvider::DisplayStateResidencyProvider(
42 std::shared_ptr<CommonDisplayContextProvider> displayContextProvider,
43 std::shared_ptr<StatisticsProvider> statisticsProvider)
44 : mDisplayContextProvider(displayContextProvider), mStatisticsProvider(statisticsProvider) {
45 if (parseDisplayStateResidencyPattern()) {
46 generatePowerStatsStates();
47 }
48 mStartStatisticTimeNs = mStatisticsProvider->getStartStatisticTimeNs();
49 }
50
getStateResidency(std::vector<StateResidency> * stats)51 void DisplayStateResidencyProvider::getStateResidency(std::vector<StateResidency>* stats) {
52 mapStatistics();
53
54 int64_t powerStatsTotalTimeNs = aggregateStatistics();
55 #ifdef DEBUG_VRR_POWERSTATS
56 uint64_t statisticDurationNs = getBootClockTimeNs() - mStartStatisticTimeNs;
57 ALOGD("DisplayStateResidencyProvider: total power stats time = %ld ms, time lapse = %ld ms",
58 powerStatsTotalTimeNs / MilliToNano, statisticDurationNs / MilliToNano);
59 if (mLastGetStateResidencyTimeNs != -1) {
60 int64_t timePassedNs = (getSteadyClockTimeNs() - mLastGetStateResidencyTimeNs);
61 int64_t statisticAccumulatedTimeNs = (powerStatsTotalTimeNs - mLastPowerStatsTotalTimeNs);
62 ALOGD("DisplayStateResidencyProvider: The time interval between successive calls to "
63 "getStateResidency() = %ld ms",
64 (timePassedNs / MilliToNano));
65 ALOGD("DisplayStateResidencyProvider: The accumulated statistic time interval between "
66 "successive calls to "
67 "getStateResidency() = %ld ms",
68 (statisticAccumulatedTimeNs / MilliToNano));
69 }
70 mLastGetStateResidencyTimeNs = getSteadyClockTimeNs();
71 mLastPowerStatsTotalTimeNs = powerStatsTotalTimeNs;
72 #endif
73 *stats = mStateResidency;
74 }
75
getStates()76 const std::vector<State>& DisplayStateResidencyProvider::getStates() {
77 return mStates;
78 }
79
mapStatistics()80 void DisplayStateResidencyProvider::mapStatistics() {
81 auto mUpdatedStatistics = mStatisticsProvider->getUpdatedStatistics();
82 #ifdef DEBUG_VRR_POWERSTATS
83 for (const auto& item : mUpdatedStatistics) {
84 ALOGI("DisplayStateResidencyProvider : update key %s value %s",
85 item.first.toString().c_str(), item.second.toString().c_str());
86 }
87 #endif
88 mRemappedStatistics.clear();
89 for (const auto& item : mUpdatedStatistics) {
90 mStatistics[item.first] = item.second;
91 }
92
93 for (const auto& item : mStatistics) {
94 const auto& displayPresentProfile = item.first;
95 PowerStatsPresentProfile powerStatsPresentProfile;
96 if (displayPresentProfile.mNumVsync <
97 0) { // To address the specific scenario of powering off.
98 powerStatsPresentProfile.mFps = -1;
99 mRemappedStatistics[powerStatsPresentProfile] += item.second;
100 mRemappedStatistics[powerStatsPresentProfile].mUpdated = true;
101 continue;
102 }
103 const auto& configId = displayPresentProfile.mCurrentDisplayConfig.mActiveConfigId;
104 powerStatsPresentProfile.mWidth = mDisplayContextProvider->getWidth(configId);
105 powerStatsPresentProfile.mHeight = mDisplayContextProvider->getHeight(configId);
106 powerStatsPresentProfile.mPowerMode =
107 displayPresentProfile.mCurrentDisplayConfig.mPowerMode;
108 powerStatsPresentProfile.mBrightnessMode =
109 displayPresentProfile.mCurrentDisplayConfig.mBrightnessMode;
110 auto teFrequency = mDisplayContextProvider->getTeFrequency(configId);
111 Fraction fps(teFrequency, displayPresentProfile.mNumVsync);
112 if ((kFpsMappingTable.count(fps) > 0)) {
113 powerStatsPresentProfile.mFps = fps.round();
114 mRemappedStatistics[powerStatsPresentProfile] += item.second;
115 mRemappedStatistics[powerStatsPresentProfile].mUpdated = true;
116 } else {
117 // Others.
118 auto key = powerStatsPresentProfile;
119 const auto& value = item.second;
120 key.mFps = 0;
121 mRemappedStatistics[key].mUpdated = true;
122 mRemappedStatistics[key].mCount += value.mCount;
123 mRemappedStatistics[key].mAccumulatedTimeNs += value.mAccumulatedTimeNs;
124 mRemappedStatistics[key].mLastTimeStampInBootClockNs =
125 std::max(mRemappedStatistics[key].mLastTimeStampInBootClockNs,
126 value.mLastTimeStampInBootClockNs);
127 }
128 }
129 }
130
aggregateStatistics()131 uint64_t DisplayStateResidencyProvider::aggregateStatistics() {
132 uint64_t totalTimeNs = 0;
133 for (auto& statistic : mRemappedStatistics) {
134 if (!statistic.second.mUpdated) {
135 continue;
136 }
137 auto it = mPowerStatsPresentProfileToIdMap.find(statistic.first);
138 if (it == mPowerStatsPresentProfileToIdMap.end()) {
139 ALOGE("DisplayStateResidencyProvider %s(): unregistered powerstats state [%s]",
140 __func__, statistic.first.toString().c_str());
141 continue;
142 }
143 int id = it->second;
144 const auto& displayPresentRecord = statistic.second;
145
146 auto& stateResidency = mStateResidency[id];
147 stateResidency.totalStateEntryCount = displayPresentRecord.mCount;
148 stateResidency.lastEntryTimestampMs =
149 displayPresentRecord.mLastTimeStampInBootClockNs / MilliToNano;
150 stateResidency.totalTimeInStateMs = displayPresentRecord.mAccumulatedTimeNs / MilliToNano;
151 statistic.second.mUpdated = false;
152 totalTimeNs += displayPresentRecord.mAccumulatedTimeNs;
153 }
154 return totalTimeNs;
155 }
156
generatePowerStatsStates()157 void DisplayStateResidencyProvider::generatePowerStatsStates() {
158 auto configs = mDisplayContextProvider->getDisplayConfigs();
159 if (!configs) return;
160 std::set<PowerStatsPresentProfile> powerStatsPresentProfileCandidates;
161 PowerStatsPresentProfile powerStatsPresentProfile;
162
163 // Generate a list of potential DisplayConfigProfiles.
164 // Include the special case 'OFF'.
165 powerStatsPresentProfile.mPowerMode = HWC2_POWER_MODE_OFF;
166 powerStatsPresentProfileCandidates.insert(powerStatsPresentProfile);
167 for (auto powerMode : kActivePowerModes) {
168 powerStatsPresentProfile.mPowerMode = powerMode;
169 for (int brightnesrMode = static_cast<int>(BrightnessMode::kNormalBrightnessMode);
170 brightnesrMode < BrightnessMode::kInvalidBrightnessMode; ++brightnesrMode) {
171 powerStatsPresentProfile.mBrightnessMode = static_cast<BrightnessMode>(brightnesrMode);
172 for (const auto& config : *configs) {
173 powerStatsPresentProfile.mWidth = mDisplayContextProvider->getWidth(config.first);
174 powerStatsPresentProfile.mHeight = mDisplayContextProvider->getHeight(config.first);
175 // Handle the special case LPM(Low Power Mode).
176 if (powerMode == HWC_POWER_MODE_DOZE) {
177 for (auto fps : kFpsLowPowerModeMappingTable) {
178 powerStatsPresentProfile.mFps = fps;
179 powerStatsPresentProfileCandidates.insert(powerStatsPresentProfile);
180 }
181 continue;
182 }
183 // Include the special case: other fps.
184 powerStatsPresentProfile.mFps = 0;
185 powerStatsPresentProfileCandidates.insert(powerStatsPresentProfile);
186 for (auto fps : kFpsMappingTable) {
187 powerStatsPresentProfile.mFps = fps.round();
188 powerStatsPresentProfileCandidates.insert(powerStatsPresentProfile);
189 }
190 }
191 }
192 }
193
194 auto uniqueComp = [](const std::pair<std::string, PowerStatsPresentProfile>& v1,
195 const std::pair<std::string, PowerStatsPresentProfile>& v2) {
196 return v1.first < v2.first;
197 };
198
199 // Transform candidate DisplayConfigProfiles into a string and eliminate duplicates.
200 std::set<std::pair<std::string, PowerStatsPresentProfile>, decltype(uniqueComp)> uniqueStates;
201 for (const auto& powerStatsPresentProfile : powerStatsPresentProfileCandidates) {
202 std::string stateName;
203 mPowerStatsPresentProfileTokenGenerator.setPowerStatsPresentProfile(
204 &powerStatsPresentProfile);
205 for (const auto& pattern : mDisplayStateResidencyPattern) {
206 const auto token = mPowerStatsPresentProfileTokenGenerator.generateToken(pattern.first);
207 if (token.has_value()) {
208 stateName += token.value();
209 // Handle special case when mode is 'OFF'.
210 if (pattern.first == "mode" && token.value() == "OFF") {
211 break;
212 }
213 } else {
214 ALOGE("DisplayStateResidencyProvider %s(): cannot find token with label %s",
215 __func__, pattern.first.c_str());
216 continue;
217 }
218 stateName += pattern.second;
219 }
220 uniqueStates.insert(std::make_pair(stateName, powerStatsPresentProfile));
221 }
222
223 auto sortComp = [](const std::pair<std::string, PowerStatsPresentProfile>& v1,
224 const std::pair<std::string, PowerStatsPresentProfile>& v2) {
225 return v1.second < v2.second;
226 };
227 std::set<std::pair<std::string, PowerStatsPresentProfile>, decltype(sortComp)> sortedStates;
228 // Sort power stats according to a predefined order.
229 std::for_each(uniqueStates.begin(), uniqueStates.end(),
230 [&](const std::pair<std::string, PowerStatsPresentProfile>& item) {
231 sortedStates.insert(item);
232 });
233
234 // Sort and assign a unique identifier to each state string.
235 mStateResidency.resize(sortedStates.size());
236 int id = 0;
237 int index = 0;
238 for (const auto& state : sortedStates) {
239 mStates.push_back({id, state.first});
240 mPowerStatsPresentProfileToIdMap[state.second] = id;
241 mStateResidency[index++].id = id;
242 ++id;
243 }
244
245 #ifdef DEBUG_VRR_POWERSTATS
246 for (const auto& state : mStates) {
247 ALOGI("DisplayStateResidencyProvider state id = %d, content = %s, len = %ld", state.id,
248 state.name.c_str(), state.name.length());
249 }
250 #endif
251 }
252
parseDisplayStateResidencyPattern()253 bool DisplayStateResidencyProvider::parseDisplayStateResidencyPattern() {
254 size_t start, end;
255 start = 0;
256 end = -1;
257 while (true) {
258 start = kDisplayStateResidencyPattern.find_first_of(kTokenLabelStart, end + 1);
259 if (start == std::string::npos) {
260 break;
261 }
262 ++start;
263 end = kDisplayStateResidencyPattern.find_first_of(kTokenLabelEnd, start);
264 if (end == std::string::npos) {
265 break;
266 }
267 std::string tokenLabel(kDisplayStateResidencyPattern.substr(start, end - start));
268
269 start = kDisplayStateResidencyPattern.find_first_of(kDelimiterStart, end + 1);
270 if (start == std::string::npos) {
271 break;
272 }
273 ++start;
274 end = kDisplayStateResidencyPattern.find_first_of(kDelimiterEnd, start);
275 if (end == std::string::npos) {
276 break;
277 }
278 std::string delimiter(kDisplayStateResidencyPattern.substr(start, end - start));
279 mDisplayStateResidencyPattern.emplace_back(std::make_pair(tokenLabel, delimiter));
280 }
281 return (end == kDisplayStateResidencyPattern.length() - 1);
282 }
283
284 } // namespace android::hardware::graphics::composer
285