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 }