| /* |
| * Copyright 2018 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_NDEBUG 0 |
| |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #undef LOG_TAG |
| #define LOG_TAG "PowerAdvisor" |
| |
| #include <unistd.h> |
| #include <cinttypes> |
| #include <cstdint> |
| #include <optional> |
| |
| #include <android-base/properties.h> |
| #include <utils/Log.h> |
| #include <utils/Mutex.h> |
| #include <utils/Trace.h> |
| |
| #include <android/hardware/power/1.3/IPower.h> |
| #include <android/hardware/power/IPowerHintSession.h> |
| #include <android/hardware/power/WorkDuration.h> |
| |
| #include <binder/IServiceManager.h> |
| |
| #include "../SurfaceFlingerProperties.h" |
| |
| #include "PowerAdvisor.h" |
| #include "SurfaceFlinger.h" |
| |
| namespace android { |
| namespace Hwc2 { |
| |
| PowerAdvisor::~PowerAdvisor() = default; |
| |
| namespace impl { |
| |
| namespace V1_0 = android::hardware::power::V1_0; |
| namespace V1_3 = android::hardware::power::V1_3; |
| using V1_3::PowerHint; |
| |
| using android::hardware::power::Boost; |
| using android::hardware::power::IPower; |
| using android::hardware::power::IPowerHintSession; |
| using android::hardware::power::Mode; |
| using android::hardware::power::WorkDuration; |
| |
| using scheduler::OneShotTimer; |
| |
| PowerAdvisor::~PowerAdvisor() = default; |
| |
| namespace { |
| std::chrono::milliseconds getUpdateTimeout() { |
| // Default to a timeout of 80ms if nothing else is specified |
| static std::chrono::milliseconds timeout = |
| std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80)); |
| return timeout; |
| } |
| |
| void traceExpensiveRendering(bool enabled) { |
| if (enabled) { |
| ATRACE_ASYNC_BEGIN("ExpensiveRendering", 0); |
| } else { |
| ATRACE_ASYNC_END("ExpensiveRendering", 0); |
| } |
| } |
| |
| } // namespace |
| |
| PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) : mFlinger(flinger) { |
| if (getUpdateTimeout() > 0ms) { |
| mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(), |
| /* resetCallback */ nullptr, |
| /* timeoutCallback */ |
| [this] { |
| while (true) { |
| auto timeSinceLastUpdate = std::chrono::nanoseconds( |
| systemTime() - mLastScreenUpdatedTime.load()); |
| if (timeSinceLastUpdate >= getUpdateTimeout()) { |
| break; |
| } |
| // We may try to disable expensive rendering and allow |
| // for sending DISPLAY_UPDATE_IMMINENT hints too early if |
| // we idled very shortly after updating the screen, so |
| // make sure we wait enough time. |
| std::this_thread::sleep_for(getUpdateTimeout() - |
| timeSinceLastUpdate); |
| } |
| mSendUpdateImminent.store(true); |
| mFlinger.disableExpensiveRendering(); |
| }); |
| } |
| } |
| |
| void PowerAdvisor::init() { |
| // Defer starting the screen update timer until SurfaceFlinger finishes construction. |
| if (mScreenUpdateTimer) { |
| mScreenUpdateTimer->start(); |
| } |
| } |
| |
| void PowerAdvisor::onBootFinished() { |
| mBootFinished.store(true); |
| } |
| |
| void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) { |
| if (expected) { |
| mExpensiveDisplays.insert(displayId); |
| } else { |
| mExpensiveDisplays.erase(displayId); |
| } |
| |
| const bool expectsExpensiveRendering = !mExpensiveDisplays.empty(); |
| if (mNotifiedExpensiveRendering != expectsExpensiveRendering) { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* const halWrapper = getPowerHal(); |
| if (halWrapper == nullptr) { |
| return; |
| } |
| |
| if (!halWrapper->setExpensiveRendering(expectsExpensiveRendering)) { |
| // The HAL has become unavailable; attempt to reconnect later |
| mReconnectPowerHal = true; |
| return; |
| } |
| |
| mNotifiedExpensiveRendering = expectsExpensiveRendering; |
| } |
| } |
| |
| void PowerAdvisor::notifyDisplayUpdateImminent() { |
| // Only start sending this notification once the system has booted so we don't introduce an |
| // early-boot dependency on Power HAL |
| if (!mBootFinished.load()) { |
| return; |
| } |
| |
| if (mSendUpdateImminent.exchange(false)) { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* const halWrapper = getPowerHal(); |
| if (halWrapper == nullptr) { |
| return; |
| } |
| |
| if (!halWrapper->notifyDisplayUpdateImminent()) { |
| // The HAL has become unavailable; attempt to reconnect later |
| mReconnectPowerHal = true; |
| return; |
| } |
| |
| if (mScreenUpdateTimer) { |
| mScreenUpdateTimer->reset(); |
| } else { |
| // If we don't have a screen update timer, then we don't throttle power hal calls so |
| // flip this bit back to allow for calling into power hal again. |
| mSendUpdateImminent.store(true); |
| } |
| } |
| |
| if (mScreenUpdateTimer) { |
| mLastScreenUpdatedTime.store(systemTime()); |
| } |
| } |
| |
| // checks both if it supports and if it's enabled |
| bool PowerAdvisor::usePowerHintSession() { |
| // uses cached value since the underlying support and flag are unlikely to change at runtime |
| return mPowerHintEnabled.value_or(false) && supportsPowerHintSession(); |
| } |
| |
| bool PowerAdvisor::supportsPowerHintSession() { |
| // cache to avoid needing lock every time |
| if (!mSupportsPowerHint.has_value()) { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* const halWrapper = getPowerHal(); |
| mSupportsPowerHint = halWrapper->supportsPowerHintSession(); |
| } |
| return *mSupportsPowerHint; |
| } |
| |
| bool PowerAdvisor::isPowerHintSessionRunning() { |
| return mPowerHintSessionRunning; |
| } |
| |
| void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) { |
| if (!usePowerHintSession()) { |
| ALOGV("Power hint session target duration cannot be set, skipping"); |
| return; |
| } |
| { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* const halWrapper = getPowerHal(); |
| if (halWrapper != nullptr) { |
| halWrapper->setTargetWorkDuration(targetDuration); |
| } |
| } |
| } |
| |
| void PowerAdvisor::sendActualWorkDuration() { |
| if (!mBootFinished || !usePowerHintSession()) { |
| ALOGV("Actual work duration power hint cannot be sent, skipping"); |
| return; |
| } |
| const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false); |
| if (actualDuration.has_value()) { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* const halWrapper = getPowerHal(); |
| if (halWrapper != nullptr) { |
| halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(), |
| systemTime()); |
| } |
| } |
| } |
| |
| void PowerAdvisor::sendPredictedWorkDuration() { |
| if (!mBootFinished || !usePowerHintSession()) { |
| ALOGV("Actual work duration power hint cannot be sent, skipping"); |
| return; |
| } |
| |
| const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true); |
| |
| if (predictedDuration.has_value()) { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* const halWrapper = getPowerHal(); |
| if (halWrapper != nullptr) { |
| halWrapper->sendActualWorkDuration(*predictedDuration, systemTime()); |
| } |
| } |
| } |
| |
| void PowerAdvisor::enablePowerHint(bool enabled) { |
| mPowerHintEnabled = enabled; |
| } |
| |
| bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) { |
| if (!usePowerHintSession()) { |
| ALOGI("Power hint session cannot be started, skipping"); |
| } |
| { |
| std::lock_guard lock(mPowerHalMutex); |
| HalWrapper* halWrapper = getPowerHal(); |
| if (halWrapper != nullptr && usePowerHintSession()) { |
| halWrapper->setPowerHintSessionThreadIds(threadIds); |
| mPowerHintSessionRunning = halWrapper->startPowerHintSession(); |
| } |
| } |
| return mPowerHintSessionRunning; |
| } |
| |
| void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) { |
| DisplayTimingData& displayData = mDisplayTimingData[displayId]; |
| if (displayData.gpuEndFenceTime) { |
| nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime(); |
| if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) { |
| for (auto&& [_, otherDisplayData] : mDisplayTimingData) { |
| // If the previous display started before us but ended after we should have |
| // started, then it likely delayed our start time and we must compensate for that. |
| // Displays finishing earlier should have already made their way through this call |
| // and swapped their timing into "lastValid" from "latest", so we check that here. |
| if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue; |
| if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) && |
| (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) { |
| displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime; |
| break; |
| } |
| } |
| displayData.lastValidGpuStartTime = displayData.gpuStartTime; |
| displayData.lastValidGpuEndTime = signalTime; |
| } |
| } |
| displayData.gpuEndFenceTime = std::move(fenceTime); |
| displayData.gpuStartTime = systemTime(); |
| } |
| |
| void PowerAdvisor::setValidateTiming(DisplayId displayId, nsecs_t validateStartTime, |
| nsecs_t validateEndTime) { |
| DisplayTimingData& displayData = mDisplayTimingData[displayId]; |
| displayData.validateStartTime = validateStartTime; |
| displayData.validateEndTime = validateEndTime; |
| } |
| |
| void PowerAdvisor::setPresentTiming(DisplayId displayId, nsecs_t presentStartTime, |
| nsecs_t presentEndTime) { |
| DisplayTimingData& displayData = mDisplayTimingData[displayId]; |
| displayData.presentStartTime = presentStartTime; |
| displayData.presentEndTime = presentEndTime; |
| } |
| |
| void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) { |
| mDisplayTimingData[displayId].skippedValidate = skipped; |
| } |
| |
| void PowerAdvisor::setRequiresClientComposition(DisplayId displayId, |
| bool requiresClientComposition) { |
| mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition; |
| } |
| |
| void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) { |
| mExpectedPresentTimes.append(expectedPresentTime); |
| } |
| |
| void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) { |
| mFrameDelayDuration = frameDelayDuration; |
| } |
| |
| void PowerAdvisor::setPresentDelayedTime( |
| DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) { |
| mDisplayTimingData[displayId].presentDelayedTime = |
| (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime(); |
| } |
| |
| void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) { |
| mCommitStartTimes.append(commitStartTime); |
| } |
| |
| void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) { |
| mLastCompositeEndTime = compositeEnd; |
| // calculate the postcomp time here as well |
| std::vector<DisplayId>&& displays = getOrderedDisplayIds(&DisplayTimingData::presentEndTime); |
| DisplayTimingData& timingData = mDisplayTimingData[displays.back()]; |
| mLastPostcompDuration = compositeEnd - |
| (timingData.skippedValidate ? *timingData.validateEndTime : *timingData.presentEndTime); |
| } |
| |
| void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) { |
| mDisplayIds = displayIds; |
| } |
| |
| void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) { |
| mTotalFrameTargetDuration = targetDuration; |
| } |
| |
| std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( |
| std::optional<nsecs_t> DisplayTimingData::*sortBy) { |
| std::vector<DisplayId> sortedDisplays; |
| std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays), |
| [&](DisplayId id) { |
| return mDisplayTimingData.count(id) && |
| (mDisplayTimingData[id].*sortBy).has_value(); |
| }); |
| std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) { |
| return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy); |
| }); |
| return sortedDisplays; |
| } |
| |
| std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { |
| if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) { |
| return std::nullopt; |
| } |
| |
| // Tracks when we finish presenting to hwc |
| nsecs_t estimatedEndTime = mCommitStartTimes[0]; |
| |
| // How long we spent this frame not doing anything, waiting for fences or vsync |
| nsecs_t idleDuration = 0; |
| |
| // Most recent previous gpu end time in the current frame, probably from a prior display, used |
| // as the start time for the next gpu operation if it ran over time since it probably blocked |
| std::optional<nsecs_t> previousValidGpuEndTime; |
| |
| // The currently estimated gpu end time for the frame, |
| // used to accumulate gpu time as we iterate over the active displays |
| std::optional<nsecs_t> estimatedGpuEndTime; |
| |
| // If we're predicting at the start of the frame, we use last frame as our reference point |
| // If we're predicting at the end of the frame, we use the current frame as a reference point |
| nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]); |
| |
| // We need an idea of when the last present fence fired and how long it made us wait |
| // If we're predicting at the start of the frame, we want frame n-2's present fence time |
| // If we're predicting at the end of the frame we want frame n-1's present time |
| nsecs_t referenceFenceTime = |
| (earlyHint ? mExpectedPresentTimes[-2] : mExpectedPresentTimes[-1]); |
| // The timing info for the previously calculated display, if there was one |
| std::optional<DisplayTimeline> previousDisplayReferenceTiming; |
| std::vector<DisplayId>&& displayIds = |
| getOrderedDisplayIds(&DisplayTimingData::presentStartTime); |
| DisplayTimeline referenceTiming, estimatedTiming; |
| |
| // Iterate over the displays in the same order they are presented |
| for (DisplayId displayId : displayIds) { |
| if (mDisplayTimingData.count(displayId) == 0) { |
| continue; |
| } |
| |
| auto& displayData = mDisplayTimingData.at(displayId); |
| referenceTiming = displayData.calculateDisplayTimeline(referenceFenceTime); |
| |
| // If this is the first display, add the pre-present time to the total |
| if (!previousDisplayReferenceTiming.has_value()) { |
| estimatedEndTime += referenceTiming.prePresentTime - referenceFrameStartTime; |
| } else { // Otherwise add last display's postprocessing time to the total |
| estimatedEndTime += referenceTiming.prePresentTime - |
| previousDisplayReferenceTiming->postPresentTime; |
| } |
| |
| estimatedTiming = referenceTiming.estimateTimelineFromReference(mExpectedPresentTimes[-1], |
| estimatedEndTime); |
| // Update predicted present finish time with this display's present time |
| estimatedEndTime = estimatedTiming.postPresentTime; |
| |
| // Track how long we spent waiting for the fence, can be excluded from the timing estimate |
| idleDuration += estimatedTiming.probablyWaitsForFence |
| ? mExpectedPresentTimes[-1] - estimatedTiming.preFenceWaitTime |
| : 0; |
| |
| // Track how long we spent waiting to present, can be excluded from the timing estimate |
| idleDuration += |
| !earlyHint ? referenceTiming.presentStartTime - referenceTiming.prePresentTime : 0; |
| |
| // Estimate the reference frame's gpu timing |
| auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime); |
| if (gpuTiming.has_value()) { |
| previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration; |
| |
| // Estimate the prediction frame's gpu end time from the reference frame |
| estimatedGpuEndTime = |
| std::max(estimatedTiming.prePresentTime, estimatedGpuEndTime.value_or(0)) + |
| gpuTiming->duration; |
| } |
| previousDisplayReferenceTiming = referenceTiming; |
| } |
| ATRACE_INT64("Idle duration", idleDuration); |
| |
| // Don't count time spent idly waiting in the estimate as we could do more work in that time |
| estimatedEndTime -= idleDuration; |
| |
| // We finish the frame when both present and the gpu are done, so wait for the later of the two |
| // Also add the frame delay duration since the target did not move while we were delayed |
| nsecs_t totalDuration = mFrameDelayDuration + |
| std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0]; |
| |
| // We finish SurfaceFlinger when post-composition finishes, so add that in here |
| nsecs_t flingerDuration = estimatedEndTime + mLastPostcompDuration - mCommitStartTimes[0]; |
| nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); |
| |
| return std::make_optional(combinedDuration); |
| } |
| |
| nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) { |
| nsecs_t targetDuration; |
| { |
| std::lock_guard lock(mPowerHalMutex); |
| targetDuration = *getPowerHal()->getTargetWorkDuration(); |
| } |
| if (!mTotalFrameTargetDuration.has_value()) return flingerDuration; |
| |
| // Normalize total to the flinger target (vsync period) since that's how often we actually send |
| // hints |
| nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration; |
| return std::max(flingerDuration, normalizedTotalDuration); |
| } |
| |
| PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference( |
| nsecs_t fenceTime, nsecs_t displayStartTime) { |
| DisplayTimeline estimated; |
| estimated.prePresentTime = displayStartTime; |
| |
| // We don't predict waiting for vsync alignment yet |
| estimated.presentStartTime = estimated.prePresentTime; |
| |
| // For now just re-use last frame's post-present duration and assume it will not change much |
| // How long we expect to run before we start waiting for the fence |
| estimated.preFenceWaitTime = estimated.presentStartTime + (preFenceWaitTime - presentStartTime); |
| estimated.probablyWaitsForFence = fenceTime > estimated.preFenceWaitTime; |
| estimated.postPresentTime = postFenceDuration + |
| (estimated.probablyWaitsForFence ? fenceTime : estimated.preFenceWaitTime); |
| return estimated; |
| } |
| |
| PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline( |
| nsecs_t fenceTime) { |
| DisplayTimeline timeline; |
| // How long between calling present from flinger and trying to wait on the fence in HWC |
| const nsecs_t preFenceWaitDelay = |
| (skippedValidate ? kPrefenceDelaySkippedValidate : kPrefenceDelayValidated).count(); |
| |
| // Did our reference frame wait for an earliest present time before calling the HWC |
| const bool waitedOnPresentTime = presentDelayedTime.has_value() && |
| *presentDelayedTime > *presentStartTime && *presentDelayedTime < *presentEndTime; |
| |
| // Use validate start here if we skipped it because we did validate + present together |
| timeline.prePresentTime = skippedValidate ? *validateStartTime : *presentStartTime; |
| |
| // Use validate end here if we skipped it because we did validate + present together |
| timeline.postPresentTime = skippedValidate ? *validateEndTime : *presentEndTime; |
| |
| // When we think we started waiting for the fence after calling into present |
| // This is after any time spent waiting for the earliest present time |
| timeline.presentStartTime = |
| (waitedOnPresentTime ? *presentDelayedTime : timeline.prePresentTime); |
| timeline.preFenceWaitTime = timeline.presentStartTime + preFenceWaitDelay; |
| timeline.probablyWaitsForFence = |
| fenceTime > timeline.preFenceWaitTime && fenceTime < timeline.postPresentTime; |
| |
| // How long we ran after we finished waiting for the fence but before present happened |
| timeline.postFenceDuration = timeline.postPresentTime - |
| (timeline.probablyWaitsForFence ? fenceTime : timeline.preFenceWaitTime); |
| return timeline; |
| } |
| |
| std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming( |
| std::optional<nsecs_t> previousEnd) { |
| if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { |
| return std::nullopt; |
| } |
| const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime); |
| const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime(); |
| nsecs_t gpuDuration = 0; |
| if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID && |
| latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) { |
| // If we know how long the most recent gpu duration was, use that |
| gpuDuration = latestGpuEndTime - latestGpuStartTime; |
| } else if (lastValidGpuEndTime.has_value()) { |
| // If we don't have the fence data, use the most recent information we do have |
| gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime; |
| if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) { |
| // If pending but went over the previous duration, use current time as the end |
| gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime); |
| } |
| } |
| return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime}; |
| } |
| |
| class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper { |
| public: |
| HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {} |
| |
| ~HidlPowerHalWrapper() override = default; |
| |
| static std::unique_ptr<HalWrapper> connect() { |
| // Power HAL 1.3 is not guaranteed to be available, thus we need to query |
| // Power HAL 1.0 first and try to cast it to Power HAL 1.3. |
| sp<V1_3::IPower> powerHal = nullptr; |
| sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService(); |
| if (powerHal_1_0 != nullptr) { |
| // Try to cast to Power HAL 1.3 |
| powerHal = V1_3::IPower::castFrom(powerHal_1_0); |
| if (powerHal == nullptr) { |
| ALOGW("No Power HAL 1.3 service in system, disabling PowerAdvisor"); |
| } else { |
| ALOGI("Loaded Power HAL 1.3 service"); |
| } |
| } else { |
| ALOGW("No Power HAL found, disabling PowerAdvisor"); |
| } |
| |
| if (powerHal == nullptr) { |
| return nullptr; |
| } |
| |
| return std::make_unique<HidlPowerHalWrapper>(std::move(powerHal)); |
| } |
| |
| bool setExpensiveRendering(bool enabled) override { |
| ALOGV("HIDL setExpensiveRendering %s", enabled ? "T" : "F"); |
| auto ret = mPowerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, enabled); |
| if (ret.isOk()) { |
| traceExpensiveRendering(enabled); |
| } |
| return ret.isOk(); |
| } |
| |
| bool notifyDisplayUpdateImminent() override { |
| // Power HAL 1.x doesn't have a notification for this |
| ALOGV("HIDL notifyUpdateImminent received but can't send"); |
| return true; |
| } |
| |
| bool supportsPowerHintSession() override { return false; } |
| |
| bool isPowerHintSessionRunning() override { return false; } |
| |
| void restartPowerHintSession() override {} |
| |
| void setPowerHintSessionThreadIds(const std::vector<int32_t>&) override {} |
| |
| bool startPowerHintSession() override { return false; } |
| |
| void setTargetWorkDuration(int64_t) override {} |
| |
| void sendActualWorkDuration(int64_t, nsecs_t) override {} |
| |
| bool shouldReconnectHAL() override { return false; } |
| |
| std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; } |
| |
| std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; } |
| |
| private: |
| const sp<V1_3::IPower> mPowerHal = nullptr; |
| }; |
| |
| AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) { |
| auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering); |
| if (!ret.isOk()) { |
| mHasExpensiveRendering = false; |
| } |
| |
| ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, &mHasDisplayUpdateImminent); |
| if (!ret.isOk()) { |
| mHasDisplayUpdateImminent = false; |
| } |
| |
| mSupportsPowerHint = checkPowerHintSessionSupported(); |
| |
| mAllowedActualDeviation = |
| base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", |
| std::chrono::nanoseconds(250us).count()); |
| } |
| |
| AidlPowerHalWrapper::~AidlPowerHalWrapper() { |
| if (mPowerHintSession != nullptr) { |
| mPowerHintSession->close(); |
| mPowerHintSession = nullptr; |
| } |
| } |
| |
| std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() { |
| // This only waits if the service is actually declared |
| sp<IPower> powerHal = waitForVintfService<IPower>(); |
| if (powerHal == nullptr) { |
| return nullptr; |
| } |
| ALOGI("Loaded AIDL Power HAL service"); |
| |
| return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal)); |
| } |
| |
| bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) { |
| ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F"); |
| if (!mHasExpensiveRendering) { |
| ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it"); |
| return true; |
| } |
| |
| auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled); |
| if (ret.isOk()) { |
| traceExpensiveRendering(enabled); |
| } |
| return ret.isOk(); |
| } |
| |
| bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { |
| ALOGV("AIDL notifyDisplayUpdateImminent"); |
| if (!mHasDisplayUpdateImminent) { |
| ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); |
| return true; |
| } |
| |
| auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0); |
| return ret.isOk(); |
| } |
| |
| // Only version 2+ of the aidl supports power hint sessions, hidl has no support |
| bool AidlPowerHalWrapper::supportsPowerHintSession() { |
| return mSupportsPowerHint; |
| } |
| |
| bool AidlPowerHalWrapper::checkPowerHintSessionSupported() { |
| int64_t unused; |
| // Try to get preferred rate to determine if hint sessions are supported |
| // We check for isOk not EX_UNSUPPORTED_OPERATION to lump together errors |
| return mPowerHal->getHintSessionPreferredRate(&unused).isOk(); |
| } |
| |
| bool AidlPowerHalWrapper::isPowerHintSessionRunning() { |
| return mPowerHintSession != nullptr; |
| } |
| |
| void AidlPowerHalWrapper::closePowerHintSession() { |
| if (mPowerHintSession != nullptr) { |
| mPowerHintSession->close(); |
| mPowerHintSession = nullptr; |
| } |
| } |
| |
| void AidlPowerHalWrapper::restartPowerHintSession() { |
| closePowerHintSession(); |
| startPowerHintSession(); |
| } |
| |
| void AidlPowerHalWrapper::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) { |
| if (threadIds != mPowerHintThreadIds) { |
| mPowerHintThreadIds = threadIds; |
| if (isPowerHintSessionRunning()) { |
| restartPowerHintSession(); |
| } |
| } |
| } |
| |
| bool AidlPowerHalWrapper::startPowerHintSession() { |
| if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) { |
| ALOGV("Cannot start power hint session, skipping"); |
| return false; |
| } |
| auto ret = |
| mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()), |
| mPowerHintThreadIds, mTargetDuration, &mPowerHintSession); |
| if (!ret.isOk()) { |
| ALOGW("Failed to start power hint session with error: %s", |
| ret.exceptionToString(ret.exceptionCode()).c_str()); |
| } else { |
| mLastTargetDurationSent = mTargetDuration; |
| } |
| return isPowerHintSessionRunning(); |
| } |
| |
| void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) { |
| ATRACE_CALL(); |
| mTargetDuration = targetDuration; |
| if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration); |
| if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) { |
| ALOGV("Sending target time: %" PRId64 "ns", targetDuration); |
| mLastTargetDurationSent = targetDuration; |
| auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration); |
| if (!ret.isOk()) { |
| ALOGW("Failed to set power hint target work duration with error: %s", |
| ret.exceptionMessage().c_str()); |
| mShouldReconnectHal = true; |
| } |
| } |
| } |
| |
| bool AidlPowerHalWrapper::shouldReportActualDurations() { |
| // Report if we have never reported before or are approaching a stale session |
| if (!mLastActualDurationSent.has_value() || |
| (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) { |
| return true; |
| } |
| |
| if (!mActualDuration.has_value()) { |
| return false; |
| } |
| // Report if the change in actual duration exceeds the threshold |
| return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation; |
| } |
| |
| void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) { |
| ATRACE_CALL(); |
| |
| if (actualDuration < 0 || !isPowerHintSessionRunning()) { |
| ALOGV("Failed to send actual work duration, skipping"); |
| return; |
| } |
| const nsecs_t reportedDuration = actualDuration; |
| |
| mActualDuration = reportedDuration; |
| WorkDuration duration; |
| duration.durationNanos = reportedDuration; |
| duration.timeStampNanos = timestamp; |
| mPowerHintQueue.push_back(duration); |
| |
| if (sTraceHintSessionData) { |
| ATRACE_INT64("Measured duration", actualDuration); |
| ATRACE_INT64("Target error term", actualDuration - mTargetDuration); |
| |
| ATRACE_INT64("Reported duration", reportedDuration); |
| ATRACE_INT64("Reported target", mLastTargetDurationSent); |
| ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent); |
| } |
| |
| ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 |
| " with error: %" PRId64, |
| reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent); |
| |
| // This rate limiter queues similar duration reports to the powerhal into |
| // batches to avoid excessive binder calls. The criteria to send a given batch |
| // are outlined in shouldReportActualDurationsNow() |
| if (shouldReportActualDurations()) { |
| ALOGV("Sending hint update batch"); |
| mLastActualReportTimestamp = systemTime(); |
| auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); |
| if (!ret.isOk()) { |
| ALOGW("Failed to report actual work durations with error: %s", |
| ret.exceptionMessage().c_str()); |
| mShouldReconnectHal = true; |
| } |
| mPowerHintQueue.clear(); |
| // We save the actual duration here for rate limiting |
| mLastActualDurationSent = actualDuration; |
| } |
| } |
| |
| bool AidlPowerHalWrapper::shouldReconnectHAL() { |
| return mShouldReconnectHal; |
| } |
| |
| std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() { |
| return mPowerHintThreadIds; |
| } |
| |
| std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() { |
| return mTargetDuration; |
| } |
| |
| void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) { |
| mAllowedActualDeviation = allowedDeviation; |
| } |
| |
| const bool AidlPowerHalWrapper::sTraceHintSessionData = |
| base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false); |
| |
| PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { |
| static std::unique_ptr<HalWrapper> sHalWrapper = nullptr; |
| static bool sHasHal = true; |
| |
| if (!sHasHal) { |
| return nullptr; |
| } |
| |
| // Grab old hint session values before we destroy any existing wrapper |
| std::vector<int32_t> oldPowerHintSessionThreadIds; |
| std::optional<int64_t> oldTargetWorkDuration; |
| |
| if (sHalWrapper != nullptr) { |
| oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds(); |
| oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration(); |
| } |
| |
| // If we used to have a HAL, but it stopped responding, attempt to reconnect |
| if (mReconnectPowerHal) { |
| sHalWrapper = nullptr; |
| mReconnectPowerHal = false; |
| } |
| |
| if (sHalWrapper != nullptr) { |
| auto wrapper = sHalWrapper.get(); |
| // If the wrapper is fine, return it, but if it indicates a reconnect, remake it |
| if (!wrapper->shouldReconnectHAL()) { |
| return wrapper; |
| } |
| ALOGD("Reconnecting Power HAL"); |
| sHalWrapper = nullptr; |
| } |
| |
| // At this point, we know for sure there is no running session |
| mPowerHintSessionRunning = false; |
| |
| // First attempt to connect to the AIDL Power HAL |
| sHalWrapper = AidlPowerHalWrapper::connect(); |
| |
| // If that didn't succeed, attempt to connect to the HIDL Power HAL |
| if (sHalWrapper == nullptr) { |
| sHalWrapper = HidlPowerHalWrapper::connect(); |
| } else { |
| ALOGD("Successfully connecting AIDL Power HAL"); |
| // If AIDL, pass on any existing hint session values |
| sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds); |
| // Only set duration and start if duration is defined |
| if (oldTargetWorkDuration.has_value()) { |
| sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration); |
| // Only start if possible to run and both threadids and duration are defined |
| if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) { |
| mPowerHintSessionRunning = sHalWrapper->startPowerHintSession(); |
| } |
| } |
| } |
| |
| // If we make it to this point and still don't have a HAL, it's unlikely we |
| // will, so stop trying |
| if (sHalWrapper == nullptr) { |
| sHasHal = false; |
| } |
| |
| return sHalWrapper.get(); |
| } |
| |
| } // namespace impl |
| } // namespace Hwc2 |
| } // namespace android |