diff options
20 files changed, 730 insertions, 214 deletions
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index db2fd1b500..2203639b1a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -27,6 +27,7 @@ #include <compositionengine/LayerFE.h> #include <renderengine/LayerSettings.h> #include <ui/Fence.h> +#include <ui/FenceTime.h> #include <ui/GraphicTypes.h> #include <ui/LayerStack.h> #include <ui/Region.h> @@ -311,6 +312,8 @@ protected: const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0; virtual void setExpensiveRenderingExpected(bool enabled) = 0; + virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0; + virtual bool isPowerHintSessionEnabled() = 0; virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0; virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 61a0e6a356..fa7bc5da7f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -89,6 +89,8 @@ public: std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const; private: + bool isPowerHintSessionEnabled() override; + void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; DisplayId mId; bool mIsDisconnected = false; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 31c51e6a8d..df721cdc89 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -140,6 +140,8 @@ protected: std::vector<LayerFE*> &outLayerFEs) override; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; + void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; + bool isPowerHintSessionEnabled() override; void dumpBase(std::string&) const; // Implemented by the final implementation for the final state it uses. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index cb9fbad8dd..2a04949cff 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -133,6 +133,8 @@ public: MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&)); MOCK_METHOD1(setPredictCompositionStrategy, void(bool)); MOCK_METHOD1(setTreat170mAsSrgb, void(bool)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, ()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index b79b46b6ee..cccf9072b1 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -243,11 +243,14 @@ bool Display::chooseCompositionStrategy( return false; } + const nsecs_t startTime = systemTime(); + // Get any composition changes requested by the HWC device, and apply them. std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); + const bool requiresClientComposition = anyLayersRequireClientComposition(); if (status_t result = - hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(), + hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition, getState().earliestPresentTime, getState().previousPresentFence, getState().expectedPresentTime, outChanges); @@ -257,6 +260,11 @@ bool Display::chooseCompositionStrategy( return false; } + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setValidateTiming(mId, startTime, systemTime()); + mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + } + return true; } @@ -356,9 +364,24 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { } auto& hwc = getCompositionEngine().getHwComposer(); + + const nsecs_t startTime = systemTime(); + + if (isPowerHintSessionEnabled()) { + if (!getCompositionEngine().getHwComposer().getComposer()->isSupported( + Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && + getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + mPowerAdvisor->setPresentDelayedTime(mId, getState().earliestPresentTime); + } + } + hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime, getState().previousPresentFence); + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setPresentTiming(mId, startTime, systemTime()); + } + fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); // TODO(b/121291683): Change HWComposer call to return entire map @@ -384,6 +407,14 @@ void Display::setExpensiveRenderingExpected(bool enabled) { } } +bool Display::isPowerHintSessionEnabled() { + return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession(); +} + +void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) { + mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence)); +} + void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) { // We only need to actually compose the display if: @@ -396,6 +427,13 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre } impl::Output::finishFrame(refreshArgs, std::move(result)); + + if (isPowerHintSessionEnabled()) { + auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); + } + } } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index c3385a8a8b..070b285f03 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -1099,6 +1099,10 @@ void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositi return; } + if (isPowerHintSessionEnabled()) { + // get fence end time to know when gpu is complete in display + setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get())))); + } // swap buffers (presentation) mRenderSurface->queueBuffer(std::move(*optReadyFence)); } @@ -1403,6 +1407,14 @@ void Output::setExpensiveRenderingExpected(bool) { // The base class does nothing with this call. } +void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) { + // The base class does nothing with this call. +} + +bool Output::isPowerHintSessionEnabled() { + return false; +} + void Output::postFramebuffer() { ATRACE_CALL(); ALOGV(__FUNCTION__); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 0e5a7b6a99..344fea3331 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -169,6 +169,7 @@ struct DisplayTestCommon : public testing::Test { EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); + EXPECT_CALL(mPowerAdvisor, usePowerHintSession()).WillRepeatedly(Return(false)); } DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() { diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 9b12b08063..d7704a893d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -137,6 +137,7 @@ public: MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override)); MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index 50adcfb827..c8b6d443af 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -38,11 +38,31 @@ public: MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp), - (override)); + MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (), (override)); + MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override)); + MOCK_METHOD(void, setGpuFenceTime, + (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); + MOCK_METHOD(void, setValidateTiming, + (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (override)); + MOCK_METHOD(void, setPresentTiming, + (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); + MOCK_METHOD(void, setRequiresClientComposition, + (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); + MOCK_METHOD(void, setPresentDelayedTime, + (DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 063726b5e2..505f94ee84 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -3319,6 +3319,9 @@ struct OutputComposeSurfacesTest : public testing::Test { MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence), + (override)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override)); }; OutputComposeSurfacesTest() { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 0da8ecea85..a6aee1f2f5 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -738,6 +738,13 @@ ftl::Future<status_t> HWComposer::setDisplayBrightness( }); } +bool HWComposer::getValidateSkipped(HalDisplayId displayId) const { + if (mDisplayData.count(displayId) == 0) { + return false; + } + return mDisplayData.at(displayId).validateWasSkipped; +} + status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId, hal::HWConfigId displayModeId) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 4c0ecd8502..92a8f30f1b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -199,6 +199,9 @@ public: PhysicalDisplayId, float brightness, float brightnessNits, const Hwc2::Composer::DisplayBrightnessOptions&) = 0; + // Get whether the display skipped validation on the latest present + virtual bool getValidateSkipped(HalDisplayId displayId) const = 0; + // Events handling --------------------------------------------------------- // Returns stable display ID (and display name on connection of new or previously disconnected @@ -397,6 +400,8 @@ public: status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override; + bool getValidateSkipped(HalDisplayId displayId) const override; + // Composer 2.4 ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index b5678b49b8..4c0a942976 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -196,7 +196,7 @@ bool PowerAdvisor::isPowerHintSessionRunning() { return mPowerHintSessionRunning; } -void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) { +void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) { if (!usePowerHintSession()) { ALOGV("Power hint session target duration cannot be set, skipping"); return; @@ -205,26 +205,44 @@ void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count()); + halWrapper->setTargetWorkDuration(targetDuration); } } } -void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) { +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(actualDurationNanos, timeStampNanos); + halWrapper->sendActualWorkDuration(*predictedDuration, systemTime()); } } } -// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished void PowerAdvisor::enablePowerHint(bool enabled) { mPowerHintEnabled = enabled; } @@ -244,6 +262,282 @@ bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) 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)) {} @@ -325,6 +619,10 @@ AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::m } mSupportsPowerHint = checkPowerHintSessionSupported(); + + mAllowedActualDeviation = + base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", + std::chrono::nanoseconds(250us).count()); } AidlPowerHalWrapper::~AidlPowerHalWrapper() { @@ -332,7 +630,7 @@ AidlPowerHalWrapper::~AidlPowerHalWrapper() { mPowerHintSession->close(); mPowerHintSession = nullptr; } -}; +} std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() { // This only waits if the service is actually declared @@ -370,7 +668,7 @@ bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { return ret.isOk(); } -// only version 2+ of the aidl supports power hint sessions, hidl has no support +// Only version 2+ of the aidl supports power hint sessions, hidl has no support bool AidlPowerHalWrapper::supportsPowerHintSession() { return mSupportsPowerHint; } @@ -424,30 +722,14 @@ bool AidlPowerHalWrapper::startPowerHintSession() { return isPowerHintSessionRunning(); } -bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) { - if (targetDurationNanos <= 0) { - return false; - } - // report if the change in target from our last submission to now exceeds the threshold - return abs(1.0 - - static_cast<double>(mLastTargetDurationSent) / - static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent; -} - -void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { +void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) { ATRACE_CALL(); - mTargetDuration = targetDurationNanos; - if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos); - if (!sNormalizeTarget && isPowerHintSessionRunning() && - shouldSetTargetDuration(targetDurationNanos)) { - if (mLastActualDurationSent.has_value()) { - // update the error term here since we are actually sending an update to powerhal - if (sTraceHintSessionData) - ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent); - } - ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos); - mLastTargetDurationSent = targetDurationNanos; - auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos); + 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()); @@ -456,8 +738,8 @@ void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { } } -bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { - // report if we have never reported before or are approaching a stale session +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; @@ -466,65 +748,42 @@ bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { if (!mActualDuration.has_value()) { return false; } - - // duration of most recent timing - const double mostRecentActualDuration = static_cast<double>(*mActualDuration); - // duration of the last timing actually reported to the powerhal - const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent); - - // report if the change in duration from then to now exceeds the threshold - return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >= - kAllowedActualDeviationPercent; + // Report if the change in actual duration exceeds the threshold + return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation; } -void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, - nsecs_t timeStampNanos) { +void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) { ATRACE_CALL(); - if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) { + if (actualDuration < 0 || !isPowerHintSessionRunning()) { ALOGV("Failed to send actual work duration, skipping"); return; } - nsecs_t reportedDuration = actualDurationNanos; + const nsecs_t reportedDuration = actualDuration; - // normalize the sent values to a pre-set target - if (sNormalizeTarget) { - reportedDuration += mLastTargetDurationSent - mTargetDuration; - } else { - // when target duration change is within deviation and not updated, adjust the actual - // duration proportionally based on the difference, e.g. if new target is 5ms longer than - // last reported but actual duration is the same as last target, we want to report a smaller - // actual work duration now to indicate that we are overshooting - if (mLastTargetDurationSent != kDefaultTarget.count() && mTargetDuration != 0) { - reportedDuration = - static_cast<int64_t>(static_cast<long double>(mLastTargetDurationSent) / - mTargetDuration * actualDurationNanos); - mActualDuration = reportedDuration; - } - } mActualDuration = reportedDuration; WorkDuration duration; duration.durationNanos = reportedDuration; - duration.timeStampNanos = timeStampNanos; + duration.timeStampNanos = timestamp; mPowerHintQueue.push_back(duration); if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", actualDurationNanos); - ATRACE_INT64("Target error term", mTargetDuration - actualDurationNanos); + 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", mLastTargetDurationSent - reportedDuration); + ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent); } ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 " with error: %" PRId64, - reportedDuration, mLastTargetDurationSent, mLastTargetDurationSent - reportedDuration); + 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 (shouldReportActualDurationsNow()) { + if (shouldReportActualDurations()) { ALOGV("Sending hint update batch"); mLastActualReportTimestamp = systemTime(); auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); @@ -534,8 +793,8 @@ void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, mShouldReconnectHal = true; } mPowerHintQueue.clear(); - // we save the non-normalized value here to detect % changes - mLastActualDurationSent = reportedDuration; + // We save the actual duration here for rate limiting + mLastActualDurationSent = actualDuration; } } @@ -551,12 +810,13 @@ 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); -const bool AidlPowerHalWrapper::sNormalizeTarget = - base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), false); - PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { static std::unique_ptr<HalWrapper> sHalWrapper = nullptr; static bool sHasHal = true; @@ -565,7 +825,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { return nullptr; } - // grab old hint session values before we destroy any existing wrapper + // Grab old hint session values before we destroy any existing wrapper std::vector<int32_t> oldPowerHintSessionThreadIds; std::optional<int64_t> oldTargetWorkDuration; @@ -582,7 +842,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { if (sHalWrapper != nullptr) { auto wrapper = sHalWrapper.get(); - // if the wrapper is fine, return it, but if it indicates a reconnect, remake it + // If the wrapper is fine, return it, but if it indicates a reconnect, remake it if (!wrapper->shouldReconnectHAL()) { return wrapper; } @@ -590,7 +850,7 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { sHalWrapper = nullptr; } - // at this point, we know for sure there is no running session + // At this point, we know for sure there is no running session mPowerHintSessionRunning = false; // First attempt to connect to the AIDL Power HAL @@ -601,13 +861,12 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { sHalWrapper = HidlPowerHalWrapper::connect(); } else { ALOGD("Successfully connecting AIDL Power HAL"); - // if AIDL, pass on any existing hint session values - // thread ids always safe to set + // If AIDL, pass on any existing hint session values sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds); - // only set duration and start if duration is defined + // 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 + // Only start if possible to run and both threadids and duration are defined if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) { mPowerHintSessionRunning = sHalWrapper->startPowerHintSession(); } diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 7c10e19e11..8e1e33fc71 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -18,11 +18,15 @@ #include <atomic> #include <chrono> +#include <unordered_map> #include <unordered_set> +#include <ui/DisplayId.h> +#include <ui/FenceTime.h> #include <utils/Mutex.h> #include <android/hardware/power/IPower.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -44,13 +48,47 @@ public: virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0; virtual bool isUsingExpensiveRendering() = 0; virtual void notifyDisplayUpdateImminent() = 0; + // Checks both if it supports and if it's enabled virtual bool usePowerHintSession() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; - virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0; - virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0; + // Sends a power hint that updates to the target work duration for the frame + virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + // Sends a power hint for the actual known work duration at the end of the frame + virtual void sendActualWorkDuration() = 0; + // Sends a power hint for the upcoming frame predicted from previous frame timing + virtual void sendPredictedWorkDuration() = 0; + // Sets whether the power hint session is enabled virtual void enablePowerHint(bool enabled) = 0; + // Initializes the power hint session virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0; + // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time + virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0; + // Reports the start and end times of a present call this frame for a given display + virtual void setValidateTiming(DisplayId displayId, nsecs_t validateStartTime, + nsecs_t validateEndTime) = 0; + // Reports the start and end times of a present call this frame for a given display + virtual void setPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) = 0; + virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0; + // Reports whether a display used client composition this frame + virtual void setRequiresClientComposition(DisplayId displayId, + bool requiresClientComposition) = 0; + // Reports whether a given display skipped validation this frame + virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0; + // Reports how much a given display delayed its present call this frame + virtual void setPresentDelayedTime( + DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0; + // Reports the start delay for SurfaceFlinger this frame + virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0; + // Reports the SurfaceFlinger commit start time this frame + virtual void setCommitStart(nsecs_t commitStartTime) = 0; + // Reports the SurfaceFlinger composite end time this frame + virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0; + // Reports the list of the currently active displays + virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0; + // Sets the target duration for the entire pipeline including the gpu + virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0; }; namespace impl { @@ -70,12 +108,11 @@ public: virtual void restartPowerHintSession() = 0; virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0; virtual bool startPowerHintSession() = 0; - virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0; - virtual void sendActualWorkDuration(int64_t actualDurationNanos, - nsecs_t timeStampNanos) = 0; + virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0; virtual bool shouldReconnectHAL() = 0; virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0; - virtual std::optional<int64_t> getTargetWorkDuration() = 0; + virtual std::optional<nsecs_t> getTargetWorkDuration() = 0; }; PowerAdvisor(SurfaceFlinger& flinger); @@ -89,10 +126,28 @@ public: bool usePowerHintSession() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; - void setTargetWorkDuration(int64_t targetDurationNanos) override; - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override; + void setTargetWorkDuration(nsecs_t targetDuration) override; + void sendActualWorkDuration() override; + void sendPredictedWorkDuration() override; void enablePowerHint(bool enabled) override; bool startPowerHintSession(const std::vector<int32_t>& threadIds) override; + void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime); + void setValidateTiming(DisplayId displayId, nsecs_t valiateStartTime, + nsecs_t validateEndTime) override; + void setPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) override; + void setSkippedValidate(DisplayId displayId, bool skipped) override; + void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override; + void setExpectedPresentTime(nsecs_t expectedPresentTime) override; + void setPresentDelayedTime( + DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime) override; + + void setFrameDelay(nsecs_t frameDelayDuration) override; + void setCommitStart(nsecs_t commitStartTime) override; + void setCompositeEnd(nsecs_t compositeEndTime) override; + void setDisplays(std::vector<DisplayId>& displayIds) override; + void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override; private: HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex); @@ -100,6 +155,103 @@ private: std::mutex mPowerHalMutex; std::atomic_bool mBootFinished = false; + + std::unordered_set<DisplayId> mExpensiveDisplays; + bool mNotifiedExpensiveRendering = false; + + SurfaceFlinger& mFlinger; + std::atomic_bool mSendUpdateImminent = true; + std::atomic<nsecs_t> mLastScreenUpdatedTime = 0; + std::optional<scheduler::OneShotTimer> mScreenUpdateTimer; + + // Higher-level timing data used for estimation + struct DisplayTimeline { + nsecs_t prePresentTime = -1; + nsecs_t postPresentTime = -1; + // Usually equals prePresentTime but can be delayed if we wait for the next valid vsync + nsecs_t presentStartTime = -1; + // When we think we started waiting for the fence after calling into present and + // after potentially waiting for the earliest present time + nsecs_t preFenceWaitTime = -1; + // How long we ran after we finished waiting for the fence but before present happened + nsecs_t postFenceDuration = 0; + // Are we likely to have waited for the present fence during composition + bool probablyWaitsForFence = false; + // Estimate one frame's timeline from that of a previous frame + DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime); + }; + + struct GpuTimeline { + nsecs_t duration = 0; + nsecs_t startTime = -1; + }; + + // Power hint session data recorded from the pipeline + struct DisplayTimingData { + std::unique_ptr<FenceTime> gpuEndFenceTime; + std::optional<nsecs_t> gpuStartTime; + std::optional<nsecs_t> lastValidGpuEndTime; + std::optional<nsecs_t> lastValidGpuStartTime; + std::optional<nsecs_t> presentStartTime; + std::optional<nsecs_t> presentEndTime; + std::optional<nsecs_t> validateStartTime; + std::optional<nsecs_t> validateEndTime; + std::optional<nsecs_t> presentDelayedTime; + bool usedClientComposition = false; + bool skippedValidate = false; + // Calculate high-level timing milestones from more granular display timing data + DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime); + // Estimate the gpu duration for a given display from previous gpu timing data + std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd); + }; + + template <class T, size_t N> + class RingBuffer { + std::array<T, N> elements = {}; + size_t mIndex = 0; + size_t numElements = 0; + + public: + void append(T item) { + mIndex = (mIndex + 1) % N; + numElements = std::min(N, numElements + 1); + elements[mIndex] = item; + } + bool isFull() const { return numElements == N; } + // Allows access like [0] == current, [-1] = previous, etc.. + T& operator[](int offset) { + size_t positiveOffset = + static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N)); + return elements[(mIndex + positiveOffset) % N]; + } + }; + + // Filter and sort the display ids by a given property + std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy); + // Estimates a frame's total work duration including gpu time. + // Runs either at the beginning or end of a frame, using the most recent data available + std::optional<nsecs_t> estimateWorkDuration(bool earlyHint); + // There are two different targets and actual work durations we care about, + // this normalizes them together and takes the max of the two + nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration); + + std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData; + + // Current frame's delay + nsecs_t mFrameDelayDuration = 0; + // Last frame's composite end time + nsecs_t mLastCompositeEndTime = -1; + // Last frame's post-composition duration + nsecs_t mLastPostcompDuration = 0; + // Buffer of recent commit start times + RingBuffer<nsecs_t, 2> mCommitStartTimes; + // Buffer of recent expected present times + RingBuffer<nsecs_t, 3> mExpectedPresentTimes; + // Target for the entire pipeline including gpu + std::optional<nsecs_t> mTotalFrameTargetDuration; + // Updated list of display IDs + std::vector<DisplayId> mDisplayIds; + std::optional<bool> mPowerHintEnabled; std::optional<bool> mSupportsPowerHint; bool mPowerHintSessionRunning = false; @@ -108,15 +260,11 @@ private: // go a bit over without dropping a frame, especially since we can't measure // the exact time HWC finishes composition so "actual" durations are measured // from the end of present() instead, which is a bit later. - static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms; + static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms; - std::unordered_set<DisplayId> mExpensiveDisplays; - bool mNotifiedExpensiveRendering = false; - - SurfaceFlinger& mFlinger; - std::atomic_bool mSendUpdateImminent = true; - std::atomic<nsecs_t> mLastScreenUpdatedTime = 0; - std::optional<scheduler::OneShotTimer> mScreenUpdateTimer; + // How long we expect hwc to run after the present call until it waits for the fence + static constexpr const std::chrono::nanoseconds kPrefenceDelayValidated = 150us; + static constexpr const std::chrono::nanoseconds kPrefenceDelaySkippedValidate = 250us; }; class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { @@ -133,50 +281,50 @@ public: void restartPowerHintSession() override; void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override; bool startPowerHintSession() override; - void setTargetWorkDuration(int64_t targetDurationNanos) override; - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override; + void setTargetWorkDuration(nsecs_t targetDuration) override; + void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override; bool shouldReconnectHAL() override; std::vector<int32_t> getPowerHintSessionThreadIds() override; - std::optional<int64_t> getTargetWorkDuration() override; + std::optional<nsecs_t> getTargetWorkDuration() override; private: + friend class AidlPowerHalWrapperTest; + bool checkPowerHintSessionSupported(); void closePowerHintSession(); - bool shouldReportActualDurationsNow(); - bool shouldSetTargetDuration(int64_t targetDurationNanos); + bool shouldReportActualDurations(); + + // Used for testing + void setAllowedActualDeviation(nsecs_t); const sp<hardware::power::IPower> mPowerHal = nullptr; bool mHasExpensiveRendering = false; bool mHasDisplayUpdateImminent = false; // Used to indicate an error state and need for reconstruction bool mShouldReconnectHal = false; - // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock + + // Power hint session data + + // Concurrent access for this is protected by mPowerHalMutex sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr; // Queue of actual durations saved to report std::vector<hardware::power::WorkDuration> mPowerHintQueue; - // The latest un-normalized values we have received for target and actual - int64_t mTargetDuration = kDefaultTarget.count(); - std::optional<int64_t> mActualDuration; + // The latest values we have received for target and actual + nsecs_t mTargetDuration = kDefaultTarget.count(); + std::optional<nsecs_t> mActualDuration; // The list of thread ids, stored so we can restart the session from this class if needed std::vector<int32_t> mPowerHintThreadIds; - bool mSupportsPowerHint; + bool mSupportsPowerHint = false; // Keep track of the last messages sent for rate limiter change detection - std::optional<int64_t> mLastActualDurationSent; - // timestamp of the last report we sent, used to avoid stale sessions - int64_t mLastActualReportTimestamp = 0; - int64_t mLastTargetDurationSent = kDefaultTarget.count(); - // Whether to normalize all the actual values as error terms relative to a constant target - // This saves a binder call by not setting the target, and should not affect the pid values - static const bool sNormalizeTarget; + std::optional<nsecs_t> mLastActualDurationSent; + // Timestamp of the last report we sent, used to avoid stale sessions + nsecs_t mLastActualReportTimestamp = 0; + nsecs_t mLastTargetDurationSent = kDefaultTarget.count(); + // Max amount the error term can vary without causing an actual value report + nsecs_t mAllowedActualDeviation = -1; // Whether we should emit ATRACE_INT data for hint sessions static const bool sTraceHintSessionData; - - // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedActualDeviationPercent = 0.1; - // Max percent the target duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedTargetDeviationPercent = 0.1; - // Target used for init and normalization, the actual value does not really matter - static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms; + static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms; // Amount of time after the last message was sent before the session goes stale // actually 100ms but we use 80 here to ideally avoid going stale static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6a17cd8881..5cf9558e7e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -444,6 +444,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI } mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); + + // Power hint session mode, representing which hint(s) to send: early, late, or both) + mPowerHintSessionMode = + {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true), + .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, true)}; } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -1991,12 +1996,6 @@ nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) cons bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) FTL_FAKE_GUARD(kMainThreadContext) { - // we set this once at the beginning of commit to ensure consistency throughout the whole frame - mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession(); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.commitStart = systemTime(); - } - // calculate the expected present time once and use the cached // value throughout this frame to make sure all layers are // seeing this same value. @@ -2010,10 +2009,6 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected const nsecs_t lastScheduledPresentTime = mScheduledPresentTime; mScheduledPresentTime = expectedVsyncTime; - if (mPowerHintSessionData.sessionEnabled) { - mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime - - mPowerHintSessionData.commitStart); - } const auto vsyncIn = [&] { if (!ATRACE_ENABLED()) return 0.f; return (mExpectedPresentTime - systemTime()) / 1e6f; @@ -2089,6 +2084,30 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected } } + // Save this once per commit + composite to ensure consistency + mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession(); + if (mPowerHintSessionEnabled) { + nsecs_t vsyncPeriod; + { + Mutex::Autolock lock(mStateLock); + vsyncPeriod = getVsyncPeriodFromHWC(); + } + mPowerAdvisor->setCommitStart(frameTime); + mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime); + const nsecs_t idealSfWorkDuration = + mVsyncModulator->getVsyncConfig().sfWorkDuration.count(); + // Frame delay is how long we should have minus how long we actually have + mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime)); + mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); + mPowerAdvisor->setTargetWorkDuration(vsyncPeriod); + + // Send early hint here to make sure there's not another frame pending + if (mPowerHintSessionMode.early) { + // Send a rough prediction for this frame based on last frame's timing info + mPowerAdvisor->sendPredictedWorkDuration(); + } + } + if (mTracingEnabledChanged) { mLayerTracingEnabled = mLayerTracing.isEnabled(); mTracingEnabledChanged = false; @@ -2165,16 +2184,15 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) FTL_FAKE_GUARD(kMainThreadContext) { ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.compositeStart = systemTime(); - } - compositionengine::CompositionRefreshArgs refreshArgs; const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays); refreshArgs.outputs.reserve(displays.size()); + std::vector<DisplayId> displayIds; for (const auto& [_, display] : displays) { refreshArgs.outputs.push_back(display->getCompositionDisplay()); + displayIds.push_back(display->getId()); } + mPowerAdvisor->setDisplays(displayIds); mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) { if (auto layerFE = layer->getCompositionEngineLayerFE()) refreshArgs.layers.push_back(layerFE); @@ -2222,12 +2240,15 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) mCompositionEngine->present(refreshArgs); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.presentEnd = systemTime(); - } - mTimeStats->recordFrameDuration(frameTime, systemTime()); + // Send a power hint hint after presentation is finished + if (mPowerHintSessionEnabled) { + if (mPowerHintSessionMode.late) { + mPowerAdvisor->sendActualWorkDuration(); + } + } + if (mScheduler->onPostComposition(presentTime)) { scheduleComposite(FrameHint::kNone); } @@ -2272,11 +2293,8 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) scheduleCommit(FrameHint::kNone); } - // calculate total render time for performance hinting if adpf cpu hint is enabled, - if (mPowerHintSessionData.sessionEnabled) { - const nsecs_t flingerDuration = - (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart); - mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd); + if (mPowerHintSessionEnabled) { + mPowerAdvisor->setCompositeEnd(systemTime()); } } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f14c755c2b..83134a2ebc 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1435,12 +1435,12 @@ private: return mScheduler->getLayerFramerate(now, id); } + bool mPowerHintSessionEnabled; + struct { - bool sessionEnabled = false; - nsecs_t commitStart; - nsecs_t compositeStart; - nsecs_t presentEnd; - } mPowerHintSessionData GUARDED_BY(kMainThreadContext); + bool late = false; + bool early = false; + } mPowerHintSessionMode; nsecs_t mAnimationTransactionTimeout = s2ns(5); diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp index 9ab35d741a..53de4a6e56 100644 --- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp +++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp @@ -52,6 +52,8 @@ protected: void verifyAndClearExpectations(); void sendActualWorkDurationGroup(std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend); + std::chrono::nanoseconds mAllowedDeviation; + std::chrono::nanoseconds mStaleTimeout; }; void AidlPowerHalWrapperTest::SetUp() { @@ -59,6 +61,9 @@ void AidlPowerHalWrapperTest::SetUp() { mMockSession = new NiceMock<MockIPowerHintSession>(); ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal); + mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count()); + mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation}; + mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout; } void AidlPowerHalWrapperTest::verifyAndClearExpectations() { @@ -76,6 +81,7 @@ void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); } } + WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) { WorkDuration duration; duration.durationNanos = durationNanos.count(); @@ -83,6 +89,10 @@ WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t time return duration; } +WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) { + return toWorkDuration(timePair.first, timePair.second); +} + std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) { std::ostringstream os; for (auto duration : durations) { @@ -112,7 +122,7 @@ TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) { EXPECT_FALSE(mWrapper->startPowerHintSession()); } -TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) { +TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector<int32_t> threadIds = {1, 2}; @@ -149,12 +159,8 @@ TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { std::chrono::nanoseconds base = 100ms; // test cases with target work duration and whether it should update hint against baseline 100ms - const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false}, - {-1ms, false}, - {200ms, true}, - {2ms, true}, - {91ms, false}, - {109ms, false}}; + const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = + {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}}; for (const auto& test : testCases) { // reset to 100ms baseline @@ -200,21 +206,21 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { // 100ms const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> testCases = {{{{-1ms, 100}}, false}, - {{{91ms, 100}}, false}, - {{{109ms, 100}}, false}, + {{{100ms - (mAllowedDeviation / 2), 100}}, false}, + {{{100ms + (mAllowedDeviation / 2), 100}}, false}, + {{{100ms + (mAllowedDeviation + 1ms), 100}}, true}, + {{{100ms - (mAllowedDeviation + 1ms), 100}}, true}, {{{100ms, 100}, {200ms, 200}}, true}, {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, 80ms); + sendActualWorkDurationGroup({base}, mStaleTimeout); auto raw = test.first; std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), - [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { - return toWorkDuration(d.first, d.second); - }); + [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) .Times(test.second ? 1 : 0); sendActualWorkDurationGroup(durations, 0ms); @@ -222,40 +228,6 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { } } -TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) { - ASSERT_TRUE(mWrapper->supportsPowerHintSession()); - - std::vector<int32_t> threadIds = {1, 2}; - mWrapper->setPowerHintSessionThreadIds(threadIds); - EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); - ASSERT_TRUE(mWrapper->startPowerHintSession()); - verifyAndClearExpectations(); - - std::chrono::nanoseconds lastTarget = 100ms; - EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1); - mWrapper->setTargetWorkDuration(lastTarget.count()); - std::chrono::nanoseconds newTarget = 105ms; - mWrapper->setTargetWorkDuration(newTarget.count()); - EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0); - std::chrono::nanoseconds actual = 21ms; - // 100 / 105 * 21ms = 20ms - std::chrono::nanoseconds expectedActualSent = 20ms; - std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)}; - - EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) - .WillOnce(DoAll( - [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) { - EXPECT_EQ(expectedDurations, durationsSent) - << base::StringPrintf("actual sent: %s vs expected: %s", - printWorkDurations(durationsSent).c_str(), - printWorkDurations(expectedDurations) - .c_str()); - }, - Return(Status::ok()))); - mWrapper->sendActualWorkDuration(actual.count(), 1); -} - TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); @@ -269,22 +241,23 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { auto base = toWorkDuration(100ms, 0); // test cases with actual work durations and whether it should update hint against baseline // 100ms - const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> - testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}}; + const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, + std::chrono::nanoseconds, bool>> + testCases = {{{{100ms, 100}}, mStaleTimeout, true}, + {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true}, + {{{100ms, 100}}, mStaleTimeout / 2, false}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, 80ms); + sendActualWorkDurationGroup({base}, mStaleTimeout); - auto raw = test.first; + auto raw = std::get<0>(test); std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), - [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { - return toWorkDuration(d.first, d.second); - }); + [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) - .Times(test.second ? 1 : 0); - sendActualWorkDurationGroup(durations, 80ms); + .Times(std::get<2>(test) ? 1 : 0); + sendActualWorkDurationGroup(durations, std::get<1>(test)); verifyAndClearExpectations(); } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index 8de9e4be29..c2d87f2484 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -74,6 +74,7 @@ void SurfaceFlingerPowerHintTest::SetUp() { mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + mFlinger.setPowerHintSessionMode(true, true); mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); static constexpr bool kIsPrimary = true; FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) @@ -142,10 +143,7 @@ TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { std::this_thread::sleep_for(mockHwcRunTime); return hardware::graphics::composer::V2_1::Error::NONE; }); - EXPECT_CALL(*mPowerAdvisor, - sendActualWorkDuration(Gt(mockHwcRunTime.count()), - Gt(now + mockHwcRunTime.count()))) - .Times(1); + EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1); static constexpr bool kVsyncId = 123; // arbitrary mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count()); } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index f1a69fb46e..b70fdcd93e 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -330,6 +330,10 @@ public: layer->mDrawingParent = drawingParent; } + void setPowerHintSessionMode(bool early, bool late) { + mFlinger->mPowerHintSessionMode = {.late = late, .early = early}; + } + /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index c598cbc28e..05cc544b90 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -36,11 +36,31 @@ public: MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp), - (override)); + MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (), (override)); + MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override)); + MOCK_METHOD(void, setGpuFenceTime, + (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); + MOCK_METHOD(void, setValidateTiming, + (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (override)); + MOCK_METHOD(void, setPresentTiming, + (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); + MOCK_METHOD(void, setRequiresClientComposition, + (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); + MOCK_METHOD(void, setPresentDelayedTime, + (DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); }; } // namespace android::Hwc2::mock |