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