summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
blob: 6e25f787d7c119ee6e0360a5ed33248a50fa1fdc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*
 * 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 <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"

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<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 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<DisplayId>& 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<int32_t>& 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<int32_t> getPowerHintSessionThreadIds() = 0;
        virtual std::optional<nsecs_t> 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<int32_t>& threadIds) override;
    void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& 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<DisplayId>& 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<HalWrapper> 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<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 {
        // 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<FenceTime> gpuEndFenceTime;
        std::optional<nsecs_t> gpuStartTime;
        std::optional<nsecs_t> lastValidGpuEndTime;
        std::optional<nsecs_t> lastValidGpuStartTime;
        std::optional<nsecs_t> hwcPresentStartTime;
        std::optional<nsecs_t> hwcPresentEndTime;
        std::optional<nsecs_t> hwcValidateStartTime;
        std::optional<nsecs_t> hwcValidateEndTime;
        std::optional<nsecs_t> 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<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 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, 2> 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<nsecs_t> mTotalFrameTargetDuration;
    // Updated list of display IDs
    std::vector<DisplayId> mDisplayIds;

    std::optional<bool> mPowerHintEnabled;
    std::optional<bool> 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<hardware::power::IPower> powerHal);
    ~AidlPowerHalWrapper() override;

    static std::unique_ptr<HalWrapper> connect();

    bool setExpensiveRendering(bool enabled) override;
    bool notifyDisplayUpdateImminent() override;
    bool supportsPowerHintSession() override;
    bool isPowerHintSessionRunning() override;
    void restartPowerHintSession() override;
    void setPowerHintSessionThreadIds(const std::vector<int32_t>& 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<int32_t> getPowerHintSessionThreadIds() override;
    std::optional<nsecs_t> getTargetWorkDuration() override;

private:
    friend class AidlPowerHalWrapperTest;

    bool checkPowerHintSessionSupported();
    void closePowerHintSession();
    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;

    // 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 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 = false;
    // Keep track of the last messages sent for rate limiter change detection
    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;
    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