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 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18
19 #undef LOG_TAG
20 #define LOG_TAG "VsyncModulator"
21
22 #include "VsyncModulator.h"
23
24 #include <android-base/properties.h>
25 #include <log/log.h>
26 #include <utils/Trace.h>
27
28 #include <chrono>
29 #include <cinttypes>
30 #include <mutex>
31
32 using namespace std::chrono_literals;
33
34 namespace android::scheduler {
35
36 const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
37
VsyncModulator(const VsyncConfigSet & config,Now now)38 VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
39 : mVsyncConfigSet(config),
40 mNow(now),
41 mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
42
setVsyncConfigSet(const VsyncConfigSet & config)43 VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
44 std::lock_guard<std::mutex> lock(mMutex);
45 mVsyncConfigSet = config;
46 return updateVsyncConfigLocked();
47 }
48
setTransactionSchedule(TransactionSchedule schedule,const sp<IBinder> & token)49 VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(TransactionSchedule schedule,
50 const sp<IBinder>& token) {
51 std::lock_guard<std::mutex> lock(mMutex);
52 switch (schedule) {
53 case Schedule::EarlyStart:
54 if (token) {
55 mEarlyWakeupRequests.emplace(token);
56 token->linkToDeath(sp<DeathRecipient>::fromExisting(this));
57 } else {
58 ALOGW("%s: EarlyStart requested without a valid token", __func__);
59 }
60 break;
61 case Schedule::EarlyEnd: {
62 if (token && mEarlyWakeupRequests.erase(token) > 0) {
63 token->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
64 } else {
65 ALOGW("%s: Unexpected EarlyEnd", __func__);
66 }
67 break;
68 }
69 case Schedule::Late:
70 // No change to mEarlyWakeup for non-explicit states.
71 break;
72 }
73
74 if (mTraceDetailedInfo) {
75 ATRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size()));
76 }
77
78 if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) {
79 mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
80 mEarlyTransactionStartTime = mNow();
81 }
82
83 // An early transaction stays an early transaction.
84 if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) {
85 return std::nullopt;
86 }
87 mTransactionSchedule = schedule;
88 return updateVsyncConfigLocked();
89 }
90
onTransactionCommit()91 VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
92 mLastTransactionCommitTime = mNow();
93 if (mTransactionSchedule == Schedule::Late) return std::nullopt;
94 mTransactionSchedule = Schedule::Late;
95 return updateVsyncConfig();
96 }
97
onRefreshRateChangeInitiated()98 VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() {
99 if (mRefreshRateChangePending) return std::nullopt;
100 mRefreshRateChangePending = true;
101 return updateVsyncConfig();
102 }
103
onRefreshRateChangeCompleted()104 VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() {
105 if (!mRefreshRateChangePending) return std::nullopt;
106 mRefreshRateChangePending = false;
107 return updateVsyncConfig();
108 }
109
onDisplayRefresh(bool usedGpuComposition)110 VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
111 bool updateOffsetsNeeded = false;
112
113 if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
114 mLastTransactionCommitTime.load()) {
115 if (mEarlyTransactionFrames > 0) {
116 mEarlyTransactionFrames--;
117 updateOffsetsNeeded = true;
118 }
119 }
120 if (usedGpuComposition) {
121 mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES;
122 updateOffsetsNeeded = true;
123 } else if (mEarlyGpuFrames > 0) {
124 mEarlyGpuFrames--;
125 updateOffsetsNeeded = true;
126 }
127
128 if (!updateOffsetsNeeded) return std::nullopt;
129 return updateVsyncConfig();
130 }
131
getVsyncConfig() const132 VsyncConfig VsyncModulator::getVsyncConfig() const {
133 std::lock_guard<std::mutex> lock(mMutex);
134 return mVsyncConfig;
135 }
136
getNextVsyncConfigType() const137 auto VsyncModulator::getNextVsyncConfigType() const -> VsyncConfigType {
138 // Early offsets are used if we're in the middle of a refresh rate
139 // change, or if we recently begin a transaction.
140 if (!mEarlyWakeupRequests.empty() || mTransactionSchedule == Schedule::EarlyEnd ||
141 mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
142 return VsyncConfigType::Early;
143 } else if (mEarlyGpuFrames > 0) {
144 return VsyncConfigType::EarlyGpu;
145 } else {
146 return VsyncConfigType::Late;
147 }
148 }
149
getNextVsyncConfig() const150 const VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
151 switch (getNextVsyncConfigType()) {
152 case VsyncConfigType::Early:
153 return mVsyncConfigSet.early;
154 case VsyncConfigType::EarlyGpu:
155 return mVsyncConfigSet.earlyGpu;
156 case VsyncConfigType::Late:
157 return mVsyncConfigSet.late;
158 }
159 }
160
updateVsyncConfig()161 VsyncConfig VsyncModulator::updateVsyncConfig() {
162 std::lock_guard<std::mutex> lock(mMutex);
163 return updateVsyncConfigLocked();
164 }
165
updateVsyncConfigLocked()166 VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
167 const VsyncConfig& offsets = getNextVsyncConfig();
168 mVsyncConfig = offsets;
169
170 if (mTraceDetailedInfo) {
171 const bool isEarly = &offsets == &mVsyncConfigSet.early;
172 const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
173 const bool isLate = &offsets == &mVsyncConfigSet.late;
174
175 ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
176 ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
177 ATRACE_INT("Vsync-LateOffsetsOn", isLate);
178 }
179
180 return offsets;
181 }
182
binderDied(const wp<IBinder> & who)183 void VsyncModulator::binderDied(const wp<IBinder>& who) {
184 std::lock_guard<std::mutex> lock(mMutex);
185 mEarlyWakeupRequests.erase(who);
186
187 static_cast<void>(updateVsyncConfigLocked());
188 }
189
isVsyncConfigEarly() const190 bool VsyncModulator::isVsyncConfigEarly() const {
191 std::lock_guard<std::mutex> lock(mMutex);
192 return getNextVsyncConfigType() != VsyncConfigType::Late;
193 }
194
195 } // namespace android::scheduler
196