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