• 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 
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 }