• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }