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
25 namespace OHOS {
26 namespace Rosen {
27 namespace impl {
28 std::once_flag VSyncSampler::createFlag_;
29 sptr<OHOS::Rosen::VSyncSampler> VSyncSampler::instance_ = nullptr;
30
31 namespace {
32 constexpr double PI = 3.1415926;
33 constexpr int64_t g_errorThreshold = 40000000000; // 200 usec squared
34 constexpr int32_t INVAILD_TIMESTAMP = -1;
35 constexpr uint32_t MINES_SAMPLE_NUMS = 3;
36 constexpr uint32_t SAMPLES_INTERVAL_DIFF_NUMS = 2;
37 constexpr int64_t MAX_IDLE_TIME_THRESHOLD = 900000000; // 900000000ns == 900ms
38 constexpr int64_t SAMPLE_VARIANCE_THRESHOLD = 250000000000; // 500 usec squared
39 }
GetInstance()40 sptr<OHOS::Rosen::VSyncSampler> VSyncSampler::GetInstance() noexcept
41 {
42 std::call_once(createFlag_, []() {
43 auto vsyncSampler = new VSyncSampler();
44 instance_ = vsyncSampler;
45 });
46
47 return instance_;
48 }
49
VSyncSampler()50 VSyncSampler::VSyncSampler()
51 : period_(0), phase_(0), referenceTime_(0),
52 error_(0), firstSampleIndex_(0), numSamples_(0),
53 modeUpdated_(false)
54 {
55 }
56
Reset()57 void VSyncSampler::Reset()
58 {
59 std::lock_guard<std::mutex> lock(mutex_);
60 period_ = 0;
61 phase_ = 0;
62 referenceTime_ = 0;
63 error_ = 0;
64 firstSampleIndex_ = 0;
65 numSamples_ = 0;
66 modeUpdated_ = false;
67 hardwareVSyncStatus_ = true;
68 }
69
ResetErrorLocked()70 void VSyncSampler::ResetErrorLocked()
71 {
72 presentFenceTimeOffset_ = 0;
73 error_ = 0;
74 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
75 presentFenceTime_[i] = INVAILD_TIMESTAMP;
76 }
77 }
78
BeginSample()79 void VSyncSampler::BeginSample()
80 {
81 ScopedBytrace func("BeginSample");
82 std::lock_guard<std::mutex> lock(mutex_);
83 numSamples_ = 0;
84 modeUpdated_ = false;
85 hardwareVSyncStatus_ = true;
86 }
87
SetHardwareVSyncStatus(bool enabled)88 void VSyncSampler::SetHardwareVSyncStatus(bool enabled)
89 {
90 std::lock_guard<std::mutex> lock(mutex_);
91 hardwareVSyncStatus_ = enabled;
92 }
93
GetHardwareVSyncStatus() const94 bool VSyncSampler::GetHardwareVSyncStatus() const
95 {
96 std::lock_guard<std::mutex> lock(mutex_);
97 return hardwareVSyncStatus_;
98 }
99
RegSetScreenVsyncEnabledCallback(VSyncSampler::SetScreenVsyncEnabledCallback cb)100 void VSyncSampler::RegSetScreenVsyncEnabledCallback(VSyncSampler::SetScreenVsyncEnabledCallback cb)
101 {
102 setScreenVsyncEnabledCallback_ = cb;
103 }
104
SetScreenVsyncEnabledInRSMainThread(bool enabled)105 void VSyncSampler::SetScreenVsyncEnabledInRSMainThread(bool enabled)
106 {
107 if (setScreenVsyncEnabledCallback_ == nullptr) {
108 VLOGE("SetScreenVsyncEnabled:%{public}d failed, setScreenVsyncEnabledCallback_ is null", enabled);
109 return;
110 }
111 setScreenVsyncEnabledCallback_(enabled);
112 }
113
AddSample(int64_t timeStamp)114 bool VSyncSampler::AddSample(int64_t timeStamp)
115 {
116 std::lock_guard<std::mutex> lock(mutex_);
117 if (numSamples_ < MAX_SAMPLES - 1) {
118 numSamples_++;
119 } else {
120 firstSampleIndex_ = (firstSampleIndex_ + 1) % MAX_SAMPLES;
121 }
122
123 uint32_t index = (firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES;
124 samples_[index] = timeStamp;
125
126 UpdateReferenceTimeLocked();
127 UpdateModeLocked();
128
129 if (numResyncSamplesSincePresent_++ > MAX_SAMPLES_WITHOUT_PRESENT) {
130 ResetErrorLocked();
131 }
132
133 // 1/2 just a empirical value
134 bool shouldDisableScreenVsync = modeUpdated_ && (error_ < g_errorThreshold / 2);
135
136 if (shouldDisableScreenVsync) {
137 // disabled screen vsync in rsMainThread
138 VLOGD("Disable Screen Vsync");
139 SetScreenVsyncEnabledInRSMainThread(false);
140 }
141
142 return !shouldDisableScreenVsync;
143 }
144
UpdateReferenceTimeLocked()145 void VSyncSampler::UpdateReferenceTimeLocked()
146 {
147 bool isFrameRateChanging = CreateVSyncGenerator()->GetFrameRateChaingStatus();
148 // update referenceTime at the first sample
149 if (!isFrameRateChanging && (numSamples_ == 1)) {
150 phase_ = 0;
151 referenceTime_ = samples_[firstSampleIndex_];
152 CreateVSyncGenerator()->UpdateMode(0, phase_, referenceTime_);
153 }
154 // check if the actual framerate is changed, at least 2 samples
155 if (isFrameRateChanging && (numSamples_ >= 2)) {
156 int64_t prevSample = samples_[(firstSampleIndex_ + numSamples_ - 2) % MAX_SAMPLES]; // at least 2 samples
157 int64_t latestSample = samples_[(firstSampleIndex_ + numSamples_ - 1) % MAX_SAMPLES];
158 CreateVSyncGenerator()->CheckAndUpdateRefereceTime(latestSample - prevSample, prevSample);
159 }
160 }
161
UpdateModeLocked()162 void VSyncSampler::UpdateModeLocked()
163 {
164 if (!CreateVSyncGenerator()->GetFrameRateChaingStatus() && (numSamples_ >= MIN_SAMPLES_FOR_UPDATE)) {
165 int64_t sum = 0;
166 int64_t min = INT64_MAX;
167 int64_t max = 0;
168 int64_t diffPrev = 0;
169 int64_t diff = 0;
170 int64_t variance = 0;
171 for (uint32_t i = 1; i < numSamples_; i++) {
172 int64_t prevSample = samples_[(firstSampleIndex_ + i - 1 + MAX_SAMPLES) % MAX_SAMPLES];
173 int64_t currentSample = samples_[(firstSampleIndex_ + i) % MAX_SAMPLES];
174 diffPrev = diff;
175 diff = currentSample - prevSample;
176 if (diffPrev != 0) {
177 int64_t delta = diff - diffPrev;
178 variance += delta * delta;
179 }
180 min = min < diff ? min : diff;
181 max = max > diff ? max : diff;
182 sum += diff;
183 }
184 variance /= (int64_t)(numSamples_ - SAMPLES_INTERVAL_DIFF_NUMS);
185 if (variance > SAMPLE_VARIANCE_THRESHOLD) {
186 // keep only the latest 5 samples, and sample the next timestamp.
187 firstSampleIndex_ = (firstSampleIndex_ + numSamples_ - MIN_SAMPLES_FOR_UPDATE + 1) % MAX_SAMPLES;
188 numSamples_ = MIN_SAMPLES_FOR_UPDATE - 1;
189 referenceTime_ = samples_[firstSampleIndex_];
190 return;
191 }
192
193 sum -= min;
194 sum -= max;
195
196 period_ = sum / (int64_t)(numSamples_ - MINES_SAMPLE_NUMS);
197 if (period_ <= 0) {
198 return;
199 }
200
201 referenceTime_ = samples_[firstSampleIndex_];
202
203 double scale = 2.0 * PI / period_;
204 double deltaAvgX = 0;
205 double deltaAvgY = 0;
206 for (uint32_t i = 1; i < numSamples_; i++) {
207 double delta = (samples_[(firstSampleIndex_ + i) % MAX_SAMPLES] - referenceTime_) % period_ * scale;
208 deltaAvgX += cos(delta);
209 deltaAvgY += sin(delta);
210 }
211
212 deltaAvgX /= double(numSamples_ - 1);
213 deltaAvgY /= double(numSamples_ - 1);
214
215 phase_ = int64_t(::atan2(deltaAvgY, deltaAvgX) / scale);
216
217 modeUpdated_ = true;
218 CreateVSyncGenerator()->UpdateMode(period_, phase_, referenceTime_);
219 pendingPeriod_ = period_;
220 }
221 }
222
UpdateErrorLocked()223 void VSyncSampler::UpdateErrorLocked()
224 {
225 if (!modeUpdated_ || (period_ <= 0)) {
226 return;
227 }
228
229 int numErrSamples = 0;
230 int64_t sqErrSum = 0;
231
232 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
233 int64_t t = presentFenceTime_[i];
234 if (t <= 0) {
235 continue;
236 }
237
238 int64_t sample = t - referenceTime_;
239 if (sample <= phase_) {
240 continue;
241 }
242
243 int64_t sampleErr = (sample - phase_) % period_;
244 // 1/2 just a empirical value
245 if (sampleErr > period_ / 2) {
246 sampleErr -= period_;
247 }
248 sqErrSum += sampleErr * sampleErr;
249 numErrSamples++;
250 }
251
252 if (numErrSamples > 0) {
253 error_ = sqErrSum / numErrSamples;
254 } else {
255 error_ = 0;
256 }
257 }
258
AddPresentFenceTime(int64_t timestamp)259 bool VSyncSampler::AddPresentFenceTime(int64_t timestamp)
260 {
261 std::lock_guard<std::mutex> lock(mutex_);
262 presentFenceTime_[presentFenceTimeOffset_] = timestamp;
263
264 int64_t prevFenceTimeStamp = presentFenceTime_[(presentFenceTimeOffset_ + NUM_PRESENT - 1) % NUM_PRESENT];
265 if ((prevFenceTimeStamp != INVAILD_TIMESTAMP) && (timestamp - prevFenceTimeStamp > MAX_IDLE_TIME_THRESHOLD)) {
266 ScopedBytrace trace("StartRefreshAfterIdle");
267 CreateVSyncGenerator()->StartRefresh();
268 numSamples_ = 0;
269 }
270
271 presentFenceTimeOffset_ = (presentFenceTimeOffset_ + 1) % NUM_PRESENT;
272 numResyncSamplesSincePresent_ = 0;
273
274 UpdateErrorLocked();
275
276 return !modeUpdated_ || error_ > g_errorThreshold;
277 }
278
GetPeriod() const279 int64_t VSyncSampler::GetPeriod() const
280 {
281 std::lock_guard<std::mutex> lock(mutex_);
282 return period_;
283 }
284
GetPhase() const285 int64_t VSyncSampler::GetPhase() const
286 {
287 std::lock_guard<std::mutex> lock(mutex_);
288 return phase_;
289 }
290
GetRefrenceTime() const291 int64_t VSyncSampler::GetRefrenceTime() const
292 {
293 std::lock_guard<std::mutex> lock(mutex_);
294 return referenceTime_;
295 }
296
GetHardwarePeriod() const297 int64_t VSyncSampler::GetHardwarePeriod() const
298 {
299 std::lock_guard<std::mutex> lock(mutex_);
300 int64_t period = period_;
301 if (!modeUpdated_ && pendingPeriod_ != 0) {
302 period = pendingPeriod_;
303 }
304 return period;
305 }
306
SetPendingPeriod(int64_t period)307 void VSyncSampler::SetPendingPeriod(int64_t period)
308 {
309 if (period <= 0) {
310 return;
311 }
312 std::lock_guard<std::mutex> lock(mutex_);
313 pendingPeriod_ = period;
314 }
315
Dump(std::string & result)316 void VSyncSampler::Dump(std::string &result)
317 {
318 result.append("\n-- VSyncSampler --");
319 result += "\nperiod:" + std::to_string(period_);
320 result += "\nphase:" + std::to_string(phase_);
321 result += "\nreferenceTime:" + std::to_string(referenceTime_);
322 result += "\nmodeUpdated:" + std::to_string(modeUpdated_);
323 result += "\nhardwareVSyncStatus:" + std::to_string(hardwareVSyncStatus_);
324 result += "\nnumSamples:" + std::to_string(numSamples_);
325 result += "\nsamples:[";
326 for (uint32_t i = 0; i < numSamples_; i++) {
327 result += std::to_string(samples_[(firstSampleIndex_ + i) % MAX_SAMPLES]) + ",";
328 }
329 result += "]";
330 result += "\npresentFenceTime:[";
331 for (uint32_t i = 0; i < NUM_PRESENT; i++) {
332 result += std::to_string(presentFenceTime_[i]) + ",";
333 }
334 result += "]";
335 result += "\npresentFenceTimeOffset:" + std::to_string(presentFenceTimeOffset_);
336 }
337
~VSyncSampler()338 VSyncSampler::~VSyncSampler()
339 {
340 }
341 } // namespace impl
342
CreateVSyncSampler()343 sptr<VSyncSampler> CreateVSyncSampler()
344 {
345 return impl::VSyncSampler::GetInstance();
346 }
347 }
348 }