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 // TODO(b/129481165): remove the #pragma below and fix conversion issues
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wconversion"
20 
21 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
22 
23 #include "VSyncModulator.h"
24 
25 #include <cutils/properties.h>
26 #include <utils/Trace.h>
27 
28 #include <chrono>
29 #include <cinttypes>
30 #include <mutex>
31 
32 namespace android::scheduler {
33 
VSyncModulator(IPhaseOffsetControl & phaseOffsetControl,Scheduler::ConnectionHandle appConnectionHandle,Scheduler::ConnectionHandle sfConnectionHandle,const OffsetsConfig & config)34 VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
35                                Scheduler::ConnectionHandle appConnectionHandle,
36                                Scheduler::ConnectionHandle sfConnectionHandle,
37                                const OffsetsConfig& config)
38       : mPhaseOffsetControl(phaseOffsetControl),
39         mAppConnectionHandle(appConnectionHandle),
40         mSfConnectionHandle(sfConnectionHandle),
41         mOffsetsConfig(config) {
42     char value[PROPERTY_VALUE_MAX];
43     property_get("debug.sf.vsync_trace_detailed_info", value, "0");
44     mTraceDetailedInfo = atoi(value);
45 }
46 
setPhaseOffsets(const OffsetsConfig & config)47 void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
48     std::lock_guard<std::mutex> lock(mMutex);
49     mOffsetsConfig = config;
50     updateOffsetsLocked();
51 }
52 
setTransactionStart(Scheduler::TransactionStart transactionStart)53 void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
54     switch (transactionStart) {
55         case Scheduler::TransactionStart::EarlyStart:
56             ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
57             mExplicitEarlyWakeup = true;
58             break;
59         case Scheduler::TransactionStart::EarlyEnd:
60             ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
61             mExplicitEarlyWakeup = false;
62             break;
63         case Scheduler::TransactionStart::Normal:
64         case Scheduler::TransactionStart::Early:
65             // Non explicit don't change the explicit early wakeup state
66             break;
67     }
68 
69     if (mTraceDetailedInfo) {
70         ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
71     }
72 
73     if (!mExplicitEarlyWakeup &&
74         (transactionStart == Scheduler::TransactionStart::Early ||
75          transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
76         mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
77         mEarlyTxnStartTime = std::chrono::steady_clock::now();
78     }
79 
80     // An early transaction stays an early transaction.
81     if (transactionStart == mTransactionStart ||
82         mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
83         return;
84     }
85     mTransactionStart = transactionStart;
86     updateOffsets();
87 }
88 
onTransactionHandled()89 void VSyncModulator::onTransactionHandled() {
90     mTxnAppliedTime = std::chrono::steady_clock::now();
91     if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
92     mTransactionStart = Scheduler::TransactionStart::Normal;
93     updateOffsets();
94 }
95 
onRefreshRateChangeInitiated()96 void VSyncModulator::onRefreshRateChangeInitiated() {
97     if (mRefreshRateChangePending) {
98         return;
99     }
100     mRefreshRateChangePending = true;
101     updateOffsets();
102 }
103 
onRefreshRateChangeCompleted()104 void VSyncModulator::onRefreshRateChangeCompleted() {
105     if (!mRefreshRateChangePending) {
106         return;
107     }
108     mRefreshRateChangePending = false;
109     updateOffsets();
110 }
111 
onRefreshed(bool usedRenderEngine)112 void VSyncModulator::onRefreshed(bool usedRenderEngine) {
113     bool updateOffsetsNeeded = false;
114 
115     // Apply a margin to account for potential data races
116     // This might make us stay in early offsets for one
117     // additional frame but it's better to be conservative here.
118     if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
119         if (mRemainingEarlyFrameCount > 0) {
120             mRemainingEarlyFrameCount--;
121             updateOffsetsNeeded = true;
122         }
123     }
124     if (usedRenderEngine) {
125         mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
126         updateOffsetsNeeded = true;
127     } else if (mRemainingRenderEngineUsageCount > 0) {
128         mRemainingRenderEngineUsageCount--;
129         updateOffsetsNeeded = true;
130     }
131     if (updateOffsetsNeeded) {
132         updateOffsets();
133     }
134 }
135 
getOffsets() const136 VSyncModulator::Offsets VSyncModulator::getOffsets() const {
137     std::lock_guard<std::mutex> lock(mMutex);
138     return mOffsets;
139 }
140 
getNextOffsets() const141 const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
142     // Early offsets are used if we're in the middle of a refresh rate
143     // change, or if we recently begin a transaction.
144     if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
145         mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
146         return mOffsetsConfig.early;
147     } else if (mRemainingRenderEngineUsageCount > 0) {
148         return mOffsetsConfig.earlyGl;
149     } else {
150         return mOffsetsConfig.late;
151     }
152 }
153 
updateOffsets()154 void VSyncModulator::updateOffsets() {
155     std::lock_guard<std::mutex> lock(mMutex);
156     updateOffsetsLocked();
157 }
158 
updateOffsetsLocked()159 void VSyncModulator::updateOffsetsLocked() {
160     const Offsets& offsets = getNextOffsets();
161 
162     mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
163     mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
164 
165     mOffsets = offsets;
166 
167     if (!mTraceDetailedInfo) {
168         return;
169     }
170 
171     const bool isEarly = &offsets == &mOffsetsConfig.early;
172     const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
173     const bool isLate = &offsets == &mOffsetsConfig.late;
174 
175     ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
176     ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
177     ATRACE_INT("Vsync-LateOffsetsOn", isLate);
178 }
179 
180 } // namespace android::scheduler
181 
182 // TODO(b/129481165): remove the #pragma below and fix conversion issues
183 #pragma clang diagnostic pop // ignored "-Wconversion"
184