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