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