1 /*
2 * Copyright 2021 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 #define LOG_TAG "powerhal-libperfmgr"
18 #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
19
20 #include "PowerHintSession.h"
21
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/parsedouble.h>
25 #include <android-base/properties.h>
26 #include <android-base/stringprintf.h>
27 #include <private/android_filesystem_config.h>
28 #include <sys/syscall.h>
29 #include <time.h>
30 #include <utils/Trace.h>
31
32 #include <atomic>
33
34 #include "GpuCalculationHelpers.h"
35 #include "tests/mocks/MockHintManager.h"
36 #include "tests/mocks/MockPowerSessionManager.h"
37
38 namespace aidl {
39 namespace google {
40 namespace hardware {
41 namespace power {
42 namespace impl {
43 namespace pixel {
44
45 using ::android::base::StringPrintf;
46 using ::android::perfmgr::AdpfConfig;
47 using ::android::perfmgr::HintManager;
48 using std::chrono::duration_cast;
49 using std::chrono::nanoseconds;
50
51 using std::operator""ms;
52
53 namespace {
54
55 static std::atomic<int64_t> sSessionIDCounter{0};
56
ns_to_100us(int64_t ns)57 static inline int64_t ns_to_100us(int64_t ns) {
58 return ns / 100000;
59 }
60
61 static const char systemSessionCheckPath[] = "/proc/vendor_sched/is_tgid_system_ui";
62 static const bool systemSessionCheckNodeExist = access(systemSessionCheckPath, W_OK) == 0;
63 static constexpr int32_t kTargetDurationChangeThreshold = 30; // Percentage change threshold
64
65 } // namespace
66
67 template <class HintManagerT, class PowerSessionManagerT>
convertWorkDurationToBoostByPid(const std::vector<WorkDuration> & actualDurations)68 int64_t PowerHintSession<HintManagerT, PowerSessionManagerT>::convertWorkDurationToBoostByPid(
69 const std::vector<WorkDuration> &actualDurations) {
70 std::shared_ptr<AdpfConfig> adpfConfig = getAdpfProfile();
71 const nanoseconds &targetDuration = mDescriptor->targetNs;
72 int64_t &integral_error = mDescriptor->integral_error;
73 int64_t &previous_error = mDescriptor->previous_error;
74 uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
75 uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
76 uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
77 int64_t targetDurationNanos = (int64_t)targetDuration.count();
78 int64_t length = actualDurations.size();
79 int64_t p_start =
80 samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
81 int64_t i_start =
82 samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
83 int64_t d_start =
84 samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
85 int64_t dt = ns_to_100us(targetDurationNanos);
86 int64_t err_sum = 0;
87 int64_t derivative_sum = 0;
88 for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
89 int64_t actualDurationNanos = actualDurations[i].durationNanos;
90 if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
91 ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
92 actualDurationNanos, targetDurationNanos);
93 }
94 // PID control algorithm
95 int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
96 if (i >= d_start) {
97 derivative_sum += error - previous_error;
98 }
99 if (i >= p_start) {
100 err_sum += error;
101 }
102 if (i >= i_start) {
103 integral_error += error * dt;
104 integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error);
105 integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error);
106 }
107 previous_error = error;
108 }
109
110 auto pid_pu_active = adpfConfig->mPidPu;
111 if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
112 auto hboostPidPu = std::min(adpfConfig->mHBoostSevereJankPidPu.value(), adpfConfig->mPidPu);
113 if (mJankyLevel == SessionJankyLevel::MODERATE) {
114 double JankyFactor =
115 mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value()
116 ? 0.0
117 : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) *
118 1.0 /
119 (adpfConfig->mHBoostSevereJankThreshold.value() -
120 adpfConfig->mHBoostModerateJankThreshold.value());
121 pid_pu_active = adpfConfig->mPidPu + JankyFactor * (hboostPidPu - adpfConfig->mPidPu);
122 } else if (mJankyLevel == SessionJankyLevel::SEVERE) {
123 pid_pu_active = hboostPidPu;
124 }
125 ATRACE_INT(mAppDescriptorTrace->trace_hboost_pid_pu.c_str(), pid_pu_active * 100);
126 }
127 int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : pid_pu_active) *
128 err_sum / (length - p_start));
129 int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error);
130 int64_t dOut =
131 static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
132 derivative_sum / dt / (length - d_start));
133
134 int64_t output = pOut + iOut + dOut;
135 ATRACE_INT(mAppDescriptorTrace->trace_pid_err.c_str(), err_sum / (length - p_start));
136 ATRACE_INT(mAppDescriptorTrace->trace_pid_integral.c_str(), integral_error);
137 ATRACE_INT(mAppDescriptorTrace->trace_pid_derivative.c_str(),
138 derivative_sum / dt / (length - d_start));
139 ATRACE_INT(mAppDescriptorTrace->trace_pid_pOut.c_str(), pOut);
140 ATRACE_INT(mAppDescriptorTrace->trace_pid_iOut.c_str(), iOut);
141 ATRACE_INT(mAppDescriptorTrace->trace_pid_dOut.c_str(), dOut);
142 ATRACE_INT(mAppDescriptorTrace->trace_pid_output.c_str(), output);
143 return output;
144 }
145
146 template <class HintManagerT, class PowerSessionManagerT>
getProcessTag(int32_t tgid)147 ProcessTag PowerHintSession<HintManagerT, PowerSessionManagerT>::getProcessTag(int32_t tgid) {
148 if (!systemSessionCheckNodeExist) {
149 ALOGD("Vendor system session checking node doesn't exist");
150 return ProcessTag::DEFAULT;
151 }
152
153 int flags = O_WRONLY | O_TRUNC | O_CLOEXEC;
154 ::android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(systemSessionCheckPath, flags)));
155 if (fd == -1) {
156 ALOGW("Can't open system session checking node %s", systemSessionCheckPath);
157 return ProcessTag::DEFAULT;
158 }
159 // The file-write return status is true if the task belongs to systemUI or Launcher. Other task
160 // or invalid tgid will return a false value.
161 auto stat = ::android::base::WriteStringToFd(std::to_string(tgid), fd);
162 ALOGD("System session checking result: %d - %d", tgid, stat);
163 if (stat) {
164 return ProcessTag::SYSTEM_UI;
165 } else {
166 return ProcessTag::DEFAULT;
167 }
168 }
169
170 template <class HintManagerT, class PowerSessionManagerT>
PowerHintSession(int32_t tgid,int32_t uid,const std::vector<int32_t> & threadIds,int64_t durationNs,SessionTag tag)171 PowerHintSession<HintManagerT, PowerSessionManagerT>::PowerHintSession(
172 int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNs,
173 SessionTag tag)
174 : mPSManager(PowerSessionManagerT::getInstance()),
175 mSessionId(++sSessionIDCounter),
176 mSessTag(tag),
177 mProcTag(getProcessTag(tgid)),
178 mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64 "-%s-%" PRId32, tgid, uid,
179 mSessionId, toString(tag).c_str(), static_cast<int32_t>(mProcTag))),
180 mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid, threadIds, tag,
181 std::chrono::nanoseconds(durationNs))),
182 mAppDescriptorTrace(std::make_shared<AppDescriptorTrace>(mIdString)),
183 mAdpfProfile(mProcTag != ProcessTag::DEFAULT
184 ? HintManager::GetInstance()->GetAdpfProfile(toString(mProcTag))
185 : HintManager::GetInstance()->GetAdpfProfile(toString(mSessTag))),
186 mOnAdpfUpdate(
187 [this](const std::shared_ptr<AdpfConfig> config) { this->setAdpfProfile(config); }),
188 mSessionRecords(getAdpfProfile()->mHeuristicBoostOn.has_value() &&
189 getAdpfProfile()->mHeuristicBoostOn.value()
190 ? std::make_unique<SessionRecords>(
191 getAdpfProfile()->mMaxRecordsNum.value(),
192 getAdpfProfile()->mJankCheckTimeFactor.value())
193 : nullptr) {
194 ATRACE_CALL();
195 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
196 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), mDescriptor->is_active.load());
197
198 if (mProcTag != ProcessTag::DEFAULT) {
199 HintManager::GetInstance()->RegisterAdpfUpdateEvent(toString(mProcTag), &mOnAdpfUpdate);
200 } else {
201 HintManager::GetInstance()->RegisterAdpfUpdateEvent(toString(mSessTag), &mOnAdpfUpdate);
202 }
203
204 mLastUpdatedTime = std::chrono::steady_clock::now();
205 mPSManager->addPowerSession(mIdString, mDescriptor, mAppDescriptorTrace, threadIds, mProcTag);
206 // init boost
207 auto adpfConfig = getAdpfProfile();
208 mPSManager->voteSet(
209 mSessionId, AdpfVoteType::CPU_LOAD_RESET, adpfConfig->mUclampMinLoadReset, kUclampMax,
210 std::chrono::steady_clock::now(),
211 duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor / 2.0));
212
213 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, adpfConfig->mUclampMinInit,
214 kUclampMax, std::chrono::steady_clock::now(), mDescriptor->targetNs);
215 ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
216 }
217
218 template <class HintManagerT, class PowerSessionManagerT>
~PowerHintSession()219 PowerHintSession<HintManagerT, PowerSessionManagerT>::~PowerHintSession() {
220 ATRACE_CALL();
221 close();
222 ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
223 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), 0);
224 ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), 0);
225 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), 0);
226 }
227
228 template <class HintManagerT, class PowerSessionManagerT>
isAppSession()229 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isAppSession() {
230 // Check if uid is in range reserved for applications
231 return mDescriptor->uid >= AID_APP_START;
232 }
233
234 template <class HintManagerT, class PowerSessionManagerT>
updatePidControlVariable(int pidControlVariable,bool updateVote)235 void PowerHintSession<HintManagerT, PowerSessionManagerT>::updatePidControlVariable(
236 int pidControlVariable, bool updateVote) {
237 mDescriptor->pidControlVariable = pidControlVariable;
238 if (updateVote) {
239 auto adpfConfig = getAdpfProfile();
240 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, pidControlVariable,
241 kUclampMax, std::chrono::steady_clock::now(),
242 std::max(duration_cast<nanoseconds>(mDescriptor->targetNs *
243 adpfConfig->mStaleTimeFactor),
244 nanoseconds(adpfConfig->mReportingRateLimitNs) * 2));
245 }
246 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), pidControlVariable);
247 }
248
249 template <class HintManagerT, class PowerSessionManagerT>
tryToSendPowerHint(std::string hint)250 void PowerHintSession<HintManagerT, PowerSessionManagerT>::tryToSendPowerHint(std::string hint) {
251 if (!mSupportedHints[hint].has_value()) {
252 mSupportedHints[hint] = HintManagerT::GetInstance()->IsHintSupported(hint);
253 }
254 if (mSupportedHints[hint].value()) {
255 HintManagerT::GetInstance()->DoHint(hint);
256 }
257 }
258
259 template <class HintManagerT, class PowerSessionManagerT>
dumpToStream(std::ostream & stream)260 void PowerHintSession<HintManagerT, PowerSessionManagerT>::dumpToStream(std::ostream &stream) {
261 std::scoped_lock lock{mPowerHintSessionLock};
262 stream << "ID.Min.Act.Timeout(" << mIdString;
263 stream << ", " << mDescriptor->pidControlVariable;
264 stream << ", " << mDescriptor->is_active;
265 stream << ", " << isTimeout() << ")";
266 }
267
268 template <class HintManagerT, class PowerSessionManagerT>
pause()269 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::pause() {
270 std::scoped_lock lock{mPowerHintSessionLock};
271 if (mSessionClosed) {
272 ALOGE("Error: session is dead");
273 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
274 }
275 if (!mDescriptor->is_active.load())
276 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
277 // Reset to default uclamp value.
278 mPSManager->setThreadsFromPowerSession(mSessionId, {}, mProcTag);
279 mDescriptor->is_active.store(false);
280 mPSManager->pause(mSessionId);
281 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), false);
282 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
283 return ndk::ScopedAStatus::ok();
284 }
285
286 template <class HintManagerT, class PowerSessionManagerT>
resume()287 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::resume() {
288 std::scoped_lock lock{mPowerHintSessionLock};
289 if (mSessionClosed) {
290 ALOGE("Error: session is dead");
291 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
292 }
293 if (mDescriptor->is_active.load()) {
294 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
295 }
296 mPSManager->setThreadsFromPowerSession(mSessionId, mDescriptor->thread_ids, mProcTag);
297 mDescriptor->is_active.store(true);
298 // resume boost
299 mPSManager->resume(mSessionId);
300 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), true);
301 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), mDescriptor->pidControlVariable);
302 return ndk::ScopedAStatus::ok();
303 }
304
305 template <class HintManagerT, class PowerSessionManagerT>
close()306 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::close() {
307 std::scoped_lock lock{mPowerHintSessionLock};
308 if (mSessionClosed) {
309 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
310 }
311 mSessionClosed = true;
312 // Remove the session from PowerSessionManager first to avoid racing.
313 mPSManager->removePowerSession(mSessionId, mProcTag);
314 mDescriptor->is_active.store(false);
315
316 if (mProcTag != ProcessTag::DEFAULT) {
317 HintManager::GetInstance()->UnregisterAdpfUpdateEvent(toString(mProcTag), &mOnAdpfUpdate);
318 } else {
319 HintManager::GetInstance()->UnregisterAdpfUpdateEvent(toString(mSessTag), &mOnAdpfUpdate);
320 }
321 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
322 return ndk::ScopedAStatus::ok();
323 }
324
325 template <class HintManagerT, class PowerSessionManagerT>
updateTargetWorkDuration(int64_t targetDurationNanos)326 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::updateTargetWorkDuration(
327 int64_t targetDurationNanos) {
328 std::scoped_lock lock{mPowerHintSessionLock};
329 if (mSessionClosed) {
330 ALOGE("Error: session is dead");
331 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
332 }
333 if (targetDurationNanos <= 0) {
334 ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
335 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
336 }
337 targetDurationNanos = targetDurationNanos * getAdpfProfile()->mTargetTimeFactor;
338
339 // Reset session records and heuristic boost states when the percentage change of target
340 // duration is over the threshold.
341 if (targetDurationNanos != mDescriptor->targetNs.count() &&
342 getAdpfProfile()->mHeuristicBoostOn.has_value() &&
343 getAdpfProfile()->mHeuristicBoostOn.value()) {
344 auto lastTargetNs = mDescriptor->targetNs.count();
345 if (abs(targetDurationNanos - lastTargetNs) >
346 lastTargetNs / 100 * kTargetDurationChangeThreshold) {
347 resetSessionHeuristicStates();
348 }
349 }
350
351 mDescriptor->targetNs = std::chrono::nanoseconds(targetDurationNanos);
352 mPSManager->updateTargetWorkDuration(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT,
353 mDescriptor->targetNs);
354 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), targetDurationNanos);
355
356 return ndk::ScopedAStatus::ok();
357 }
358
359 template <class HintManagerT, class PowerSessionManagerT>
resetSessionHeuristicStates()360 void PowerHintSession<HintManagerT, PowerSessionManagerT>::resetSessionHeuristicStates() {
361 mSessionRecords->resetRecords();
362 mJankyLevel = SessionJankyLevel::LIGHT;
363 mJankyFrameNum = 0;
364 ATRACE_INT(mAppDescriptorTrace->trace_hboost_janky_level.c_str(),
365 static_cast<int32_t>(mJankyLevel));
366 ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), mJankyFrameNum);
367 ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), 0);
368 ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), 0);
369 ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(), false);
370 }
371
372 template <class HintManagerT, class PowerSessionManagerT>
updateSessionJankState(SessionJankyLevel oldState,int32_t numOfJankFrames,double durationVariance,bool isLowFPS)373 SessionJankyLevel PowerHintSession<HintManagerT, PowerSessionManagerT>::updateSessionJankState(
374 SessionJankyLevel oldState, int32_t numOfJankFrames, double durationVariance,
375 bool isLowFPS) {
376 SessionJankyLevel newState = SessionJankyLevel::LIGHT;
377 if (isLowFPS) {
378 newState = SessionJankyLevel::LIGHT;
379 return newState;
380 }
381
382 auto adpfConfig = getAdpfProfile();
383 if (numOfJankFrames < adpfConfig->mHBoostModerateJankThreshold.value()) {
384 if (oldState == SessionJankyLevel::LIGHT ||
385 durationVariance < adpfConfig->mHBoostOffMaxAvgDurRatio.value()) {
386 newState = SessionJankyLevel::LIGHT;
387 } else {
388 newState = SessionJankyLevel::MODERATE;
389 }
390 } else if (numOfJankFrames < adpfConfig->mHBoostSevereJankThreshold.value()) {
391 newState = SessionJankyLevel::MODERATE;
392 } else {
393 newState = SessionJankyLevel::SEVERE;
394 }
395
396 return newState;
397 }
398
399 template <class HintManagerT, class PowerSessionManagerT>
updateHeuristicBoost()400 void PowerHintSession<HintManagerT, PowerSessionManagerT>::updateHeuristicBoost() {
401 auto maxDurationUs = mSessionRecords->getMaxDuration(); // micro seconds
402 auto avgDurationUs = mSessionRecords->getAvgDuration(); // micro seconds
403 auto numOfJankFrames = mSessionRecords->getNumOfMissedCycles();
404
405 if (!maxDurationUs.has_value() || !avgDurationUs.has_value() || avgDurationUs.value() <= 0) {
406 // No history data stored or invalid average duration.
407 return;
408 }
409
410 auto maxToAvgRatio = maxDurationUs.value() * 1.0 / avgDurationUs.value();
411 auto isLowFPS =
412 mSessionRecords->isLowFrameRate(getAdpfProfile()->mLowFrameRateThreshold.value());
413
414 mJankyLevel = updateSessionJankState(mJankyLevel, numOfJankFrames, maxToAvgRatio, isLowFPS);
415 mJankyFrameNum = numOfJankFrames;
416
417 ATRACE_INT(mAppDescriptorTrace->trace_hboost_janky_level.c_str(),
418 static_cast<int32_t>(mJankyLevel));
419 ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), mJankyFrameNum);
420 ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), avgDurationUs.value());
421 ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), maxDurationUs.value());
422 ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(), isLowFPS);
423 if (mSessTag == SessionTag::SURFACEFLINGER) {
424 ATRACE_INT(mAppDescriptorTrace->trace_game_mode_fps.c_str(),
425 mSessionRecords->getLatestFPS());
426 ATRACE_INT(mAppDescriptorTrace->trace_game_mode_fps_jitters.c_str(),
427 mSessionRecords->getNumOfFPSJitters());
428 }
429 }
430
431 template <class HintManagerT, class PowerSessionManagerT>
reportActualWorkDuration(const std::vector<WorkDuration> & actualDurations)432 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::reportActualWorkDuration(
433 const std::vector<WorkDuration> &actualDurations) {
434 std::scoped_lock lock{mPowerHintSessionLock};
435 if (mSessionClosed) {
436 ALOGE("Error: session is dead");
437 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
438 }
439 if (mDescriptor->targetNs.count() == 0LL) {
440 ALOGE("Expect to call updateTargetWorkDuration() first.");
441 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
442 }
443 if (actualDurations.empty()) {
444 ALOGE("Error: durations shouldn't be empty.");
445 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
446 }
447 if (!mDescriptor->is_active.load()) {
448 ALOGE("Error: shouldn't report duration during pause state.");
449 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
450 }
451 auto adpfConfig = getAdpfProfile();
452 mDescriptor->update_count++;
453 bool isFirstFrame = isTimeout();
454 ATRACE_INT(mAppDescriptorTrace->trace_batch_size.c_str(), actualDurations.size());
455 ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), actualDurations.back().durationNanos);
456 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
457 ATRACE_INT(mAppDescriptorTrace->trace_hint_count.c_str(), mDescriptor->update_count);
458 ATRACE_INT(mAppDescriptorTrace->trace_hint_overtime.c_str(),
459 actualDurations.back().durationNanos - mDescriptor->targetNs.count() > 0);
460 ATRACE_INT(mAppDescriptorTrace->trace_is_first_frame.c_str(), (isFirstFrame) ? (1) : (0));
461 ATRACE_INT(mAppDescriptorTrace->trace_cpu_duration.c_str(),
462 actualDurations.back().cpuDurationNanos);
463 ATRACE_INT(mAppDescriptorTrace->trace_gpu_duration.c_str(),
464 actualDurations.back().gpuDurationNanos);
465
466 mLastUpdatedTime = std::chrono::steady_clock::now();
467 if (isFirstFrame) {
468 mPSManager->updateUniversalBoostMode();
469 }
470
471 mPSManager->disableBoosts(mSessionId);
472
473 if (!adpfConfig->mPidOn) {
474 updatePidControlVariable(adpfConfig->mUclampMinHigh);
475 return ndk::ScopedAStatus::ok();
476 }
477
478 bool hboostEnabled =
479 adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value();
480
481 if (hboostEnabled) {
482 FrameBuckets newFramesInBuckets;
483 mSessionRecords->addReportedDurations(
484 actualDurations, mDescriptor->targetNs.count(), newFramesInBuckets,
485 mSessTag == SessionTag::SURFACEFLINGER && mPSManager->getGameModeEnableState());
486 mPSManager->updateHboostStatistics(mSessionId, mJankyLevel, actualDurations.size());
487 mPSManager->updateFrameBuckets(mSessionId, newFramesInBuckets);
488 updateHeuristicBoost();
489 }
490
491 int64_t output = convertWorkDurationToBoostByPid(actualDurations);
492
493 // Apply to all the threads in the group
494 auto uclampMinFloor = adpfConfig->mUclampMinLow;
495 auto uclampMinCeiling = adpfConfig->mUclampMinHigh;
496 if (hboostEnabled) {
497 auto hboostMinUclampMinFloor = std::max(
498 adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().first);
499 auto hboostMaxUclampMinFloor = std::max(
500 adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().second);
501 auto hboostMinUclampMinCeiling = std::max(
502 adpfConfig->mUclampMinHigh, adpfConfig->mHBoostUclampMinCeilingRange.value().first);
503 auto hboostMaxUclampMinCeiling =
504 std::max(adpfConfig->mUclampMinHigh,
505 adpfConfig->mHBoostUclampMinCeilingRange.value().second);
506 if (mJankyLevel == SessionJankyLevel::MODERATE) {
507 double JankyFactor =
508 mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value()
509 ? 0.0
510 : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) *
511 1.0 /
512 (adpfConfig->mHBoostSevereJankThreshold.value() -
513 adpfConfig->mHBoostModerateJankThreshold.value());
514 uclampMinFloor = hboostMinUclampMinFloor +
515 (hboostMaxUclampMinFloor - hboostMinUclampMinFloor) * JankyFactor;
516 uclampMinCeiling =
517 hboostMinUclampMinCeiling +
518 (hboostMaxUclampMinCeiling - hboostMinUclampMinCeiling) * JankyFactor;
519 } else if (mJankyLevel == SessionJankyLevel::SEVERE) {
520 uclampMinFloor = hboostMaxUclampMinFloor;
521 uclampMinCeiling = hboostMaxUclampMinCeiling;
522 }
523 ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_ceiling.c_str(), uclampMinCeiling);
524 ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_floor.c_str(), uclampMinFloor);
525 }
526
527 int next_min = std::min(static_cast<int>(uclampMinCeiling),
528 mDescriptor->pidControlVariable + static_cast<int>(output));
529 next_min = std::max(static_cast<int>(uclampMinFloor), next_min);
530
531 updatePidControlVariable(next_min);
532
533 if (!adpfConfig->mGpuBoostOn.value_or(false) || !adpfConfig->mGpuBoostCapacityMax ||
534 !actualDurations.back().gpuDurationNanos) {
535 return ndk::ScopedAStatus::ok();
536 }
537
538 auto const gpu_freq = mPSManager->gpuFrequency();
539 if (!gpu_freq) {
540 return ndk::ScopedAStatus::ok();
541 }
542 auto const additional_gpu_capacity =
543 calculate_capacity(actualDurations.back(), mDescriptor->targetNs, *gpu_freq);
544 ATRACE_INT(mAppDescriptorTrace->trace_gpu_capacity.c_str(),
545 static_cast<int>(additional_gpu_capacity));
546
547 auto const additional_gpu_capacity_clamped = std::clamp(
548 additional_gpu_capacity, Cycles(0), Cycles(*adpfConfig->mGpuBoostCapacityMax));
549
550 mPSManager->voteSet(
551 mSessionId, AdpfVoteType::GPU_CAPACITY, additional_gpu_capacity_clamped,
552 std::chrono::steady_clock::now(),
553 duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor));
554
555 return ndk::ScopedAStatus::ok();
556 }
557
558 template <class HintManagerT, class PowerSessionManagerT>
sendHint(SessionHint hint)559 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::sendHint(
560 SessionHint hint) {
561 {
562 std::scoped_lock lock{mPowerHintSessionLock};
563 if (mSessionClosed) {
564 ALOGE("Error: session is dead");
565 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
566 }
567 if (mDescriptor->targetNs.count() == 0LL) {
568 ALOGE("Expect to call updateTargetWorkDuration() first.");
569 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
570 }
571 auto adpfConfig = getAdpfProfile();
572
573 switch (hint) {
574 case SessionHint::CPU_LOAD_UP:
575 updatePidControlVariable(mDescriptor->pidControlVariable);
576 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_UP,
577 adpfConfig->mUclampMinLoadUp, kUclampMax,
578 std::chrono::steady_clock::now(), mDescriptor->targetNs * 2);
579 break;
580 case SessionHint::CPU_LOAD_DOWN:
581 updatePidControlVariable(adpfConfig->mUclampMinLow);
582 break;
583 case SessionHint::CPU_LOAD_RESET:
584 updatePidControlVariable(
585 std::max(adpfConfig->mUclampMinInit,
586 static_cast<uint32_t>(mDescriptor->pidControlVariable)),
587 false);
588 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESET,
589 adpfConfig->mUclampMinLoadReset, kUclampMax,
590 std::chrono::steady_clock::now(),
591 duration_cast<nanoseconds>(mDescriptor->targetNs *
592 adpfConfig->mStaleTimeFactor / 2.0));
593 break;
594 case SessionHint::CPU_LOAD_RESUME:
595 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESUME,
596 mDescriptor->pidControlVariable, kUclampMax,
597 std::chrono::steady_clock::now(),
598 duration_cast<nanoseconds>(mDescriptor->targetNs *
599 adpfConfig->mStaleTimeFactor / 2.0));
600 break;
601 case SessionHint::POWER_EFFICIENCY:
602 setModeLocked(SessionMode::POWER_EFFICIENCY, true);
603 break;
604 case SessionHint::GPU_LOAD_UP:
605 mPSManager->voteSet(mSessionId, AdpfVoteType::GPU_LOAD_UP,
606 Cycles(adpfConfig->mGpuCapacityLoadUpHeadroom),
607 std::chrono::steady_clock::now(), mDescriptor->targetNs);
608 break;
609 case SessionHint::GPU_LOAD_DOWN:
610 // TODO(kevindubois): add impl
611 break;
612 case SessionHint::GPU_LOAD_RESET:
613 // TODO(kevindubois): add impl
614 break;
615 case SessionHint::CPU_LOAD_SPIKE:
616 // TODO(mattbuckley): add impl
617 break;
618 case SessionHint::GPU_LOAD_SPIKE:
619 // TODO(kevindubois): add impl
620 break;
621 default:
622 ALOGE("Error: hint is invalid");
623 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
624 }
625 mLastUpdatedTime = std::chrono::steady_clock::now();
626 }
627 // Don't hold a lock (mPowerHintSession) while DoHint will try to take another
628 // lock(NodeLooperThread).
629 tryToSendPowerHint(toString(hint));
630 return ndk::ScopedAStatus::ok();
631 }
632
633 template <class HintManagerT, class PowerSessionManagerT>
setMode(SessionMode mode,bool enabled)634 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setMode(SessionMode mode,
635 bool enabled) {
636 std::scoped_lock lock{mPowerHintSessionLock};
637 return setModeLocked(mode, enabled);
638 }
639
640 template <class HintManagerT, class PowerSessionManagerT>
setModeLocked(SessionMode mode,bool enabled)641 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setModeLocked(
642 SessionMode mode, bool enabled) {
643 if (mSessionClosed) {
644 ALOGE("Error: session is dead");
645 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
646 }
647
648 switch (mode) {
649 case SessionMode::POWER_EFFICIENCY:
650 mPSManager->setPreferPowerEfficiency(mSessionId, enabled);
651 break;
652 default:
653 ALOGE("Error: mode is invalid");
654 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
655 }
656
657 mModes[static_cast<size_t>(mode)] = enabled;
658 ATRACE_INT(mAppDescriptorTrace->trace_modes[static_cast<size_t>(mode)].c_str(), enabled);
659 mLastUpdatedTime = std::chrono::steady_clock::now();
660 return ndk::ScopedAStatus::ok();
661 }
662
663 template <class HintManagerT, class PowerSessionManagerT>
setThreads(const std::vector<int32_t> & threadIds)664 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setThreads(
665 const std::vector<int32_t> &threadIds) {
666 std::scoped_lock lock{mPowerHintSessionLock};
667 if (mSessionClosed) {
668 ALOGE("Error: session is dead");
669 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
670 }
671 if (threadIds.empty()) {
672 ALOGE("Error: threadIds should not be empty");
673 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
674 }
675 mDescriptor->thread_ids = threadIds;
676 mPSManager->setThreadsFromPowerSession(mSessionId, threadIds, mProcTag);
677 // init boost
678 updatePidControlVariable(getAdpfProfile()->mUclampMinInit);
679 return ndk::ScopedAStatus::ok();
680 }
681
682 template <class HintManagerT, class PowerSessionManagerT>
getSessionConfig(SessionConfig * _aidl_return)683 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionConfig(
684 SessionConfig *_aidl_return) {
685 _aidl_return->id = mSessionId;
686 return ndk::ScopedAStatus::ok();
687 }
688
689 template <class HintManagerT, class PowerSessionManagerT>
getSessionTag() const690 SessionTag PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionTag() const {
691 return mSessTag;
692 }
693
694 template <class HintManagerT, class PowerSessionManagerT>
695 const std::shared_ptr<AdpfConfig>
getAdpfProfile() const696 PowerHintSession<HintManagerT, PowerSessionManagerT>::getAdpfProfile() const {
697 if (!mAdpfProfile) {
698 return mProcTag == ProcessTag::DEFAULT
699 ? HintManager::GetInstance()->GetAdpfProfile(toString(mSessTag))
700 : HintManager::GetInstance()->GetAdpfProfile(toString(mProcTag));
701 }
702 return mAdpfProfile;
703 }
704
705 template <class HintManagerT, class PowerSessionManagerT>
setAdpfProfile(const std::shared_ptr<AdpfConfig> profile)706 void PowerHintSession<HintManagerT, PowerSessionManagerT>::setAdpfProfile(
707 const std::shared_ptr<AdpfConfig> profile) {
708 // Must prevent profile from being changed in a binder call duration.
709 std::scoped_lock lock{mPowerHintSessionLock};
710 mAdpfProfile = profile;
711 }
712
toString() const713 std::string AppHintDesc::toString() const {
714 std::string out = StringPrintf("session %" PRId64 "\n", sessionId);
715 out.append(
716 StringPrintf(" duration: %" PRId64 " ns\n", static_cast<int64_t>(targetNs.count())));
717 out.append(StringPrintf(" uclamp.min: %d \n", pidControlVariable));
718 out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid));
719 return out;
720 }
721
722 template <class HintManagerT, class PowerSessionManagerT>
isTimeout()723 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isTimeout() {
724 auto now = std::chrono::steady_clock::now();
725 time_point<steady_clock> staleTime =
726 mLastUpdatedTime +
727 nanoseconds(static_cast<int64_t>(mDescriptor->targetNs.count() *
728 getAdpfProfile()->mStaleTimeFactor));
729 return now >= staleTime;
730 }
731
732 template class PowerHintSession<>;
733 template class PowerHintSession<testing::NiceMock<mock::pixel::MockHintManager>,
734 testing::NiceMock<mock::pixel::MockPowerSessionManager>>;
735 template class PowerHintSession<
736 testing::NiceMock<mock::pixel::MockHintManager>,
737 PowerSessionManager<testing::NiceMock<mock::pixel::MockHintManager>>>;
738 } // namespace pixel
739 } // namespace impl
740 } // namespace power
741 } // namespace hardware
742 } // namespace google
743 } // namespace aidl
744