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 }