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 }