1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "vsync_sampler.h"
17 #include <cmath>
18 #include "vsync_generator.h"
19 #include "vsync_log.h"
20 #include <cstdint>
21 #include <mutex>
22 #include <scoped_bytrace.h>
23 #include <string>
24 #include <rs_trace.h>
25
26 namespace OHOS {
27 namespace Rosen {
28 namespace impl {
29 std::once_flag VSyncSampler::createFlag_;
30 sptr<OHOS::Rosen::VSyncSampler> VSyncSampler::instance_ = nullptr;
31
32 namespace {
33 constexpr double PI = 3.1415926;
34 constexpr double ERROR_THRESHOLD = 160000000000.0; // 400 usec squared
35 constexpr int32_t INVALID_TIMESTAMP = -1;
36 constexpr uint32_t MINES_SAMPLE_NUMS = 3;
37 constexpr uint32_t SAMPLES_INTERVAL_DIFF_NUMS = 2;
38 constexpr int64_t MAX_IDLE_TIME_THRESHOLD = 900000000; // 900000000ns == 900ms
39 constexpr double SAMPLE_VARIANCE_THRESHOLD = 250000000000.0; // 500 usec squared
40 }
41 sptr<OHOS::Rosen::VSyncSampler> VSyncSampler::GetInstance() noexcept
42 {
43 std::call_once(createFlag_, []() {
44 auto vsyncSampler = new VSyncSampler();
45 instance_ = vsyncSampler;
46 });
47
48 return instance_;
49 }
50
VSyncSampler()51 VSyncSampler::VSyncSampler()
52 : period_(0), phase_(0), referenceTime_(0),
53 error_(0), firstSampleIndex_(0), numSamples_(0),
54 modeUpdated_(false)
55 {
56 }
57
Reset()58 void VSyncSampler::Reset()
59 {
60 std::lock_guard<std::mutex> lock(mutex_);
61 period_ = 0;
62 phase_ = 0;
63 referenceTime_ = 0;
64 error_ = 0;
65 firstSampleIndex_ = 0;
66 numSamples_ = 0;
67 modeUpdated_ = false;
68 hardwareVSyncStatus_ = true;
69 }
70
ResetErrorLocked()71 void VSyncSampler::ResetErrorLocked()
72 {
73 presentFenceTimeOffset_ = 0;
74 error_ = 0;
75 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
76 presentFenceTime_[i] = INVALID_TIMESTAMP;
77 }
78 }
79
BeginSample()80 void VSyncSampler::BeginSample()
81 {
82 ScopedBytrace func("BeginSample");
83 std::lock_guard<std::mutex> lock(mutex_);
84 numSamples_ = 0;
85 modeUpdated_ = false;
86 hardwareVSyncStatus_ = true;
87 }
88
ClearAllSamples()89 void VSyncSampler::ClearAllSamples()
90 {
91 ScopedBytrace func("ClearAllSamples");
92 std::lock_guard<std::mutex> lock(mutex_);
93 numSamples_ = 0;
94 }
95
SetHardwareVSyncStatus(bool enabled)96 void VSyncSampler::SetHardwareVSyncStatus(bool enabled)
97 {
98 std::lock_guard<std::mutex> lock(mutex_);
99 hardwareVSyncStatus_ = enabled;
100 }
101
GetHardwareVSyncStatus() const102 bool VSyncSampler::GetHardwareVSyncStatus() const
103 {
104 std::lock_guard<std::mutex> lock(mutex_);
105 return hardwareVSyncStatus_;
106 }
107
RegSetScreenVsyncEnabledCallback(VSyncSampler::SetScreenVsyncEnabledCallback cb)108 void VSyncSampler::RegSetScreenVsyncEnabledCallback(VSyncSampler::SetScreenVsyncEnabledCallback cb)
109 {
110 std::lock_guard<std::mutex> lock(mutex_);
111 setScreenVsyncEnabledCallback_ = cb;
112 }
113
SetScreenVsyncEnabledInRSMainThread(bool enabled)114 void VSyncSampler::SetScreenVsyncEnabledInRSMainThread(bool enabled)
115 {
116 SetScreenVsyncEnabledCallback cb;
117 {
118 std::lock_guard<std::mutex> lock(mutex_);
119 cb = setScreenVsyncEnabledCallback_;
120 }
121 SetScreenVsyncEnabledInRSMainThreadInternal(cb, enabled);
122 }
123
SetScreenVsyncEnabledInRSMainThreadInternal(SetScreenVsyncEnabledCallback cb, bool enabled)124 void VSyncSampler::SetScreenVsyncEnabledInRSMainThreadInternal(SetScreenVsyncEnabledCallback cb, bool enabled)
125 {
126 if (cb == nullptr) {
127 VLOGE("SetScreenVsyncEnabled:%{public}d failed, cb is null", enabled);
128 return;
129 }
130 cb(enabled);
131 }
132
AddSample(int64_t timeStamp)133 bool VSyncSampler::AddSample(int64_t timeStamp)
134 {
135 if (timeStamp < 0) {
136 return true;
137 }
138 SetScreenVsyncEnabledCallback cb;
139 bool shouldDisableScreenVsync;
140 {
141 std::lock_guard<std::mutex> lock(mutex_);
142 if (numSamples_ < MAX_SAMPLES - 1) {
143 numSamples_++;
144 } else {
145 firstSampleIndex_ = (firstSampleIndex_ + 1) % MAX_SAMPLES;
146 }
147
148 if (firstSampleIndex_ + numSamples_ >= 1) {
149 uint32_t index = (firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES;
150 samples_[index] = timeStamp;
151 }
152
153 UpdateReferenceTimeLocked();
154 UpdateModeLocked();
155
156 if (numResyncSamplesSincePresent_++ > MAX_SAMPLES_WITHOUT_PRESENT) {
157 ResetErrorLocked();
158 }
159
160 // 1/2 just a empirical value
161 shouldDisableScreenVsync = modeUpdated_ && (error_ < ERROR_THRESHOLD / 2);
162 cb = setScreenVsyncEnabledCallback_;
163 }
164 if (shouldDisableScreenVsync) {
165 // disabled screen vsync in rsMainThread
166 VLOGD("Disable Screen Vsync");
167 SetScreenVsyncEnabledInRSMainThreadInternal(cb, false);
168 }
169
170 return !shouldDisableScreenVsync;
171 }
172
UpdateReferenceTimeLocked()173 void VSyncSampler::UpdateReferenceTimeLocked()
174 {
175 bool isFrameRateChanging = CreateVSyncGenerator()->GetFrameRateChaingStatus();
176 // update referenceTime at the first sample
177 if (!isFrameRateChanging && (numSamples_ == 1)) {
178 phase_ = 0;
179 referenceTime_ = samples_[firstSampleIndex_];
180 CheckIfFirstRefreshAfterIdleLocked();
181 CreateVSyncGenerator()->UpdateMode(0, phase_, referenceTime_);
182 } else if (isFrameRateChanging && (numSamples_ >= 2)) { // at least 2 samples
183 int64_t prevSample = samples_[(firstSampleIndex_ + numSamples_ - 2) % MAX_SAMPLES]; // at least 2 samples
184 int64_t latestSample = samples_[(firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES];
185 CheckIfFirstRefreshAfterIdleLocked();
186 CreateVSyncGenerator()->CheckAndUpdateReferenceTime(latestSample - prevSample, prevSample);
187 }
188 }
189
UpdateModeLocked()190 void VSyncSampler::UpdateModeLocked()
191 {
192 if (!CreateVSyncGenerator()->GetFrameRateChaingStatus() && (numSamples_ >= MIN_SAMPLES_FOR_UPDATE)) {
193 int64_t sum = 0;
194 int64_t min = INT64_MAX;
195 int64_t max = 0;
196 int64_t diffPrev = 0;
197 int64_t diff = 0;
198 double variance = 0;
199 for (uint32_t i = 1; i < numSamples_; i++) {
200 int64_t prevSample = samples_[(firstSampleIndex_ + i - 1 + MAX_SAMPLES) % MAX_SAMPLES];
201 int64_t currentSample = samples_[(firstSampleIndex_ + i) % MAX_SAMPLES];
202 diffPrev = diff;
203 diff = currentSample - prevSample;
204 if (diffPrev != 0) {
205 int64_t delta = diff - diffPrev;
206 variance += pow(static_cast<double>(delta), 2); // the 2nd power of delta
207 }
208 min = min < diff ? min : diff;
209 max = max > diff ? max : diff;
210 sum += diff;
211 }
212 variance /= (numSamples_ - SAMPLES_INTERVAL_DIFF_NUMS);
213 if (variance > SAMPLE_VARIANCE_THRESHOLD) {
214 // keep only the latest 5 samples, and sample the next timestamp.
215 firstSampleIndex_ = (firstSampleIndex_ + numSamples_ - MIN_SAMPLES_FOR_UPDATE + 1) % MAX_SAMPLES;
216 numSamples_ = MIN_SAMPLES_FOR_UPDATE - 1;
217 referenceTime_ = samples_[firstSampleIndex_];
218 return;
219 }
220
221 sum -= min;
222 sum -= max;
223
224 period_ = sum / (int64_t)(numSamples_ - MINES_SAMPLE_NUMS);
225 if (period_ <= 0) {
226 return;
227 }
228
229 referenceTime_ = samples_[firstSampleIndex_];
230
231 ComputePhaseLocked();
232
233 modeUpdated_ = true;
234 CheckIfFirstRefreshAfterIdleLocked();
235 CreateVSyncGenerator()->UpdateMode(period_, phase_, referenceTime_);
236 pendingPeriod_ = period_;
237 }
238 }
239
UpdateErrorLocked()240 void VSyncSampler::UpdateErrorLocked()
241 {
242 if (!modeUpdated_ || (period_ <= 0)) {
243 return;
244 }
245
246 int numErrSamples = 0;
247 double sqErrSum = 0;
248
249 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
250 int64_t t = presentFenceTime_[i];
251 if (t <= 0) {
252 continue;
253 }
254
255 int64_t sample = t - referenceTime_;
256 if (sample <= phase_) {
257 continue;
258 }
259
260 int64_t sampleErr = (sample - phase_) % period_;
261 // 1/2 just a empirical value
262 if (sampleErr > period_ / 2) {
263 sampleErr -= period_;
264 }
265 sqErrSum += pow(static_cast<double>(sampleErr), 2); // the 2nd power of sampleErr
266 numErrSamples++;
267 }
268
269 if (numErrSamples > 0) {
270 error_ = sqErrSum / numErrSamples;
271 } else {
272 error_ = 0;
273 }
274 }
275
AddPresentFenceTime(int64_t timestamp)276 bool VSyncSampler::AddPresentFenceTime(int64_t timestamp)
277 {
278 if (timestamp < 0) {
279 return false;
280 }
281 std::lock_guard<std::mutex> lock(mutex_);
282 presentFenceTime_[presentFenceTimeOffset_] = timestamp;
283
284 presentFenceTimeOffset_ = (presentFenceTimeOffset_ + 1) % NUM_PRESENT;
285 numResyncSamplesSincePresent_ = 0;
286
287 UpdateErrorLocked();
288 if (error_ > ERROR_THRESHOLD) {
289 RS_TRACE_NAME_FMT("PresentFenceTime error_:%lf", error_);
290 }
291
292 return !modeUpdated_ || error_ > ERROR_THRESHOLD;
293 }
294
CheckIfFirstRefreshAfterIdleLocked()295 void VSyncSampler::CheckIfFirstRefreshAfterIdleLocked()
296 {
297 if (presentFenceTimeOffset_ + NUM_PRESENT < 1) {
298 return;
299 }
300 int64_t curFenceTimeStamp = presentFenceTime_[presentFenceTimeOffset_];
301 int64_t prevFenceTimeStamp = presentFenceTime_[(presentFenceTimeOffset_ + NUM_PRESENT - 1) % NUM_PRESENT];
302 if ((curFenceTimeStamp != INVALID_TIMESTAMP) && (prevFenceTimeStamp != INVALID_TIMESTAMP) &&
303 (curFenceTimeStamp - prevFenceTimeStamp > MAX_IDLE_TIME_THRESHOLD)) {
304 CreateVSyncGenerator()->StartRefresh();
305 }
306 }
307
ComputePhaseLocked()308 void VSyncSampler::ComputePhaseLocked()
309 {
310 double scale = 2.0 * PI / period_;
311 double deltaAvgX = 0;
312 double deltaAvgY = 0;
313 for (uint32_t i = 1; i < numSamples_; i++) {
314 double delta = (samples_[(firstSampleIndex_ + i) % MAX_SAMPLES] - referenceTime_) % period_ * scale;
315 deltaAvgX += cos(delta);
316 deltaAvgY += sin(delta);
317 }
318
319 deltaAvgX /= double(numSamples_ - 1);
320 deltaAvgY /= double(numSamples_ - 1);
321
322 phase_ = int64_t(::atan2(deltaAvgY, deltaAvgX) / scale);
323 }
324
GetPeriod() const325 int64_t VSyncSampler::GetPeriod() const
326 {
327 std::lock_guard<std::mutex> lock(mutex_);
328 return period_;
329 }
330
GetPhase() const331 int64_t VSyncSampler::GetPhase() const
332 {
333 std::lock_guard<std::mutex> lock(mutex_);
334 return phase_;
335 }
336
GetRefrenceTime() const337 int64_t VSyncSampler::GetRefrenceTime() const
338 {
339 std::lock_guard<std::mutex> lock(mutex_);
340 return referenceTime_;
341 }
342
GetHardwarePeriod() const343 int64_t VSyncSampler::GetHardwarePeriod() const
344 {
345 std::lock_guard<std::mutex> lock(mutex_);
346 int64_t period = period_;
347 if (!modeUpdated_ && pendingPeriod_ != 0) {
348 period = pendingPeriod_;
349 }
350 return period;
351 }
352
SetPendingPeriod(int64_t period)353 void VSyncSampler::SetPendingPeriod(int64_t period)
354 {
355 if (period <= 0) {
356 return;
357 }
358 std::lock_guard<std::mutex> lock(mutex_);
359 pendingPeriod_ = period;
360 CreateVSyncGenerator()->SetFrameRateChangingStatus(true);
361 }
362
Dump(std::string &result)363 void VSyncSampler::Dump(std::string &result)
364 {
365 std::lock_guard<std::mutex> lock(mutex_);
366 result.append("\n-- VSyncSampler --");
367 result += "\nperiod:" + std::to_string(period_);
368 result += "\nphase:" + std::to_string(phase_);
369 result += "\nreferenceTime:" + std::to_string(referenceTime_);
370 result += "\nmodeUpdated:" + std::to_string(modeUpdated_);
371 result += "\nhardwareVSyncStatus:" + std::to_string(hardwareVSyncStatus_);
372 result += "\nnumSamples:" + std::to_string(numSamples_);
373 result += "\nsamples:[";
374 for (uint32_t i = 0; i < numSamples_; i++) {
375 result += std::to_string(samples_[(firstSampleIndex_ + i) % MAX_SAMPLES]) + ",";
376 }
377 result += "]";
378 result += "\npresentFenceTime:[";
379 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
380 result += std::to_string(presentFenceTime_[i]) + ",";
381 }
382 result += "]";
383 result += "\npresentFenceTimeOffset:" + std::to_string(presentFenceTimeOffset_);
384 }
385
~VSyncSampler()386 VSyncSampler::~VSyncSampler()
387 {
388 }
389 } // namespace impl
390
CreateVSyncSampler()391 sptr<VSyncSampler> CreateVSyncSampler()
392 {
393 return impl::VSyncSampler::GetInstance();
394 }
395 }
396 }