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