| /* |
| * Copyright 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "powerhal-libperfmgr" |
| #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL) |
| |
| #include "PowerHintSession.h" |
| |
| #include <android-base/logging.h> |
| #include <android-base/parsedouble.h> |
| #include <android-base/properties.h> |
| #include <android-base/stringprintf.h> |
| #include <private/android_filesystem_config.h> |
| #include <sys/syscall.h> |
| #include <time.h> |
| #include <utils/Trace.h> |
| |
| #include <atomic> |
| |
| #include "GpuCalculationHelpers.h" |
| #include "tests/mocks/MockHintManager.h" |
| #include "tests/mocks/MockPowerSessionManager.h" |
| |
| namespace aidl { |
| namespace google { |
| namespace hardware { |
| namespace power { |
| namespace impl { |
| namespace pixel { |
| |
| using ::android::base::StringPrintf; |
| using ::android::perfmgr::AdpfConfig; |
| using ::android::perfmgr::HintManager; |
| using std::chrono::duration_cast; |
| using std::chrono::nanoseconds; |
| |
| using std::operator""ms; |
| |
| namespace { |
| |
| static std::atomic<int64_t> sSessionIDCounter{0}; |
| |
| static inline int64_t ns_to_100us(int64_t ns) { |
| return ns / 100000; |
| } |
| |
| } // namespace |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| int64_t PowerHintSession<HintManagerT, PowerSessionManagerT>::convertWorkDurationToBoostByPid( |
| const std::vector<WorkDuration> &actualDurations) { |
| std::shared_ptr<AdpfConfig> adpfConfig = getAdpfProfile(); |
| const nanoseconds &targetDuration = mDescriptor->targetNs; |
| int64_t &integral_error = mDescriptor->integral_error; |
| int64_t &previous_error = mDescriptor->previous_error; |
| uint64_t samplingWindowP = adpfConfig->mSamplingWindowP; |
| uint64_t samplingWindowI = adpfConfig->mSamplingWindowI; |
| uint64_t samplingWindowD = adpfConfig->mSamplingWindowD; |
| int64_t targetDurationNanos = (int64_t)targetDuration.count(); |
| int64_t length = actualDurations.size(); |
| int64_t p_start = |
| samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP; |
| int64_t i_start = |
| samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI; |
| int64_t d_start = |
| samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD; |
| int64_t dt = ns_to_100us(targetDurationNanos); |
| int64_t err_sum = 0; |
| int64_t derivative_sum = 0; |
| for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) { |
| int64_t actualDurationNanos = actualDurations[i].durationNanos; |
| if (std::abs(actualDurationNanos) > targetDurationNanos * 20) { |
| ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")", |
| actualDurationNanos, targetDurationNanos); |
| } |
| // PID control algorithm |
| int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos); |
| if (i >= d_start) { |
| derivative_sum += error - previous_error; |
| } |
| if (i >= p_start) { |
| err_sum += error; |
| } |
| if (i >= i_start) { |
| integral_error += error * dt; |
| integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error); |
| integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error); |
| } |
| previous_error = error; |
| } |
| |
| auto pid_pu_active = adpfConfig->mPidPu; |
| if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) { |
| auto hboostPidPu = std::min(adpfConfig->mHBoostSevereJankPidPu.value(), adpfConfig->mPidPu); |
| if (mJankyLevel == SessionJankyLevel::MODERATE) { |
| double JankyFactor = |
| mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value() |
| ? 0.0 |
| : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) * |
| 1.0 / |
| (adpfConfig->mHBoostSevereJankThreshold.value() - |
| adpfConfig->mHBoostModerateJankThreshold.value()); |
| pid_pu_active = adpfConfig->mPidPu + JankyFactor * (hboostPidPu - adpfConfig->mPidPu); |
| } else if (mJankyLevel == SessionJankyLevel::SEVERE) { |
| pid_pu_active = hboostPidPu; |
| } |
| ATRACE_INT(mAppDescriptorTrace->trace_hboost_pid_pu.c_str(), pid_pu_active * 100); |
| } |
| int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : pid_pu_active) * |
| err_sum / (length - p_start)); |
| int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error); |
| int64_t dOut = |
| static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) * |
| derivative_sum / dt / (length - d_start)); |
| |
| int64_t output = pOut + iOut + dOut; |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_err.c_str(), err_sum / (length - p_start)); |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_integral.c_str(), integral_error); |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_derivative.c_str(), |
| derivative_sum / dt / (length - d_start)); |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_pOut.c_str(), pOut); |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_iOut.c_str(), iOut); |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_dOut.c_str(), dOut); |
| ATRACE_INT(mAppDescriptorTrace->trace_pid_output.c_str(), output); |
| return output; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| PowerHintSession<HintManagerT, PowerSessionManagerT>::PowerHintSession( |
| int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNs, |
| SessionTag tag) |
| : mPSManager(PowerSessionManagerT::getInstance()), |
| mSessionId(++sSessionIDCounter), |
| mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64 "-%s", tgid, uid, mSessionId, |
| toString(tag).c_str())), |
| mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid, threadIds, tag, |
| std::chrono::nanoseconds(durationNs))), |
| mAppDescriptorTrace(std::make_shared<AppDescriptorTrace>(mIdString)), |
| mTag(tag), |
| mAdpfProfile(HintManager::GetInstance()->GetAdpfProfile(toString(mTag))), |
| mOnAdpfUpdate( |
| [this](const std::shared_ptr<AdpfConfig> config) { this->setAdpfProfile(config); }), |
| mSessionRecords(getAdpfProfile()->mHeuristicBoostOn.has_value() && |
| getAdpfProfile()->mHeuristicBoostOn.value() |
| ? std::make_unique<SessionRecords>( |
| getAdpfProfile()->mMaxRecordsNum.value(), |
| getAdpfProfile()->mJankCheckTimeFactor.value()) |
| : nullptr) { |
| ATRACE_CALL(); |
| ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count()); |
| ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), mDescriptor->is_active.load()); |
| HintManager::GetInstance()->RegisterAdpfUpdateEvent(toString(mTag), &mOnAdpfUpdate); |
| |
| mLastUpdatedTime = std::chrono::steady_clock::now(); |
| mPSManager->addPowerSession(mIdString, mDescriptor, mAppDescriptorTrace, threadIds); |
| // init boost |
| auto adpfConfig = getAdpfProfile(); |
| mPSManager->voteSet( |
| mSessionId, AdpfVoteType::CPU_LOAD_RESET, adpfConfig->mUclampMinLoadReset, kUclampMax, |
| std::chrono::steady_clock::now(), |
| duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor / 2.0)); |
| |
| mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, adpfConfig->mUclampMinInit, |
| kUclampMax, std::chrono::steady_clock::now(), mDescriptor->targetNs); |
| ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str()); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| PowerHintSession<HintManagerT, PowerSessionManagerT>::~PowerHintSession() { |
| ATRACE_CALL(); |
| close(); |
| ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str()); |
| ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), 0); |
| ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), 0); |
| ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), 0); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isAppSession() { |
| // Check if uid is in range reserved for applications |
| return mDescriptor->uid >= AID_APP_START; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| void PowerHintSession<HintManagerT, PowerSessionManagerT>::updatePidControlVariable( |
| int pidControlVariable, bool updateVote) { |
| mDescriptor->pidControlVariable = pidControlVariable; |
| if (updateVote) { |
| auto adpfConfig = getAdpfProfile(); |
| mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, pidControlVariable, |
| kUclampMax, std::chrono::steady_clock::now(), |
| std::max(duration_cast<nanoseconds>(mDescriptor->targetNs * |
| adpfConfig->mStaleTimeFactor), |
| nanoseconds(adpfConfig->mReportingRateLimitNs) * 2)); |
| } |
| ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), pidControlVariable); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| void PowerHintSession<HintManagerT, PowerSessionManagerT>::tryToSendPowerHint(std::string hint) { |
| if (!mSupportedHints[hint].has_value()) { |
| mSupportedHints[hint] = HintManagerT::GetInstance()->IsHintSupported(hint); |
| } |
| if (mSupportedHints[hint].value()) { |
| HintManagerT::GetInstance()->DoHint(hint); |
| } |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| void PowerHintSession<HintManagerT, PowerSessionManagerT>::dumpToStream(std::ostream &stream) { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| stream << "ID.Min.Act.Timeout(" << mIdString; |
| stream << ", " << mDescriptor->pidControlVariable; |
| stream << ", " << mDescriptor->is_active; |
| stream << ", " << isTimeout() << ")"; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::pause() { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (!mDescriptor->is_active.load()) |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| // Reset to default uclamp value. |
| mPSManager->setThreadsFromPowerSession(mSessionId, {}); |
| mDescriptor->is_active.store(false); |
| mPSManager->pause(mSessionId); |
| ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), false); |
| ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::resume() { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (mDescriptor->is_active.load()) { |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| mPSManager->setThreadsFromPowerSession(mSessionId, mDescriptor->thread_ids); |
| mDescriptor->is_active.store(true); |
| // resume boost |
| mPSManager->resume(mSessionId); |
| ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), true); |
| ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), mDescriptor->pidControlVariable); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::close() { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| mSessionClosed = true; |
| // Remove the session from PowerSessionManager first to avoid racing. |
| mPSManager->removePowerSession(mSessionId); |
| mDescriptor->is_active.store(false); |
| HintManager::GetInstance()->UnregisterAdpfUpdateEvent(toString(mTag), &mOnAdpfUpdate); |
| ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::updateTargetWorkDuration( |
| int64_t targetDurationNanos) { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (targetDurationNanos <= 0) { |
| ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| } |
| targetDurationNanos = targetDurationNanos * getAdpfProfile()->mTargetTimeFactor; |
| |
| mDescriptor->targetNs = std::chrono::nanoseconds(targetDurationNanos); |
| mPSManager->updateTargetWorkDuration(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, |
| mDescriptor->targetNs); |
| ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), targetDurationNanos); |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| SessionJankyLevel PowerHintSession<HintManagerT, PowerSessionManagerT>::updateSessionJankState( |
| SessionJankyLevel oldState, int32_t numOfJankFrames, double durationVariance, |
| bool isLowFPS) { |
| SessionJankyLevel newState = SessionJankyLevel::LIGHT; |
| if (isLowFPS) { |
| newState = SessionJankyLevel::LIGHT; |
| return newState; |
| } |
| |
| auto adpfConfig = getAdpfProfile(); |
| if (numOfJankFrames < adpfConfig->mHBoostModerateJankThreshold.value()) { |
| if (oldState == SessionJankyLevel::LIGHT || |
| durationVariance < adpfConfig->mHBoostOffMaxAvgDurRatio.value()) { |
| newState = SessionJankyLevel::LIGHT; |
| } else { |
| newState = SessionJankyLevel::MODERATE; |
| } |
| } else if (numOfJankFrames < adpfConfig->mHBoostSevereJankThreshold.value()) { |
| newState = SessionJankyLevel::MODERATE; |
| } else { |
| newState = SessionJankyLevel::SEVERE; |
| } |
| |
| return newState; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| void PowerHintSession<HintManagerT, PowerSessionManagerT>::updateHeuristicBoost() { |
| auto maxDurationUs = mSessionRecords->getMaxDuration(); // micro seconds |
| auto avgDurationUs = mSessionRecords->getAvgDuration(); // micro seconds |
| auto numOfReportedDurations = mSessionRecords->getNumOfRecords(); |
| auto numOfJankFrames = mSessionRecords->getNumOfMissedCycles(); |
| |
| if (!maxDurationUs.has_value() || !avgDurationUs.has_value()) { |
| // No history data stored |
| return; |
| } |
| |
| double maxToAvgRatio; |
| if (numOfReportedDurations <= 0) { |
| maxToAvgRatio = maxDurationUs.value() * 1.0 / (mDescriptor->targetNs.count() / 1000); |
| } else { |
| maxToAvgRatio = maxDurationUs.value() / avgDurationUs.value(); |
| } |
| |
| auto isLowFPS = |
| mSessionRecords->isLowFrameRate(getAdpfProfile()->mLowFrameRateThreshold.value()); |
| |
| mJankyLevel = updateSessionJankState(mJankyLevel, numOfJankFrames, maxToAvgRatio, isLowFPS); |
| mJankyFrameNum = numOfJankFrames; |
| |
| ATRACE_INT(mAppDescriptorTrace->trace_hboost_janky_level.c_str(), |
| static_cast<int32_t>(mJankyLevel)); |
| ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), mJankyFrameNum); |
| ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), avgDurationUs.value()); |
| ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), maxDurationUs.value()); |
| ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(), isLowFPS); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::reportActualWorkDuration( |
| const std::vector<WorkDuration> &actualDurations) { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (mDescriptor->targetNs.count() == 0LL) { |
| ALOGE("Expect to call updateTargetWorkDuration() first."); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (actualDurations.empty()) { |
| ALOGE("Error: durations shouldn't be empty."); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| } |
| if (!mDescriptor->is_active.load()) { |
| ALOGE("Error: shouldn't report duration during pause state."); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| auto adpfConfig = getAdpfProfile(); |
| mDescriptor->update_count++; |
| bool isFirstFrame = isTimeout(); |
| ATRACE_INT(mAppDescriptorTrace->trace_batch_size.c_str(), actualDurations.size()); |
| ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), actualDurations.back().durationNanos); |
| ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count()); |
| ATRACE_INT(mAppDescriptorTrace->trace_hint_count.c_str(), mDescriptor->update_count); |
| ATRACE_INT(mAppDescriptorTrace->trace_hint_overtime.c_str(), |
| actualDurations.back().durationNanos - mDescriptor->targetNs.count() > 0); |
| ATRACE_INT(mAppDescriptorTrace->trace_is_first_frame.c_str(), (isFirstFrame) ? (1) : (0)); |
| ATRACE_INT(mAppDescriptorTrace->trace_cpu_duration.c_str(), |
| actualDurations.back().cpuDurationNanos); |
| ATRACE_INT(mAppDescriptorTrace->trace_gpu_duration.c_str(), |
| actualDurations.back().gpuDurationNanos); |
| |
| mLastUpdatedTime = std::chrono::steady_clock::now(); |
| if (isFirstFrame) { |
| mPSManager->updateUniversalBoostMode(); |
| } |
| |
| mPSManager->disableBoosts(mSessionId); |
| |
| if (!adpfConfig->mPidOn) { |
| updatePidControlVariable(adpfConfig->mUclampMinHigh); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| bool hboostEnabled = |
| adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value(); |
| |
| if (hboostEnabled) { |
| mSessionRecords->addReportedDurations(actualDurations, mDescriptor->targetNs.count()); |
| mPSManager->updateHboostStatistics(mSessionId, mJankyLevel, actualDurations.size()); |
| updateHeuristicBoost(); |
| } |
| |
| int64_t output = convertWorkDurationToBoostByPid(actualDurations); |
| |
| // Apply to all the threads in the group |
| auto uclampMinFloor = adpfConfig->mUclampMinLow; |
| auto uclampMinCeiling = adpfConfig->mUclampMinHigh; |
| if (hboostEnabled) { |
| auto hboostMinUclampMinFloor = std::max( |
| adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().first); |
| auto hboostMaxUclampMinFloor = std::max( |
| adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().second); |
| auto hboostMinUclampMinCeiling = std::max( |
| adpfConfig->mUclampMinHigh, adpfConfig->mHBoostUclampMinCeilingRange.value().first); |
| auto hboostMaxUclampMinCeiling = |
| std::max(adpfConfig->mUclampMinHigh, |
| adpfConfig->mHBoostUclampMinCeilingRange.value().second); |
| if (mJankyLevel == SessionJankyLevel::MODERATE) { |
| double JankyFactor = |
| mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value() |
| ? 0.0 |
| : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) * |
| 1.0 / |
| (adpfConfig->mHBoostSevereJankThreshold.value() - |
| adpfConfig->mHBoostModerateJankThreshold.value()); |
| uclampMinFloor = hboostMinUclampMinFloor + |
| (hboostMaxUclampMinFloor - hboostMinUclampMinFloor) * JankyFactor; |
| uclampMinCeiling = |
| hboostMinUclampMinCeiling + |
| (hboostMaxUclampMinCeiling - hboostMinUclampMinCeiling) * JankyFactor; |
| } else if (mJankyLevel == SessionJankyLevel::SEVERE) { |
| uclampMinFloor = hboostMaxUclampMinFloor; |
| uclampMinCeiling = hboostMaxUclampMinCeiling; |
| } |
| ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_ceiling.c_str(), uclampMinCeiling); |
| ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_floor.c_str(), uclampMinFloor); |
| } |
| |
| int next_min = std::min(static_cast<int>(uclampMinCeiling), |
| mDescriptor->pidControlVariable + static_cast<int>(output)); |
| next_min = std::max(static_cast<int>(uclampMinFloor), next_min); |
| |
| updatePidControlVariable(next_min); |
| |
| if (!adpfConfig->mGpuBoostOn.value_or(false) || !adpfConfig->mGpuBoostCapacityMax || |
| !actualDurations.back().gpuDurationNanos) { |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| auto const gpu_freq = mPSManager->gpuFrequency(); |
| if (!gpu_freq) { |
| return ndk::ScopedAStatus::ok(); |
| } |
| auto const additional_gpu_capacity = |
| calculate_capacity(actualDurations.back(), mDescriptor->targetNs, *gpu_freq); |
| ATRACE_INT(mAppDescriptorTrace->trace_gpu_capacity.c_str(), |
| static_cast<int>(additional_gpu_capacity)); |
| |
| auto const additional_gpu_capacity_clamped = std::clamp( |
| additional_gpu_capacity, Cycles(0), Cycles(*adpfConfig->mGpuBoostCapacityMax)); |
| |
| mPSManager->voteSet( |
| mSessionId, AdpfVoteType::GPU_CAPACITY, additional_gpu_capacity_clamped, |
| std::chrono::steady_clock::now(), |
| duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor)); |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::sendHint( |
| SessionHint hint) { |
| { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (mDescriptor->targetNs.count() == 0LL) { |
| ALOGE("Expect to call updateTargetWorkDuration() first."); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| auto adpfConfig = getAdpfProfile(); |
| |
| switch (hint) { |
| case SessionHint::CPU_LOAD_UP: |
| updatePidControlVariable(mDescriptor->pidControlVariable); |
| mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_UP, |
| adpfConfig->mUclampMinLoadUp, kUclampMax, |
| std::chrono::steady_clock::now(), mDescriptor->targetNs * 2); |
| break; |
| case SessionHint::CPU_LOAD_DOWN: |
| updatePidControlVariable(adpfConfig->mUclampMinLow); |
| break; |
| case SessionHint::CPU_LOAD_RESET: |
| updatePidControlVariable( |
| std::max(adpfConfig->mUclampMinInit, |
| static_cast<uint32_t>(mDescriptor->pidControlVariable)), |
| false); |
| mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESET, |
| adpfConfig->mUclampMinLoadReset, kUclampMax, |
| std::chrono::steady_clock::now(), |
| duration_cast<nanoseconds>(mDescriptor->targetNs * |
| adpfConfig->mStaleTimeFactor / 2.0)); |
| break; |
| case SessionHint::CPU_LOAD_RESUME: |
| mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESUME, |
| mDescriptor->pidControlVariable, kUclampMax, |
| std::chrono::steady_clock::now(), |
| duration_cast<nanoseconds>(mDescriptor->targetNs * |
| adpfConfig->mStaleTimeFactor / 2.0)); |
| break; |
| case SessionHint::POWER_EFFICIENCY: |
| setMode(SessionMode::POWER_EFFICIENCY, true); |
| break; |
| case SessionHint::GPU_LOAD_UP: |
| mPSManager->voteSet(mSessionId, AdpfVoteType::GPU_LOAD_UP, |
| Cycles(adpfConfig->mGpuCapacityLoadUpHeadroom), |
| std::chrono::steady_clock::now(), mDescriptor->targetNs); |
| break; |
| case SessionHint::GPU_LOAD_DOWN: |
| // TODO(kevindubois): add impl |
| break; |
| case SessionHint::GPU_LOAD_RESET: |
| // TODO(kevindubois): add impl |
| break; |
| default: |
| ALOGE("Error: hint is invalid"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| } |
| mLastUpdatedTime = std::chrono::steady_clock::now(); |
| } |
| // Don't hold a lock (mPowerHintSession) while DoHint will try to take another |
| // lock(NodeLooperThread). |
| tryToSendPowerHint(toString(hint)); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setMode(SessionMode mode, |
| bool enabled) { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| |
| switch (mode) { |
| case SessionMode::POWER_EFFICIENCY: |
| mPSManager->setPreferPowerEfficiency(mSessionId, enabled); |
| break; |
| default: |
| ALOGE("Error: mode is invalid"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| } |
| |
| mModes[static_cast<size_t>(mode)] = enabled; |
| ATRACE_INT(mAppDescriptorTrace->trace_modes[static_cast<size_t>(mode)].c_str(), enabled); |
| mLastUpdatedTime = std::chrono::steady_clock::now(); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setThreads( |
| const std::vector<int32_t> &threadIds) { |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| if (mSessionClosed) { |
| ALOGE("Error: session is dead"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); |
| } |
| if (threadIds.empty()) { |
| ALOGE("Error: threadIds should not be empty"); |
| return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); |
| } |
| mDescriptor->thread_ids = threadIds; |
| mPSManager->setThreadsFromPowerSession(mSessionId, threadIds); |
| // init boost |
| updatePidControlVariable(getAdpfProfile()->mUclampMinInit); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionConfig( |
| SessionConfig *_aidl_return) { |
| _aidl_return->id = mSessionId; |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| SessionTag PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionTag() const { |
| return mTag; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| const std::shared_ptr<AdpfConfig> |
| PowerHintSession<HintManagerT, PowerSessionManagerT>::getAdpfProfile() const { |
| if (!mAdpfProfile) { |
| return HintManager::GetInstance()->GetAdpfProfile(toString(mTag)); |
| } |
| return mAdpfProfile; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| void PowerHintSession<HintManagerT, PowerSessionManagerT>::setAdpfProfile( |
| const std::shared_ptr<AdpfConfig> profile) { |
| // Must prevent profile from being changed in a binder call duration. |
| std::scoped_lock lock{mPowerHintSessionLock}; |
| mAdpfProfile = profile; |
| } |
| |
| std::string AppHintDesc::toString() const { |
| std::string out = StringPrintf("session %" PRId64 "\n", sessionId); |
| out.append( |
| StringPrintf(" duration: %" PRId64 " ns\n", static_cast<int64_t>(targetNs.count()))); |
| out.append(StringPrintf(" uclamp.min: %d \n", pidControlVariable)); |
| out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid)); |
| return out; |
| } |
| |
| template <class HintManagerT, class PowerSessionManagerT> |
| bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isTimeout() { |
| auto now = std::chrono::steady_clock::now(); |
| time_point<steady_clock> staleTime = |
| mLastUpdatedTime + |
| nanoseconds(static_cast<int64_t>(mDescriptor->targetNs.count() * |
| getAdpfProfile()->mStaleTimeFactor)); |
| return now >= staleTime; |
| } |
| |
| template class PowerHintSession<>; |
| template class PowerHintSession<testing::NiceMock<mock::pixel::MockHintManager>, |
| testing::NiceMock<mock::pixel::MockPowerSessionManager>>; |
| template class PowerHintSession< |
| testing::NiceMock<mock::pixel::MockHintManager>, |
| PowerSessionManager<testing::NiceMock<mock::pixel::MockHintManager>>>; |
| } // namespace pixel |
| } // namespace impl |
| } // namespace power |
| } // namespace hardware |
| } // namespace google |
| } // namespace aidl |