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 #include "PhaseOffsets.h"
18 
19 #include <cutils/properties.h>
20 
21 #include <optional>
22 
23 #include "SurfaceFlingerProperties.h"
24 
25 namespace {
26 
getProperty(const char * name)27 std::optional<nsecs_t> getProperty(const char* name) {
28     char value[PROPERTY_VALUE_MAX];
29     property_get(name, value, "-1");
30     if (const int i = atoi(value); i != -1) return i;
31     return std::nullopt;
32 }
33 
fpsEqualsWithMargin(float fpsA,float fpsB)34 bool fpsEqualsWithMargin(float fpsA, float fpsB) {
35     static constexpr float MARGIN = 0.01f;
36     return std::abs(fpsA - fpsB) <= MARGIN;
37 }
38 
getRefreshRatesFromConfigs(const android::scheduler::RefreshRateConfigs & refreshRateConfigs)39 std::vector<float> getRefreshRatesFromConfigs(
40         const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
41     const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
42     std::vector<float> refreshRates;
43     refreshRates.reserve(allRefreshRates.size());
44 
45     for (const auto& [ignored, refreshRate] : allRefreshRates) {
46         refreshRates.emplace_back(refreshRate->getFps());
47     }
48 
49     return refreshRates;
50 }
51 
52 } // namespace
53 
54 namespace android::scheduler {
55 
56 PhaseConfiguration::~PhaseConfiguration() = default;
57 
58 namespace impl {
59 
PhaseOffsets(const scheduler::RefreshRateConfigs & refreshRateConfigs)60 PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
61       : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
62                      refreshRateConfigs.getCurrentRefreshRate().getFps(),
63                      sysprop::vsync_event_phase_offset_ns(1000000),
64                      sysprop::vsync_sf_event_phase_offset_ns(1000000),
65                      getProperty("debug.sf.early_phase_offset_ns"),
66                      getProperty("debug.sf.early_gl_phase_offset_ns"),
67                      getProperty("debug.sf.early_app_phase_offset_ns"),
68                      getProperty("debug.sf.early_gl_app_phase_offset_ns"),
69                      // Below defines the threshold when an offset is considered to be negative,
70                      // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
71                      // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
72                      // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
73                      // vsync.
74                      getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
75                              .value_or(std::numeric_limits<nsecs_t>::max())) {}
76 
PhaseOffsets(const std::vector<float> & refreshRates,float currentFps,nsecs_t vsyncPhaseOffsetNs,nsecs_t sfVSyncPhaseOffsetNs,std::optional<nsecs_t> earlySfOffsetNs,std::optional<nsecs_t> earlyGlSfOffsetNs,std::optional<nsecs_t> earlyAppOffsetNs,std::optional<nsecs_t> earlyGlAppOffsetNs,nsecs_t thresholdForNextVsync)77 PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
78                            nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
79                            std::optional<nsecs_t> earlySfOffsetNs,
80                            std::optional<nsecs_t> earlyGlSfOffsetNs,
81                            std::optional<nsecs_t> earlyAppOffsetNs,
82                            std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
83       : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
84         mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
85         mEarlySfOffsetNs(earlySfOffsetNs),
86         mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
87         mEarlyAppOffsetNs(earlyAppOffsetNs),
88         mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
89         mThresholdForNextVsync(thresholdForNextVsync),
90         mOffsets(initializeOffsets(refreshRates)),
91         mRefreshRateFps(currentFps) {}
92 
dump(std::string & result) const93 void PhaseOffsets::dump(std::string& result) const {
94     const auto [early, earlyGl, late] = getCurrentOffsets();
95     using base::StringAppendF;
96     StringAppendF(&result,
97                   "           app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
98                   "     early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
99                   "  GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
100                   "next VSYNC threshold: %9" PRId64 " ns\n",
101                   late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
102                   mThresholdForNextVsync);
103 }
104 
initializeOffsets(const std::vector<float> & refreshRates) const105 std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
106         const std::vector<float>& refreshRates) const {
107     std::unordered_map<float, Offsets> offsets;
108 
109     for (const auto& refreshRate : refreshRates) {
110         offsets.emplace(refreshRate,
111                         getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
112     }
113     return offsets;
114 }
115 
getPhaseOffsets(float fps,nsecs_t vsyncPeriod) const116 PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
117     if (fps > 65.0f) {
118         return getHighFpsOffsets(vsyncPeriod);
119     } else {
120         return getDefaultOffsets(vsyncPeriod);
121     }
122 }
123 
getDefaultOffsets(nsecs_t vsyncDuration) const124 PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
125     return {
126             {
127                     mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
128                             ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
129                             : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
130 
131                     mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
132             },
133             {
134                     mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
135                             ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
136                             : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
137 
138                     mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
139             },
140             {
141                     mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
142                             ? mSfVSyncPhaseOffsetNs
143                             : mSfVSyncPhaseOffsetNs - vsyncDuration,
144 
145                     mVSyncPhaseOffsetNs,
146             },
147     };
148 }
149 
getHighFpsOffsets(nsecs_t vsyncDuration) const150 PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
151     const auto highFpsLateAppOffsetNs =
152             getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
153     const auto highFpsLateSfOffsetNs =
154             getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
155 
156     const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
157     const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
158     const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
159     const auto highFpsEarlyGlAppOffsetNs =
160             getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
161 
162     return {
163             {
164                     highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
165                             ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
166                             : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
167                                     vsyncDuration,
168 
169                     highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
170             },
171             {
172                     highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
173                                     mThresholdForNextVsync
174                             ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
175                             : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
176                                     vsyncDuration,
177 
178                     highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
179             },
180             {
181                     highFpsLateSfOffsetNs < mThresholdForNextVsync
182                             ? highFpsLateSfOffsetNs
183                             : highFpsLateSfOffsetNs - vsyncDuration,
184 
185                     highFpsLateAppOffsetNs,
186             },
187     };
188 }
189 
getOffsetsForRefreshRate(float fps) const190 PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
191     const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
192                                    [&fps](const std::pair<float, Offsets>& candidateFps) {
193                                        return fpsEqualsWithMargin(fps, candidateFps.first);
194                                    });
195 
196     if (iter != mOffsets.end()) {
197         return iter->second;
198     }
199 
200     // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
201     // In this case just construct the offset.
202     ALOGW("Can't find offset for %.2f fps", fps);
203     return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
204 }
205 
validateSysprops()206 static void validateSysprops() {
207     const auto validatePropertyBool = [](const char* prop) {
208         LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
209     };
210 
211     validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
212 
213     LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
214                         "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
215                         "duration");
216 
217     LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
218                         "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
219                         "duration");
220 
221     const auto validateProperty = [](const char* prop) {
222         LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
223                             "%s is set to %" PRId64 " but expecting duration", prop,
224                             getProperty(prop).value_or(-1));
225     };
226 
227     validateProperty("debug.sf.early_phase_offset_ns");
228     validateProperty("debug.sf.early_gl_phase_offset_ns");
229     validateProperty("debug.sf.early_app_phase_offset_ns");
230     validateProperty("debug.sf.early_gl_app_phase_offset_ns");
231     validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
232     validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
233     validateProperty("debug.sf.high_fps_early_phase_offset_ns");
234     validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
235     validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
236     validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
237 }
238 
sfDurationToOffset(nsecs_t sfDuration,nsecs_t vsyncDuration)239 static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
240     return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
241 }
242 
appDurationToOffset(nsecs_t appDuration,nsecs_t sfDuration,nsecs_t vsyncDuration)243 static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
244     return sfDuration == -1 ? 1'000'000
245                             : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
246 }
247 
constructOffsets(nsecs_t vsyncDuration) const248 PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
249     return Offsets{
250             {
251                     mSfEarlyDuration < vsyncDuration
252                             ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
253                             : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
254 
255                     appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
256             },
257             {
258                     mSfEarlyGlDuration < vsyncDuration
259                             ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
260                             : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
261 
262                     appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
263             },
264             {
265                     mSfDuration < vsyncDuration
266                             ? sfDurationToOffset(mSfDuration, vsyncDuration)
267                             : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
268 
269                     appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
270             },
271     };
272 }
273 
initializeOffsets(const std::vector<float> & refreshRates) const274 std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
275         const std::vector<float>& refreshRates) const {
276     std::unordered_map<float, Offsets> offsets;
277 
278     for (const auto fps : refreshRates) {
279         offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
280     }
281     return offsets;
282 }
283 
PhaseDurations(const scheduler::RefreshRateConfigs & refreshRateConfigs)284 PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
285       : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
286                        refreshRateConfigs.getCurrentRefreshRate().getFps(),
287                        getProperty("debug.sf.late.sf.duration").value_or(-1),
288                        getProperty("debug.sf.late.app.duration").value_or(-1),
289                        getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
290                        getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
291                        getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
292                        getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
293     validateSysprops();
294 }
295 
PhaseDurations(const std::vector<float> & refreshRates,float currentFps,nsecs_t sfDuration,nsecs_t appDuration,nsecs_t sfEarlyDuration,nsecs_t appEarlyDuration,nsecs_t sfEarlyGlDuration,nsecs_t appEarlyGlDuration)296 PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
297                                nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
298                                nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
299                                nsecs_t appEarlyGlDuration)
300       : mSfDuration(sfDuration),
301         mAppDuration(appDuration),
302         mSfEarlyDuration(sfEarlyDuration),
303         mAppEarlyDuration(appEarlyDuration),
304         mSfEarlyGlDuration(sfEarlyGlDuration),
305         mAppEarlyGlDuration(appEarlyGlDuration),
306         mOffsets(initializeOffsets(refreshRates)),
307         mRefreshRateFps(currentFps) {}
308 
getOffsetsForRefreshRate(float fps) const309 PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
310     const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
311         return fpsEqualsWithMargin(fps, candidateFps.first);
312     });
313 
314     if (iter != mOffsets.end()) {
315         return iter->second;
316     }
317 
318     // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
319     // In this case just construct the offset.
320     ALOGW("Can't find offset for %.2f fps", fps);
321     return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
322 }
323 
dump(std::string & result) const324 void PhaseDurations::dump(std::string& result) const {
325     const auto [early, earlyGl, late] = getCurrentOffsets();
326     using base::StringAppendF;
327     StringAppendF(&result,
328                   "           app phase:    %9" PRId64 " ns\t         SF phase:    %9" PRId64
329                   " ns\n"
330                   "           app duration: %9" PRId64 " ns\t         SF duration: %9" PRId64
331                   " ns\n"
332                   "     early app phase:    %9" PRId64 " ns\t   early SF phase:    %9" PRId64
333                   " ns\n"
334                   "     early app duration: %9" PRId64 " ns\t   early SF duration: %9" PRId64
335                   " ns\n"
336                   "  GL early app phase:    %9" PRId64 " ns\tGL early SF phase:    %9" PRId64
337                   " ns\n"
338                   "  GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
339                   " ns\n",
340                   late.app,
341 
342                   late.sf,
343 
344                   mAppDuration, mSfDuration,
345 
346                   early.app, early.sf,
347 
348                   mAppEarlyDuration, mSfEarlyDuration,
349 
350                   earlyGl.app,
351 
352                   earlyGl.sf,
353 
354                   mAppEarlyGlDuration, mSfEarlyGlDuration);
355 }
356 
357 } // namespace impl
358 } // namespace android::scheduler
359