/* * 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include "../Scheduler/OneShotTimer.h" using namespace std::chrono_literals; namespace android { class SurfaceFlinger; namespace Hwc2 { class PowerAdvisor { public: virtual ~PowerAdvisor(); // Initializes resources that cannot be initialized on construction virtual void init() = 0; virtual void onBootFinished() = 0; 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; // 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& 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) = 0; // Reports the start and end times of a hwc validate call this frame for a given display virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, nsecs_t validateEndTime) = 0; // Reports the start and end times of a hwc present call this frame for a given display virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime) = 0; // Reports the expected time that the current frame will present to the display virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0; // Reports the most recent present fence time and end time once known virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 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 when a hwc present is delayed, and the time that it will resume virtual void setHwcPresentDelayedTime( 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& displayIds) = 0; // Sets the target duration for the entire pipeline including the gpu virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0; }; namespace impl { // PowerAdvisor is a wrapper around IPower HAL which takes into account the // full state of the system when sending out power hints to things like the GPU. class PowerAdvisor final : public Hwc2::PowerAdvisor { public: class HalWrapper { public: virtual ~HalWrapper() = default; virtual bool setExpensiveRendering(bool enabled) = 0; virtual bool notifyDisplayUpdateImminent() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; virtual void restartPowerHintSession() = 0; virtual void setPowerHintSessionThreadIds(const std::vector& threadIds) = 0; virtual bool startPowerHintSession() = 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 getPowerHintSessionThreadIds() = 0; virtual std::optional getTargetWorkDuration() = 0; }; PowerAdvisor(SurfaceFlinger& flinger); ~PowerAdvisor() override; void init() override; void onBootFinished() override; void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override; bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }; void notifyDisplayUpdateImminent() override; bool usePowerHintSession() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; void setTargetWorkDuration(nsecs_t targetDuration) override; void sendActualWorkDuration() override; void sendPredictedWorkDuration() override; void enablePowerHint(bool enabled) override; bool startPowerHintSession(const std::vector& threadIds) override; void setGpuFenceTime(DisplayId displayId, std::unique_ptr&& fenceTime); void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime) override; void setHwcPresentTiming(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 setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override; void setHwcPresentDelayedTime( 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& displayIds) override; void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override; private: friend class PowerAdvisorTest; // Tracks if powerhal exists bool mHasHal = true; // Holds the hal wrapper for getPowerHal std::unique_ptr mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr; HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex); bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false; std::mutex mPowerHalMutex; std::atomic_bool mBootFinished = false; std::unordered_set mExpensiveDisplays; bool mNotifiedExpensiveRendering = false; SurfaceFlinger& mFlinger; std::atomic_bool mSendUpdateImminent = true; std::atomic mLastScreenUpdatedTime = 0; std::optional mScreenUpdateTimer; // Higher-level timing data used for estimation struct DisplayTimeline { // The start of hwc present, or the start of validate if it happened there instead nsecs_t hwcPresentStartTime = -1; // The end of hwc present or validate, whichever one actually presented nsecs_t hwcPresentEndTime = -1; // How long the actual hwc present was delayed after hwcPresentStartTime nsecs_t hwcPresentDelayDuration = 0; // When we think we started waiting for the present fence after calling into hwc present and // after potentially waiting for the earliest present time nsecs_t presentFenceWaitStartTime = -1; // How long we ran after we finished waiting for the fence but before hwc present finished nsecs_t postPresentFenceHwcPresentDuration = 0; // Are we likely to have waited for the present fence during composition bool probablyWaitsForPresentFence = 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 gpuEndFenceTime; std::optional gpuStartTime; std::optional lastValidGpuEndTime; std::optional lastValidGpuStartTime; std::optional hwcPresentStartTime; std::optional hwcPresentEndTime; std::optional hwcValidateStartTime; std::optional hwcValidateEndTime; std::optional hwcPresentDelayedTime; 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 estimateGpuTiming(std::optional previousEnd); }; template class RingBuffer { std::array 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((offset % static_cast(N)) + static_cast(N)); return elements[(mIndex + positiveOffset) % N]; } }; // Filter and sort the display ids by a given property std::vector getOrderedDisplayIds(std::optional 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 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 mDisplayTimingData; // Current frame's delay nsecs_t mFrameDelayDuration = 0; // Last frame's post-composition duration nsecs_t mLastPostcompDuration = 0; // Buffer of recent commit start times RingBuffer mCommitStartTimes; // Buffer of recent expected present times RingBuffer mExpectedPresentTimes; // Most recent present fence time, set at the end of the frame once known nsecs_t mLastPresentFenceTime = -1; // Most recent present fence time, set at the end of the frame once known nsecs_t mLastSfPresentEndTime = -1; // Target for the entire pipeline including gpu std::optional mTotalFrameTargetDuration; // Updated list of display IDs std::vector mDisplayIds; std::optional mPowerHintEnabled; std::optional mSupportsPowerHint; bool mPowerHintSessionRunning = false; // An adjustable safety margin which pads the "actual" value sent to PowerHAL, // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms; // How long we expect hwc to run after the present call until it waits for the fence static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us; static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us; }; class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { public: explicit AidlPowerHalWrapper(sp powerHal); ~AidlPowerHalWrapper() override; static std::unique_ptr connect(); bool setExpensiveRendering(bool enabled) override; bool notifyDisplayUpdateImminent() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; void restartPowerHintSession() override; void setPowerHintSessionThreadIds(const std::vector& threadIds) override; bool startPowerHintSession() override; void setTargetWorkDuration(nsecs_t targetDuration) override; void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override; bool shouldReconnectHAL() override; std::vector getPowerHintSessionThreadIds() override; std::optional getTargetWorkDuration() override; private: friend class AidlPowerHalWrapperTest; bool checkPowerHintSessionSupported(); void closePowerHintSession(); bool shouldReportActualDurations(); // Used for testing void setAllowedActualDeviation(nsecs_t); const sp mPowerHal = nullptr; bool mHasExpensiveRendering = false; bool mHasDisplayUpdateImminent = false; // Used to indicate an error state and need for reconstruction bool mShouldReconnectHal = false; // Power hint session data // Concurrent access for this is protected by mPowerHalMutex sp mPowerHintSession = nullptr; // Queue of actual durations saved to report std::vector mPowerHintQueue; // The latest values we have received for target and actual nsecs_t mTargetDuration = kDefaultTarget.count(); std::optional mActualDuration; // The list of thread ids, stored so we can restart the session from this class if needed std::vector mPowerHintThreadIds; bool mSupportsPowerHint = false; // Keep track of the last messages sent for rate limiter change detection std::optional 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; 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; }; } // namespace impl } // namespace Hwc2 } // namespace android