1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <deque>
20 #include <mutex>
21 #include <unordered_map>
22 #include <vector>
23 
24 #include <android-base/thread_annotations.h>
25 #include <scheduler/TimeKeeper.h>
26 #include <ui/DisplayId.h>
27 
28 #include "VSyncTracker.h"
29 
30 namespace android::scheduler {
31 
32 class VSyncPredictor : public VSyncTracker {
33 public:
34     /*
35      * \param [in] Clock The clock abstraction. Useful for unit tests.
36      * \param [in] PhysicalDisplayid The display this corresponds to.
37      * \param [in] modePtr  The initial display mode
38      * \param [in] historySize  The internal amount of entries to store in the model.
39      * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
40      * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
41      * samples that fall outlierTolerancePercent from an anticipated vsync event.
42      */
43     VSyncPredictor(std::unique_ptr<Clock>, ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
44                    size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
45     ~VSyncPredictor();
46 
47     bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
48     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
49                                          std::optional<nsecs_t> lastVsyncOpt = {}) final
50             EXCLUDES(mMutex);
51     nsecs_t currentPeriod() const final EXCLUDES(mMutex);
52     Period minFramePeriod() const final EXCLUDES(mMutex);
53     void resetModel() final EXCLUDES(mMutex);
54 
55     /* Query if the model is in need of more samples to make a prediction.
56      * \return  True, if model would benefit from more samples, False if not.
57      */
58     bool needsMoreSamples() const final EXCLUDES(mMutex);
59 
60     struct Model {
61         nsecs_t slope;
62         nsecs_t intercept;
63     };
64 
65     VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
66 
67     bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) final EXCLUDES(mMutex);
68 
69     void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
70 
isCurrentMode(const ftl::NonNull<DisplayModePtr> & modePtr)71     bool isCurrentMode(const ftl::NonNull<DisplayModePtr>& modePtr) const EXCLUDES(mMutex) {
72         std::lock_guard lock(mMutex);
73         return mDisplayModePtr->getId() == modePtr->getId() &&
74                 mDisplayModePtr->getVsyncRate().getPeriodNsecs() ==
75                 mRateMap.find(idealPeriod())->second.slope;
76     }
77 
78     void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex);
79 
80     void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
81             EXCLUDES(mMutex);
82     void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex);
83 
84     void dump(std::string& result) const final EXCLUDES(mMutex);
85 
86 private:
87     struct VsyncSequence {
88         nsecs_t vsyncTime;
89         int64_t seq;
90     };
91 
92     struct MissedVsync {
93         TimePoint vsync = TimePoint::fromNs(0);
94         Duration fixup = Duration::fromNs(0);
95     };
96 
97     class VsyncTimeline {
98     public:
99         VsyncTimeline(TimePoint knownVsync, Period idealPeriod, std::optional<Fps> renderRateOpt);
100         std::optional<TimePoint> nextAnticipatedVSyncTimeFrom(
101                 Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsyncTime,
102                 MissedVsync lastMissedVsync, std::optional<nsecs_t> lastVsyncOpt = {});
103         void freeze(TimePoint lastVsync);
validUntil()104         std::optional<TimePoint> validUntil() const { return mValidUntil; }
105         bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
106         void shiftVsyncSequence(Duration phase);
setRenderRate(std::optional<Fps> renderRateOpt)107         void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
108 
109     private:
110         nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
111         VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
112         std::optional<VsyncSequence> makeVsyncSequence(TimePoint knownVsync);
113 
114         const Period mIdealPeriod = Duration::fromNs(0);
115         std::optional<Fps> mRenderRateOpt;
116         std::optional<TimePoint> mValidUntil;
117         std::optional<VsyncSequence> mLastVsyncSequence;
118     };
119 
120     VSyncPredictor(VSyncPredictor const&) = delete;
121     VSyncPredictor& operator=(VSyncPredictor const&) = delete;
122     void clearTimestamps() REQUIRES(mMutex);
123 
124     const std::unique_ptr<Clock> mClock;
125     const PhysicalDisplayId mId;
126 
127     inline void traceInt64If(const char* name, int64_t value) const;
128     inline void traceInt64(const char* name, int64_t value) const;
129 
130     size_t next(size_t i) const REQUIRES(mMutex);
131     bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
132     Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
133     nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex);
134     Period minFramePeriodLocked() const REQUIRES(mMutex);
135     Duration ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
136     void purgeTimelines(android::TimePoint now) REQUIRES(mMutex);
137 
138     nsecs_t idealPeriod() const REQUIRES(mMutex);
139 
140     bool const mTraceOn;
141     size_t const kHistorySize;
142     size_t const kMinimumSamplesForPrediction;
143     size_t const kOutlierTolerancePercent;
144     std::mutex mutable mMutex;
145 
146     std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
147 
148     // Map between ideal vsync period and the calculated model
149     std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
150 
151     size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
152     std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
153 
154     ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex);
155     int mNumVsyncsForFrame GUARDED_BY(mMutex);
156 
157     std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
158 
159     MissedVsync mMissedVsync GUARDED_BY(mMutex);
160 
161     std::deque<VsyncTimeline> mTimelines GUARDED_BY(mMutex);
162     TimePoint mLastCommittedVsync GUARDED_BY(mMutex) = TimePoint::fromNs(0);
163     Period mIdealPeriod GUARDED_BY(mMutex) = Duration::fromNs(0);
164     std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);
165 };
166 
167 } // namespace android::scheduler
168