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