summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/Scheduler/VsyncModulator.h
blob: b2b045166640aaa06abdd72c23dbb5797945693c (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
/*
 * 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 <chrono>
#include <mutex>
#include <optional>
#include <unordered_set>

#include <android-base/thread_annotations.h>
#include <binder/IBinder.h>
#include <utils/Timers.h>

namespace android::scheduler {

// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
// fixed number of frames, respectively.
enum class TransactionSchedule {
    Late,  // Default.
    EarlyStart,
    EarlyEnd
};

// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
class VsyncModulator : public IBinder::DeathRecipient {
public:
    // Number of frames to keep early offsets after an early transaction or GPU composition.
    // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
    // composition strategy alternates on subsequent frames.
    static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
    static constexpr int MIN_EARLY_GPU_FRAMES = 2;

    // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
    // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
    static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;

    // Phase offsets and work durations for SF and app deadlines from VSYNC.
    struct VsyncConfig {
        nsecs_t sfOffset;
        nsecs_t appOffset;
        std::chrono::nanoseconds sfWorkDuration;
        std::chrono::nanoseconds appWorkDuration;

        bool operator==(const VsyncConfig& other) const {
            return sfOffset == other.sfOffset && appOffset == other.appOffset &&
                    sfWorkDuration == other.sfWorkDuration &&
                    appWorkDuration == other.appWorkDuration;
        }

        bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
    };

    using VsyncConfigOpt = std::optional<VsyncConfig>;

    struct VsyncConfigSet {
        VsyncConfig early;    // Used for early transactions, and during refresh rate change.
        VsyncConfig earlyGpu; // Used during GPU composition.
        VsyncConfig late;     // Default.
        std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the
                                                     // earliest present time

        bool operator==(const VsyncConfigSet& other) const {
            return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
                    hwcMinWorkDuration == other.hwcMinWorkDuration;
        }

        bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
    };

    using Clock = std::chrono::steady_clock;
    using TimePoint = Clock::time_point;
    using Now = TimePoint (*)();

    explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);

    VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);

    [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);

    // Changes offsets in response to transaction flags or commit.
    [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule,
                                                        const sp<IBinder>& = {}) EXCLUDES(mMutex);
    [[nodiscard]] VsyncConfigOpt onTransactionCommit();

    // Called when we send a refresh rate change to hardware composer, so that
    // we can move into early offsets.
    [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();

    // Called when we detect from VSYNC signals that the refresh rate changed.
    // This way we can move out of early offsets if no longer necessary.
    [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();

    [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);

protected:
    // Called from unit tests as well
    void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);

private:
    const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
    [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
    [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);

    mutable std::mutex mMutex;
    VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);

    VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};

    using Schedule = TransactionSchedule;
    std::atomic<Schedule> mTransactionSchedule = Schedule::Late;

    struct WpHash {
        size_t operator()(const wp<IBinder>& p) const {
            return std::hash<IBinder*>()(p.unsafe_get());
        }
    };

    std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex);
    std::atomic<bool> mRefreshRateChangePending = false;

    std::atomic<int> mEarlyTransactionFrames = 0;
    std::atomic<int> mEarlyGpuFrames = 0;
    std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
    std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();

    const Now mNow;
    const bool mTraceDetailedInfo;
};

} // namespace android::scheduler