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/logging.h>
23 #include <android-base/parsedouble.h>
24 #include <android-base/properties.h>
25 #include <android-base/stringprintf.h>
26 #include <perfmgr/AdpfConfig.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 std::chrono::duration_cast;
48 using std::chrono::nanoseconds;
49
50 using std::operator""ms;
51
52 namespace {
53
54 static std::atomic<int64_t> sSessionIDCounter{0};
55
ns_to_100us(int64_t ns)56 static inline int64_t ns_to_100us(int64_t ns) {
57 return ns / 100000;
58 }
59
60 } // namespace
61
62 template <class HintManagerT, class PowerSessionManagerT>
convertWorkDurationToBoostByPid(const std::vector<WorkDuration> & actualDurations)63 int64_t PowerHintSession<HintManagerT, PowerSessionManagerT>::convertWorkDurationToBoostByPid(
64 const std::vector<WorkDuration> &actualDurations) {
65 std::shared_ptr<AdpfConfig> adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
66 const nanoseconds &targetDuration = mDescriptor->targetNs;
67 int64_t &integral_error = mDescriptor->integral_error;
68 int64_t &previous_error = mDescriptor->previous_error;
69 uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
70 uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
71 uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
72 int64_t targetDurationNanos = (int64_t)targetDuration.count();
73 int64_t length = actualDurations.size();
74 int64_t p_start =
75 samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
76 int64_t i_start =
77 samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
78 int64_t d_start =
79 samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
80 int64_t dt = ns_to_100us(targetDurationNanos);
81 int64_t err_sum = 0;
82 int64_t derivative_sum = 0;
83 for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
84 int64_t actualDurationNanos = actualDurations[i].durationNanos;
85 if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
86 ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
87 actualDurationNanos, targetDurationNanos);
88 }
89 // PID control algorithm
90 int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
91 if (i >= d_start) {
92 derivative_sum += error - previous_error;
93 }
94 if (i >= p_start) {
95 err_sum += error;
96 }
97 if (i >= i_start) {
98 integral_error += error * dt;
99 integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error);
100 integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error);
101 }
102 previous_error = error;
103 }
104
105 auto pid_pu_active = adpfConfig->mPidPu;
106 if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
107 pid_pu_active = mHeuristicBoostActive
108 ? adpfConfig->mPidPu * adpfConfig->mHBoostPidPuFactor.value()
109 : adpfConfig->mPidPu;
110 }
111 int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : pid_pu_active) *
112 err_sum / (length - p_start));
113 int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error);
114 int64_t dOut =
115 static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
116 derivative_sum / dt / (length - d_start));
117
118 int64_t output = pOut + iOut + dOut;
119 ATRACE_INT(mAppDescriptorTrace->trace_pid_err.c_str(), err_sum / (length - p_start));
120 ATRACE_INT(mAppDescriptorTrace->trace_pid_integral.c_str(), integral_error);
121 ATRACE_INT(mAppDescriptorTrace->trace_pid_derivative.c_str(),
122 derivative_sum / dt / (length - d_start));
123 ATRACE_INT(mAppDescriptorTrace->trace_pid_pOut.c_str(), pOut);
124 ATRACE_INT(mAppDescriptorTrace->trace_pid_iOut.c_str(), iOut);
125 ATRACE_INT(mAppDescriptorTrace->trace_pid_dOut.c_str(), dOut);
126 ATRACE_INT(mAppDescriptorTrace->trace_pid_output.c_str(), output);
127 return output;
128 }
129
130 template <class HintManagerT, class PowerSessionManagerT>
PowerHintSession(int32_t tgid,int32_t uid,const std::vector<int32_t> & threadIds,int64_t durationNs,SessionTag tag)131 PowerHintSession<HintManagerT, PowerSessionManagerT>::PowerHintSession(
132 int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNs,
133 SessionTag tag)
134 : mPSManager(PowerSessionManagerT::getInstance()),
135 mSessionId(++sSessionIDCounter),
136 mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64, tgid, uid, mSessionId)),
137 mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid, threadIds, tag,
138 std::chrono::nanoseconds(durationNs))),
139 mAppDescriptorTrace(std::make_shared<AppDescriptorTrace>(mIdString)),
140 mTag(tag),
141 mSessionRecords(
142 HintManagerT::GetInstance()->GetAdpfProfile()->mHeuristicBoostOn.has_value() &&
143 HintManagerT::GetInstance()
144 ->GetAdpfProfile()
145 ->mHeuristicBoostOn.value()
146 ? std::make_unique<SessionRecords>(HintManagerT::GetInstance()
147 ->GetAdpfProfile()
148 ->mMaxRecordsNum.value(),
149 HintManagerT::GetInstance()
150 ->GetAdpfProfile()
151 ->mJankCheckTimeFactor.value())
152 : nullptr) {
153 ATRACE_CALL();
154 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
155 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), mDescriptor->is_active.load());
156
157 mLastUpdatedTime = std::chrono::steady_clock::now();
158 mPSManager->addPowerSession(mIdString, mDescriptor, mAppDescriptorTrace, threadIds);
159 // init boost
160 auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
161 mPSManager->voteSet(
162 mSessionId, AdpfVoteType::CPU_LOAD_RESET, adpfConfig->mUclampMinLoadReset, kUclampMax,
163 std::chrono::steady_clock::now(),
164 duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor / 2.0));
165
166 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, adpfConfig->mUclampMinInit,
167 kUclampMax, std::chrono::steady_clock::now(), mDescriptor->targetNs);
168 ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
169 }
170
171 template <class HintManagerT, class PowerSessionManagerT>
~PowerHintSession()172 PowerHintSession<HintManagerT, PowerSessionManagerT>::~PowerHintSession() {
173 ATRACE_CALL();
174 close();
175 ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
176 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), 0);
177 ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), 0);
178 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), 0);
179 }
180
181 template <class HintManagerT, class PowerSessionManagerT>
isAppSession()182 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isAppSession() {
183 // Check if uid is in range reserved for applications
184 return mDescriptor->uid >= AID_APP_START;
185 }
186
187 template <class HintManagerT, class PowerSessionManagerT>
updatePidControlVariable(int pidControlVariable,bool updateVote)188 void PowerHintSession<HintManagerT, PowerSessionManagerT>::updatePidControlVariable(
189 int pidControlVariable, bool updateVote) {
190 mDescriptor->pidControlVariable = pidControlVariable;
191 if (updateVote) {
192 auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
193 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, pidControlVariable,
194 kUclampMax, std::chrono::steady_clock::now(),
195 std::max(duration_cast<nanoseconds>(mDescriptor->targetNs *
196 adpfConfig->mStaleTimeFactor),
197 nanoseconds(adpfConfig->mReportingRateLimitNs) * 2));
198 }
199 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), pidControlVariable);
200 }
201
202 template <class HintManagerT, class PowerSessionManagerT>
tryToSendPowerHint(std::string hint)203 void PowerHintSession<HintManagerT, PowerSessionManagerT>::tryToSendPowerHint(std::string hint) {
204 if (!mSupportedHints[hint].has_value()) {
205 mSupportedHints[hint] = HintManagerT::GetInstance()->IsHintSupported(hint);
206 }
207 if (mSupportedHints[hint].value()) {
208 HintManagerT::GetInstance()->DoHint(hint);
209 }
210 }
211
212 template <class HintManagerT, class PowerSessionManagerT>
dumpToStream(std::ostream & stream)213 void PowerHintSession<HintManagerT, PowerSessionManagerT>::dumpToStream(std::ostream &stream) {
214 std::scoped_lock lock{mPowerHintSessionLock};
215 stream << "ID.Min.Act.Timeout(" << mIdString;
216 stream << ", " << mDescriptor->pidControlVariable;
217 stream << ", " << mDescriptor->is_active;
218 stream << ", " << isTimeout() << ")";
219 }
220
221 template <class HintManagerT, class PowerSessionManagerT>
pause()222 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::pause() {
223 std::scoped_lock lock{mPowerHintSessionLock};
224 if (mSessionClosed) {
225 ALOGE("Error: session is dead");
226 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
227 }
228 if (!mDescriptor->is_active.load())
229 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
230 // Reset to default uclamp value.
231 mPSManager->setThreadsFromPowerSession(mSessionId, {});
232 mDescriptor->is_active.store(false);
233 mPSManager->pause(mSessionId);
234 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), false);
235 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
236 return ndk::ScopedAStatus::ok();
237 }
238
239 template <class HintManagerT, class PowerSessionManagerT>
resume()240 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::resume() {
241 std::scoped_lock lock{mPowerHintSessionLock};
242 if (mSessionClosed) {
243 ALOGE("Error: session is dead");
244 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
245 }
246 if (mDescriptor->is_active.load()) {
247 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
248 }
249 mPSManager->setThreadsFromPowerSession(mSessionId, mDescriptor->thread_ids);
250 mDescriptor->is_active.store(true);
251 // resume boost
252 mPSManager->resume(mSessionId);
253 ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), true);
254 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), mDescriptor->pidControlVariable);
255 return ndk::ScopedAStatus::ok();
256 }
257
258 template <class HintManagerT, class PowerSessionManagerT>
close()259 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::close() {
260 std::scoped_lock lock{mPowerHintSessionLock};
261 if (mSessionClosed) {
262 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
263 }
264 mSessionClosed = true;
265 // Remove the session from PowerSessionManager first to avoid racing.
266 mPSManager->removePowerSession(mSessionId);
267 mDescriptor->is_active.store(false);
268 ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
269 return ndk::ScopedAStatus::ok();
270 }
271
272 template <class HintManagerT, class PowerSessionManagerT>
updateTargetWorkDuration(int64_t targetDurationNanos)273 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::updateTargetWorkDuration(
274 int64_t targetDurationNanos) {
275 std::scoped_lock lock{mPowerHintSessionLock};
276 if (mSessionClosed) {
277 ALOGE("Error: session is dead");
278 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
279 }
280 if (targetDurationNanos <= 0) {
281 ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
282 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
283 }
284 targetDurationNanos =
285 targetDurationNanos * HintManagerT::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
286
287 mDescriptor->targetNs = std::chrono::nanoseconds(targetDurationNanos);
288 mPSManager->updateTargetWorkDuration(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT,
289 mDescriptor->targetNs);
290 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), targetDurationNanos);
291
292 return ndk::ScopedAStatus::ok();
293 }
294
295 template <class HintManagerT, class PowerSessionManagerT>
updateHeuristicBoost()296 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::updateHeuristicBoost() {
297 auto maxDurationUs = mSessionRecords->getMaxDuration(); // micro seconds
298 auto avgDurationUs = mSessionRecords->getAvgDuration(); // micro seconds
299 auto numOfReportedDurations = mSessionRecords->getNumOfRecords();
300 auto numOfMissedCycles = mSessionRecords->getNumOfMissedCycles();
301
302 if (!maxDurationUs.has_value() || !avgDurationUs.has_value()) {
303 return false;
304 }
305
306 double maxToAvgRatio;
307 if (numOfReportedDurations <= 0) {
308 maxToAvgRatio = maxDurationUs.value() * 1.0 / (mDescriptor->targetNs.count() / 1000);
309 } else {
310 maxToAvgRatio = maxDurationUs.value() / avgDurationUs.value();
311 }
312
313 auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
314
315 if (mSessionRecords->isLowFrameRate(adpfConfig->mLowFrameRateThreshold.value())) {
316 // Turn off the boost when the FPS drops to a low value,
317 // since usually this is because of ui changing to low rate scenarios.
318 // Extra boost is not needed in these scenarios.
319 mHeuristicBoostActive = false;
320 } else if (numOfMissedCycles >= adpfConfig->mHBoostOnMissedCycles.value()) {
321 mHeuristicBoostActive = true;
322 } else if (numOfMissedCycles <= adpfConfig->mHBoostOffMissedCycles.value() &&
323 maxToAvgRatio < adpfConfig->mHBoostOffMaxAvgRatio.value()) {
324 mHeuristicBoostActive = false;
325 }
326 ATRACE_INT(mAppDescriptorTrace->trace_heuristic_boost_active.c_str(), mHeuristicBoostActive);
327 ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), numOfMissedCycles);
328 ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), avgDurationUs.value());
329 ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), maxDurationUs.value());
330 ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(),
331 mSessionRecords->isLowFrameRate(adpfConfig->mLowFrameRateThreshold.value()));
332 return mHeuristicBoostActive;
333 }
334
335 template <class HintManagerT, class PowerSessionManagerT>
reportActualWorkDuration(const std::vector<WorkDuration> & actualDurations)336 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::reportActualWorkDuration(
337 const std::vector<WorkDuration> &actualDurations) {
338 std::scoped_lock lock{mPowerHintSessionLock};
339 if (mSessionClosed) {
340 ALOGE("Error: session is dead");
341 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
342 }
343 if (mDescriptor->targetNs.count() == 0LL) {
344 ALOGE("Expect to call updateTargetWorkDuration() first.");
345 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
346 }
347 if (actualDurations.empty()) {
348 ALOGE("Error: durations shouldn't be empty.");
349 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
350 }
351 if (!mDescriptor->is_active.load()) {
352 ALOGE("Error: shouldn't report duration during pause state.");
353 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
354 }
355
356 auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
357 mDescriptor->update_count++;
358 bool isFirstFrame = isTimeout();
359 ATRACE_INT(mAppDescriptorTrace->trace_batch_size.c_str(), actualDurations.size());
360 ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), actualDurations.back().durationNanos);
361 ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
362 ATRACE_INT(mAppDescriptorTrace->trace_hint_count.c_str(), mDescriptor->update_count);
363 ATRACE_INT(mAppDescriptorTrace->trace_hint_overtime.c_str(),
364 actualDurations.back().durationNanos - mDescriptor->targetNs.count() > 0);
365 ATRACE_INT(mAppDescriptorTrace->trace_is_first_frame.c_str(), (isFirstFrame) ? (1) : (0));
366 ATRACE_INT(mAppDescriptorTrace->trace_cpu_duration.c_str(),
367 actualDurations.back().cpuDurationNanos);
368 ATRACE_INT(mAppDescriptorTrace->trace_gpu_duration.c_str(),
369 actualDurations.back().gpuDurationNanos);
370
371 mLastUpdatedTime = std::chrono::steady_clock::now();
372 if (isFirstFrame) {
373 if (isAppSession()) {
374 tryToSendPowerHint("ADPF_FIRST_FRAME");
375 }
376
377 mPSManager->updateUniversalBoostMode();
378 }
379
380 mPSManager->disableBoosts(mSessionId);
381
382 if (!adpfConfig->mPidOn) {
383 updatePidControlVariable(adpfConfig->mUclampMinHigh);
384 return ndk::ScopedAStatus::ok();
385 }
386
387 if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
388 mSessionRecords->addReportedDurations(actualDurations, mDescriptor->targetNs.count());
389 updateHeuristicBoost();
390 }
391
392 int64_t output = convertWorkDurationToBoostByPid(actualDurations);
393
394 // Apply to all the threads in the group
395 auto uclampMinCeiling = adpfConfig->mUclampMinHigh;
396 if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
397 uclampMinCeiling = mHeuristicBoostActive ? adpfConfig->mHBoostUclampMin.value()
398 : adpfConfig->mUclampMinHigh;
399 }
400
401 int next_min = std::min(static_cast<int>(uclampMinCeiling),
402 mDescriptor->pidControlVariable + static_cast<int>(output));
403 next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
404
405 updatePidControlVariable(next_min);
406
407 if (!adpfConfig->mGpuBoostOn.value_or(false) || !adpfConfig->mGpuBoostCapacityMax ||
408 !actualDurations.back().gpuDurationNanos) {
409 return ndk::ScopedAStatus::ok();
410 }
411
412 auto const gpu_freq = mPSManager->gpuFrequency();
413 if (!gpu_freq) {
414 return ndk::ScopedAStatus::ok();
415 }
416 auto const additional_gpu_capacity =
417 calculate_capacity(actualDurations.back(), mDescriptor->targetNs, *gpu_freq);
418 ATRACE_INT(mAppDescriptorTrace->trace_gpu_capacity.c_str(),
419 static_cast<int>(additional_gpu_capacity));
420
421 auto const additional_gpu_capacity_clamped = std::clamp(
422 additional_gpu_capacity, Cycles(0), Cycles(*adpfConfig->mGpuBoostCapacityMax));
423
424 mPSManager->voteSet(
425 mSessionId, AdpfVoteType::GPU_CAPACITY, additional_gpu_capacity_clamped,
426 std::chrono::steady_clock::now(),
427 duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor));
428
429 return ndk::ScopedAStatus::ok();
430 }
431
432 template <class HintManagerT, class PowerSessionManagerT>
sendHint(SessionHint hint)433 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::sendHint(
434 SessionHint hint) {
435 std::scoped_lock lock{mPowerHintSessionLock};
436 if (mSessionClosed) {
437 ALOGE("Error: session is dead");
438 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
439 }
440 if (mDescriptor->targetNs.count() == 0LL) {
441 ALOGE("Expect to call updateTargetWorkDuration() first.");
442 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
443 }
444 auto adpfConfig = HintManagerT::GetInstance()->GetAdpfProfile();
445
446 switch (hint) {
447 case SessionHint::CPU_LOAD_UP:
448 updatePidControlVariable(mDescriptor->pidControlVariable);
449 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_UP, adpfConfig->mUclampMinLoadUp,
450 kUclampMax, std::chrono::steady_clock::now(),
451 mDescriptor->targetNs * 2);
452 break;
453 case SessionHint::CPU_LOAD_DOWN:
454 updatePidControlVariable(adpfConfig->mUclampMinLow);
455 break;
456 case SessionHint::CPU_LOAD_RESET:
457 updatePidControlVariable(
458 std::max(adpfConfig->mUclampMinInit,
459 static_cast<uint32_t>(mDescriptor->pidControlVariable)),
460 false);
461 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESET,
462 adpfConfig->mUclampMinLoadReset, kUclampMax,
463 std::chrono::steady_clock::now(),
464 duration_cast<nanoseconds>(mDescriptor->targetNs *
465 adpfConfig->mStaleTimeFactor / 2.0));
466 break;
467 case SessionHint::CPU_LOAD_RESUME:
468 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESUME,
469 mDescriptor->pidControlVariable, kUclampMax,
470 std::chrono::steady_clock::now(),
471 duration_cast<nanoseconds>(mDescriptor->targetNs *
472 adpfConfig->mStaleTimeFactor / 2.0));
473 break;
474 case SessionHint::POWER_EFFICIENCY:
475 setMode(SessionMode::POWER_EFFICIENCY, true);
476 break;
477 case SessionHint::GPU_LOAD_UP:
478 mPSManager->voteSet(mSessionId, AdpfVoteType::GPU_LOAD_UP,
479 Cycles(adpfConfig->mGpuCapacityLoadUpHeadroom),
480 std::chrono::steady_clock::now(), mDescriptor->targetNs);
481 break;
482 case SessionHint::GPU_LOAD_DOWN:
483 // TODO(kevindubois): add impl
484 break;
485 case SessionHint::GPU_LOAD_RESET:
486 // TODO(kevindubois): add impl
487 break;
488 default:
489 ALOGE("Error: hint is invalid");
490 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
491 }
492 tryToSendPowerHint(toString(hint));
493 mLastUpdatedTime = std::chrono::steady_clock::now();
494 return ndk::ScopedAStatus::ok();
495 }
496
497 template <class HintManagerT, class PowerSessionManagerT>
setMode(SessionMode mode,bool enabled)498 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setMode(SessionMode mode,
499 bool enabled) {
500 std::scoped_lock lock{mPowerHintSessionLock};
501 if (mSessionClosed) {
502 ALOGE("Error: session is dead");
503 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
504 }
505
506 switch (mode) {
507 case SessionMode::POWER_EFFICIENCY:
508 mPSManager->setPreferPowerEfficiency(mSessionId, enabled);
509 break;
510 default:
511 ALOGE("Error: mode is invalid");
512 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
513 }
514
515 mModes[static_cast<size_t>(mode)] = enabled;
516 ATRACE_INT(mAppDescriptorTrace->trace_modes[static_cast<size_t>(mode)].c_str(), enabled);
517 mLastUpdatedTime = std::chrono::steady_clock::now();
518 return ndk::ScopedAStatus::ok();
519 }
520
521 template <class HintManagerT, class PowerSessionManagerT>
setThreads(const std::vector<int32_t> & threadIds)522 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setThreads(
523 const std::vector<int32_t> &threadIds) {
524 std::scoped_lock lock{mPowerHintSessionLock};
525 if (mSessionClosed) {
526 ALOGE("Error: session is dead");
527 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
528 }
529 if (threadIds.empty()) {
530 ALOGE("Error: threadIds should not be empty");
531 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
532 }
533 mDescriptor->thread_ids = threadIds;
534 mPSManager->setThreadsFromPowerSession(mSessionId, threadIds);
535 // init boost
536 updatePidControlVariable(HintManagerT::GetInstance()->GetAdpfProfile()->mUclampMinInit);
537 return ndk::ScopedAStatus::ok();
538 }
539
540 template <class HintManagerT, class PowerSessionManagerT>
getSessionConfig(SessionConfig * _aidl_return)541 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionConfig(
542 SessionConfig *_aidl_return) {
543 _aidl_return->id = mSessionId;
544 return ndk::ScopedAStatus::ok();
545 }
546
547 template <class HintManagerT, class PowerSessionManagerT>
getSessionTag() const548 SessionTag PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionTag() const {
549 return mTag;
550 }
551
toString() const552 std::string AppHintDesc::toString() const {
553 std::string out = StringPrintf("session %" PRId64 "\n", sessionId);
554 out.append(
555 StringPrintf(" duration: %" PRId64 " ns\n", static_cast<int64_t>(targetNs.count())));
556 out.append(StringPrintf(" uclamp.min: %d \n", pidControlVariable));
557 out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid));
558 return out;
559 }
560
561 template <class HintManagerT, class PowerSessionManagerT>
isTimeout()562 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isTimeout() {
563 auto now = std::chrono::steady_clock::now();
564 time_point<steady_clock> staleTime =
565 mLastUpdatedTime +
566 nanoseconds(static_cast<int64_t>(
567 mDescriptor->targetNs.count() *
568 HintManagerT::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
569 return now >= staleTime;
570 }
571
572 template class PowerHintSession<>;
573 template class PowerHintSession<testing::NiceMock<mock::pixel::MockHintManager>,
574 testing::NiceMock<mock::pixel::MockPowerSessionManager>>;
575 template class PowerHintSession<
576 testing::NiceMock<mock::pixel::MockHintManager>,
577 PowerSessionManager<testing::NiceMock<mock::pixel::MockHintManager>>>;
578 } // namespace pixel
579 } // namespace impl
580 } // namespace power
581 } // namespace hardware
582 } // namespace google
583 } // namespace aidl
584