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 constexpr int64_t INSPECTION_PERIOD = 5000000000; //5s
41
SystemTime()42 static int64_t SystemTime()
43 {
44 timespec t = {};
45 clock_gettime(CLOCK_MONOTONIC, &t);
46 return int64_t(t.tv_sec) * 1000000000LL + t.tv_nsec; // 1000000000ns == 1s
47 }
48 }
GetInstance()49 sptr<OHOS::Rosen::VSyncSampler> VSyncSampler::GetInstance() noexcept
50 {
51 std::call_once(createFlag_, []() {
52 auto vsyncSampler = new VSyncSampler();
53 instance_ = vsyncSampler;
54 });
55
56 return instance_;
57 }
58
VSyncSampler()59 VSyncSampler::VSyncSampler()
60 : period_(0), phase_(0), referenceTime_(0),
61 error_(0), firstSampleIndex_(0), numSamples_(0),
62 modeUpdated_(false)
63 {
64 }
65
ResetErrorLocked()66 void VSyncSampler::ResetErrorLocked()
67 {
68 presentFenceTimeOffset_ = 0;
69 error_ = 0;
70 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
71 presentFenceTime_[i] = INVALID_TIMESTAMP;
72 }
73 }
74
SetAdaptive(bool isAdaptive)75 void VSyncSampler::SetAdaptive(bool isAdaptive)
76 {
77 std::lock_guard<std::mutex> lock(mutex_);
78 if (isAdaptive_ == isAdaptive) {
79 return;
80 }
81 lastAdaptiveTime_ = 0;
82 isAdaptive_ = isAdaptive;
83 }
84
SetVsyncEnabledScreenId(uint64_t vsyncEnabledScreenId)85 void VSyncSampler::SetVsyncEnabledScreenId(uint64_t vsyncEnabledScreenId)
86 {
87 RS_TRACE_NAME_FMT("SetVsyncEnabledScreenId:%lu", vsyncEnabledScreenId);
88 VLOGI("SetVsyncEnabledScreenId:" VPUBU64, vsyncEnabledScreenId);
89 std::lock_guard<std::mutex> lock(mutex_);
90 vsyncEnabledScreenId_ = vsyncEnabledScreenId;
91 }
92
GetVsyncEnabledScreenId()93 uint64_t VSyncSampler::GetVsyncEnabledScreenId()
94 {
95 std::lock_guard<std::mutex> lock(mutex_);
96 return vsyncEnabledScreenId_;
97 }
98
SetVsyncSamplerEnabled(bool enabled)99 void VSyncSampler::SetVsyncSamplerEnabled(bool enabled)
100 {
101 RS_TRACE_NAME_FMT("HdiOutput::SetVsyncSamplerEnabled, enableVsyncSample_:%d", enabled);
102 VLOGI("Change enableVsyncSample_, value is %{public}d", enabled);
103 enableVsyncSample_.store(enabled);
104 }
105
GetVsyncSamplerEnabled()106 bool VSyncSampler::GetVsyncSamplerEnabled()
107 {
108 return enableVsyncSample_.load();
109 }
110
StartSample(bool forceReSample)111 int32_t VSyncSampler::StartSample(bool forceReSample)
112 {
113 RS_TRACE_NAME_FMT("HdiOutput::StartVSyncSampler, forceReSample:%d", forceReSample);
114 if (!enableVsyncSample_.load()) {
115 RS_TRACE_NAME_FMT("disabled vsyncSample");
116 return VSYNC_ERROR_API_FAILED;
117 }
118 std::lock_guard<std::mutex> lock(mutex_);
119 if (!forceReSample && hardwareVSyncStatus_) {
120 RS_TRACE_NAME("Already Start Sample.");
121 VLOGD("Already Start Sample.");
122 return VSYNC_ERROR_OK;
123 }
124 VLOGD("Enable Screen Vsync");
125 numSamples_ = 0;
126 modeUpdated_ = false;
127 SetScreenVsyncEnabledInRSMainThreadLocked(vsyncEnabledScreenId_, true);
128 return VSYNC_ERROR_OK;
129 }
130
ClearAllSamples()131 void VSyncSampler::ClearAllSamples()
132 {
133 ScopedBytrace func("ClearAllSamples");
134 std::lock_guard<std::mutex> lock(mutex_);
135 numSamples_ = 0;
136 }
137
SetHardwareVSyncStatus(bool enabled)138 void VSyncSampler::SetHardwareVSyncStatus(bool enabled)
139 {
140 std::lock_guard<std::mutex> lock(mutex_);
141 hardwareVSyncStatus_ = enabled;
142 }
143
RecordDisplayVSyncStatus(bool enabled)144 void VSyncSampler::RecordDisplayVSyncStatus(bool enabled)
145 {
146 std::lock_guard<std::mutex> lock(mutex_);
147 displayVSyncStatus_ = enabled;
148 }
149
RollbackHardwareVSyncStatus()150 void VSyncSampler::RollbackHardwareVSyncStatus()
151 {
152 std::lock_guard<std::mutex> lock(mutex_);
153 hardwareVSyncStatus_ = displayVSyncStatus_;
154 }
155
RegSetScreenVsyncEnabledCallback(VSyncSampler::SetScreenVsyncEnabledCallback cb)156 void VSyncSampler::RegSetScreenVsyncEnabledCallback(VSyncSampler::SetScreenVsyncEnabledCallback cb)
157 {
158 std::lock_guard<std::mutex> lock(mutex_);
159 setScreenVsyncEnabledCallback_ = cb;
160 }
161
SetScreenVsyncEnabledInRSMainThread(uint64_t screenId,bool enabled)162 void VSyncSampler::SetScreenVsyncEnabledInRSMainThread(uint64_t screenId, bool enabled)
163 {
164 std::lock_guard<std::mutex> lock(mutex_);
165 SetScreenVsyncEnabledInRSMainThreadLocked(screenId, enabled);
166 }
167
SetScreenVsyncEnabledInRSMainThreadLocked(uint64_t screenId,bool enabled)168 void VSyncSampler::SetScreenVsyncEnabledInRSMainThreadLocked(uint64_t screenId, bool enabled)
169 {
170 if (setScreenVsyncEnabledCallback_ == nullptr) {
171 VLOGE("SetScreenVsyncEnabled:%{public}d failed, cb is null", enabled);
172 return;
173 }
174 hardwareVSyncStatus_ = enabled;
175 setScreenVsyncEnabledCallback_(screenId, enabled);
176 }
177
AddSample(int64_t timeStamp)178 bool VSyncSampler::AddSample(int64_t timeStamp)
179 {
180 if (timeStamp < 0) {
181 return true;
182 }
183 std::lock_guard<std::mutex> lock(mutex_);
184 if (!hardwareVSyncStatus_) {
185 return true;
186 }
187 if (numSamples_ > 0) {
188 auto preSample = samples_[(firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES];
189
190 if (auto intervalStamp = timeStamp - preSample; intervalStamp <= 0) {
191 RS_TRACE_NAME_FMT("VSyncSampler::AddSample, invalid sample, preSample is larger");
192 numSamples_ = 0;
193 return true;
194 }
195 }
196
197 if (isAdaptive_ && SystemTime() - lastAdaptiveTime_ < INSPECTION_PERIOD) {
198 return true;
199 }
200
201 if (numSamples_ < MAX_SAMPLES - 1) {
202 numSamples_++;
203 } else {
204 firstSampleIndex_ = (firstSampleIndex_ + 1) % MAX_SAMPLES;
205 }
206
207 if (firstSampleIndex_ + numSamples_ >= 1) {
208 uint32_t index = (firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES;
209 samples_[index] = timeStamp;
210 }
211
212 UpdateReferenceTimeLocked();
213 UpdateModeLocked();
214
215 if (numResyncSamplesSincePresent_++ > MAX_SAMPLES_WITHOUT_PRESENT) {
216 ResetErrorLocked();
217 }
218
219 // 1/2 just a empirical value
220 bool shouldDisableScreenVsync = modeUpdated_ && (error_ < ERROR_THRESHOLD / 2);
221 if (shouldDisableScreenVsync) {
222 // disabled screen vsync in rsMainThread
223 VLOGD("Disable Screen Vsync");
224 SetScreenVsyncEnabledInRSMainThreadLocked(vsyncEnabledScreenId_, false);
225 }
226
227 return !shouldDisableScreenVsync;
228 }
229
UpdateReferenceTimeLocked()230 void VSyncSampler::UpdateReferenceTimeLocked()
231 {
232 bool isFrameRateChanging = CreateVSyncGenerator()->GetFrameRateChaingStatus();
233 // update referenceTime at the first sample, unless in adaptive sync mode
234 if (!isFrameRateChanging && (numSamples_ == 1) && !isAdaptive_) {
235 phase_ = 0;
236 referenceTime_ = samples_[firstSampleIndex_];
237 CheckIfFirstRefreshAfterIdleLocked();
238 CreateVSyncGenerator()->UpdateMode(0, phase_, referenceTime_);
239 } else if (isFrameRateChanging && (numSamples_ >= 2)) { // at least 2 samples
240 int64_t prevSample = samples_[(firstSampleIndex_ + numSamples_ - 2) % MAX_SAMPLES]; // at least 2 samples
241 int64_t latestSample = samples_[(firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES];
242 CheckIfFirstRefreshAfterIdleLocked();
243 CreateVSyncGenerator()->CheckAndUpdateReferenceTime(latestSample - prevSample, prevSample);
244 }
245 }
246
UpdateModeLocked()247 void VSyncSampler::UpdateModeLocked()
248 {
249 if (!CreateVSyncGenerator()->GetFrameRateChaingStatus() && (numSamples_ >= MIN_SAMPLES_FOR_UPDATE)) {
250 int64_t sum = 0;
251 int64_t min = INT64_MAX;
252 int64_t max = 0;
253 int64_t diffPrev = 0;
254 int64_t diff = 0;
255 double variance = 0;
256 for (uint32_t i = 1; i < numSamples_; i++) {
257 int64_t prevSample = samples_[(firstSampleIndex_ + i - 1 + MAX_SAMPLES) % MAX_SAMPLES];
258 int64_t currentSample = samples_[(firstSampleIndex_ + i) % MAX_SAMPLES];
259 diffPrev = diff;
260 diff = currentSample - prevSample;
261 if (diffPrev != 0) {
262 int64_t delta = diff - diffPrev;
263 variance += pow(static_cast<double>(delta), 2); // the 2nd power of delta
264 }
265 min = min < diff ? min : diff;
266 max = max > diff ? max : diff;
267 sum += diff;
268 }
269 variance /= (numSamples_ - SAMPLES_INTERVAL_DIFF_NUMS);
270 if (variance > SAMPLE_VARIANCE_THRESHOLD) {
271 // keep only the latest 5 samples, and sample the next timestamp.
272 firstSampleIndex_ = (firstSampleIndex_ + numSamples_ - MIN_SAMPLES_FOR_UPDATE + 1) % MAX_SAMPLES;
273 numSamples_ = MIN_SAMPLES_FOR_UPDATE - 1;
274 referenceTime_ = samples_[firstSampleIndex_];
275 return;
276 }
277
278 sum -= min;
279 sum -= max;
280
281 period_ = sum / (int64_t)(numSamples_ - MINES_SAMPLE_NUMS);
282 if (period_ <= 0) {
283 return;
284 }
285
286 referenceTime_ = samples_[firstSampleIndex_];
287
288 ComputePhaseLocked();
289
290 modeUpdated_ = true;
291 CheckIfFirstRefreshAfterIdleLocked();
292 CreateVSyncGenerator()->UpdateMode(period_, phase_, referenceTime_);
293 pendingPeriod_ = period_;
294 }
295 }
296
UpdateErrorLocked()297 void VSyncSampler::UpdateErrorLocked()
298 {
299 if (!modeUpdated_ || (period_ <= 0)) {
300 return;
301 }
302
303 int numErrSamples = 0;
304 double sqErrSum = 0;
305
306 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
307 int64_t t = presentFenceTime_[i];
308 if (t <= 0) {
309 continue;
310 }
311
312 int64_t sample = t - referenceTime_;
313 if (sample <= phase_) {
314 continue;
315 }
316
317 int64_t sampleErr = (sample - phase_) % period_;
318 // 1/2 just a empirical value
319 if (sampleErr > period_ / 2) {
320 sampleErr -= period_;
321 }
322 sqErrSum += pow(static_cast<double>(sampleErr), 2); // the 2nd power of sampleErr
323 numErrSamples++;
324 }
325
326 if (numErrSamples > 0) {
327 error_ = sqErrSum / numErrSamples;
328 } else {
329 error_ = 0;
330 }
331 }
332
AddPresentFenceTime(uint32_t screenId,int64_t timestamp)333 bool VSyncSampler::AddPresentFenceTime(uint32_t screenId, int64_t timestamp)
334 {
335 if (timestamp < 0) {
336 return false;
337 }
338 std::lock_guard<std::mutex> lock(mutex_);
339 if (isAdaptive_) {
340 auto prePresentFenceTime = presentFenceTime_[(presentFenceTimeOffset_ + NUM_PRESENT - 1) % NUM_PRESENT];
341 auto interval = timestamp - prePresentFenceTime;
342 if (CreateVSyncGenerator()->CheckSampleIsAdaptive(interval)) {
343 RS_TRACE_NAME_FMT("VSyncSampler::AddPresentFenceTime, adaptive sample");
344 lastAdaptiveTime_ = SystemTime();
345 }
346 }
347 if (screenId != vsyncEnabledScreenId_) {
348 return false;
349 }
350 presentFenceTime_[presentFenceTimeOffset_] = timestamp;
351
352 presentFenceTimeOffset_ = (presentFenceTimeOffset_ + 1) % NUM_PRESENT;
353 numResyncSamplesSincePresent_ = 0;
354
355 UpdateErrorLocked();
356 if (error_ > ERROR_THRESHOLD) {
357 RS_TRACE_NAME_FMT("PresentFenceTime error_:%lf", error_);
358 }
359
360 return !modeUpdated_ || error_ > ERROR_THRESHOLD;
361 }
362
CheckIfFirstRefreshAfterIdleLocked()363 void VSyncSampler::CheckIfFirstRefreshAfterIdleLocked()
364 {
365 if (presentFenceTimeOffset_ + NUM_PRESENT < 1) {
366 return;
367 }
368 int64_t curFenceTimeStamp = presentFenceTime_[presentFenceTimeOffset_];
369 int64_t prevFenceTimeStamp = presentFenceTime_[(presentFenceTimeOffset_ + NUM_PRESENT - 1) % NUM_PRESENT];
370 if ((curFenceTimeStamp != INVALID_TIMESTAMP) && (prevFenceTimeStamp != INVALID_TIMESTAMP) &&
371 (curFenceTimeStamp - prevFenceTimeStamp > MAX_IDLE_TIME_THRESHOLD)) {
372 CreateVSyncGenerator()->StartRefresh();
373 }
374 }
375
ComputePhaseLocked()376 void VSyncSampler::ComputePhaseLocked()
377 {
378 double scale = 2.0 * PI / period_;
379 double deltaAvgX = 0;
380 double deltaAvgY = 0;
381 for (uint32_t i = 1; i < numSamples_; i++) {
382 double delta = (samples_[(firstSampleIndex_ + i) % MAX_SAMPLES] - referenceTime_) % period_ * scale;
383 deltaAvgX += cos(delta);
384 deltaAvgY += sin(delta);
385 }
386
387 deltaAvgX /= double(numSamples_ - 1);
388 deltaAvgY /= double(numSamples_ - 1);
389
390 phase_ = int64_t(::atan2(deltaAvgY, deltaAvgX) / scale);
391 }
392
GetPeriod() const393 int64_t VSyncSampler::GetPeriod() const
394 {
395 std::lock_guard<std::mutex> lock(mutex_);
396 return period_;
397 }
398
GetPhase() const399 int64_t VSyncSampler::GetPhase() const
400 {
401 std::lock_guard<std::mutex> lock(mutex_);
402 return phase_;
403 }
404
GetRefrenceTime() const405 int64_t VSyncSampler::GetRefrenceTime() const
406 {
407 std::lock_guard<std::mutex> lock(mutex_);
408 return referenceTime_;
409 }
410
GetHardwarePeriod() const411 int64_t VSyncSampler::GetHardwarePeriod() const
412 {
413 std::lock_guard<std::mutex> lock(mutex_);
414 int64_t period = period_;
415 if (!modeUpdated_ && pendingPeriod_ != 0) {
416 period = pendingPeriod_;
417 }
418 return period;
419 }
420
SetPendingPeriod(int64_t period)421 void VSyncSampler::SetPendingPeriod(int64_t period)
422 {
423 if (period <= 0) {
424 return;
425 }
426 std::lock_guard<std::mutex> lock(mutex_);
427 pendingPeriod_ = period;
428 CreateVSyncGenerator()->SetFrameRateChangingStatus(true);
429 }
430
Dump(std::string & result)431 void VSyncSampler::Dump(std::string &result)
432 {
433 std::lock_guard<std::mutex> lock(mutex_);
434 result.append("\n-- VSyncSampler --");
435 result += "\nperiod:" + std::to_string(period_);
436 result += "\nphase:" + std::to_string(phase_);
437 result += "\nreferenceTime:" + std::to_string(referenceTime_);
438 result += "\nmodeUpdated:" + std::to_string(modeUpdated_);
439 result += "\nhardwareVSyncStatus:" + std::to_string(hardwareVSyncStatus_);
440 result += "\nnumSamples:" + std::to_string(numSamples_);
441 result += "\nsamples:[";
442 for (uint32_t i = 0; i < numSamples_; i++) {
443 result += std::to_string(samples_[(firstSampleIndex_ + i) % MAX_SAMPLES]) + ",";
444 }
445 result += "]";
446 result += "\npresentFenceTime:[";
447 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
448 result += std::to_string(presentFenceTime_[i]) + ",";
449 }
450 result += "]";
451 result += "\npresentFenceTimeOffset:" + std::to_string(presentFenceTimeOffset_);
452 result += "\nvsyncEnabledScreenId:" + std::to_string(vsyncEnabledScreenId_);
453 }
454
~VSyncSampler()455 VSyncSampler::~VSyncSampler()
456 {
457 }
458 } // namespace impl
459
CreateVSyncSampler()460 sptr<VSyncSampler> CreateVSyncSampler()
461 {
462 return impl::VSyncSampler::GetInstance();
463 }
464 }
465 }