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_generator.h"
17 #include <scoped_bytrace.h>
18 #include <sched.h>
19 #include <sys/resource.h>
20 #include <string>
21
22 namespace OHOS {
23 namespace Rosen {
24 namespace impl {
25 namespace {
GetSysTimeNs()26 static int64_t GetSysTimeNs()
27 {
28 auto now = std::chrono::steady_clock::now().time_since_epoch();
29 return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
30 }
31
32 // 1.5ms
33 constexpr int64_t maxWaleupDelay = 1500000;
34 constexpr int32_t THREAD_PRIORTY = -6;
35 constexpr int32_t SCHED_PRIORITY = 2;
36 constexpr int64_t errorThreshold = 500000;
37
SetThreadHighPriority()38 static void SetThreadHighPriority()
39 {
40 setpriority(PRIO_PROCESS, 0, THREAD_PRIORTY);
41 struct sched_param param = {0};
42 param.sched_priority = SCHED_PRIORITY;
43 sched_setscheduler(0, SCHED_FIFO, ¶m);
44 }
45 }
46
47 std::once_flag VSyncGenerator::createFlag_;
48 sptr<OHOS::Rosen::VSyncGenerator> VSyncGenerator::instance_ = nullptr;
49
GetInstance()50 sptr<OHOS::Rosen::VSyncGenerator> VSyncGenerator::GetInstance() noexcept
51 {
52 std::call_once(createFlag_, []() {
53 auto vsyncGenerator = new VSyncGenerator();
54 instance_ = vsyncGenerator;
55 });
56
57 return instance_;
58 }
59
DeleteInstance()60 void VSyncGenerator::DeleteInstance() noexcept
61 {
62 instance_ = nullptr;
63 }
64
VSyncGenerator()65 VSyncGenerator::VSyncGenerator()
66 : period_(0), phase_(0), refrenceTime_(0), wakeupDelay_(0)
67 {
68 vsyncThreadRunning_ = true;
69 thread_ = std::thread(std::bind(&VSyncGenerator::ThreadLoop, this));
70 pthread_setname_np(thread_.native_handle(), "VSyncGenerator");
71 }
72
~VSyncGenerator()73 VSyncGenerator::~VSyncGenerator()
74 {
75 {
76 std::unique_lock<std::mutex> locker(mutex_);
77 vsyncThreadRunning_ = false;
78 }
79 if (thread_.joinable()) {
80 con_.notify_all();
81 thread_.join();
82 }
83 }
84
ThreadLoop()85 void VSyncGenerator::ThreadLoop()
86 {
87 // set thread priorty
88 SetThreadHighPriority();
89
90 int64_t occurTimestamp = 0;
91 int64_t nextTimeStamp = 0;
92 int64_t occurRefrenceTime = 0;
93 while (vsyncThreadRunning_ == true) {
94 std::vector<Listener> listeners;
95 {
96 std::unique_lock<std::mutex> locker(mutex_);
97 occurRefrenceTime = refrenceTime_;
98 if (period_ == 0) {
99 if (vsyncThreadRunning_ == true) {
100 con_.wait(locker);
101 }
102 continue;
103 }
104 occurTimestamp = GetSysTimeNs();
105 nextTimeStamp = ComputeNextVSyncTimeStamp(occurTimestamp, occurRefrenceTime);
106 if (nextTimeStamp == INT64_MAX) {
107 if (vsyncThreadRunning_ == true) {
108 con_.wait(locker);
109 }
110 continue;
111 }
112 }
113
114 bool isWakeup = false;
115 if (occurTimestamp < nextTimeStamp) {
116 std::unique_lock<std::mutex> lck(waitForTimeoutMtx_);
117 auto err = waitForTimeoutCon_.wait_for(lck, std::chrono::nanoseconds(nextTimeStamp - occurTimestamp));
118 if (err == std::cv_status::timeout) {
119 isWakeup = true;
120 } else {
121 ScopedBytrace func("VSyncGenerator::ThreadLoop::Continue");
122 continue;
123 }
124 }
125 {
126 std::unique_lock<std::mutex> locker(mutex_);
127 occurTimestamp = GetSysTimeNs();
128 if (isWakeup) {
129 UpdateWakeupDelay(occurTimestamp, nextTimeStamp);
130 }
131 listeners = GetListenerTimeouted(occurTimestamp, occurRefrenceTime);
132 }
133 ScopedBytrace func(
134 "GenerateVsyncCount:" + std::to_string(listeners.size()) + ", period:" + std::to_string(period_));
135 for (uint32_t i = 0; i < listeners.size(); i++) {
136 listeners[i].callback_->OnVSyncEvent(listeners[i].lastTime_, period_);
137 }
138 }
139 }
140
UpdateWakeupDelay(int64_t occurTimestamp,int64_t nextTimeStamp)141 void VSyncGenerator::UpdateWakeupDelay(int64_t occurTimestamp, int64_t nextTimeStamp)
142 {
143 // 63, 1 / 64
144 wakeupDelay_ = ((wakeupDelay_ * 63) + (occurTimestamp - nextTimeStamp)) / 64;
145 wakeupDelay_ = wakeupDelay_ > maxWaleupDelay ? maxWaleupDelay : wakeupDelay_;
146 }
147
ComputeNextVSyncTimeStamp(int64_t now,int64_t refrenceTime)148 int64_t VSyncGenerator::ComputeNextVSyncTimeStamp(int64_t now, int64_t refrenceTime)
149 {
150 int64_t nextVSyncTime = INT64_MAX;
151 for (uint32_t i = 0; i < listeners_.size(); i++) {
152 int64_t t = ComputeListenerNextVSyncTimeStamp(listeners_[i], now, refrenceTime);
153 if (t < nextVSyncTime) {
154 nextVSyncTime = t;
155 }
156 }
157
158 return nextVSyncTime;
159 }
160
ComputeListenerNextVSyncTimeStamp(const Listener & listener,int64_t now,int64_t refrenceTime)161 int64_t VSyncGenerator::ComputeListenerNextVSyncTimeStamp(const Listener& listener, int64_t now, int64_t refrenceTime)
162 {
163 int64_t lastVSyncTime = listener.lastTime_ + wakeupDelay_;
164 if (now < lastVSyncTime) {
165 now = lastVSyncTime;
166 }
167
168 now -= refrenceTime;
169 int64_t phase = phase_ + listener.phase_;
170 now -= phase;
171 if (now < 0) {
172 now = -period_;
173 }
174 int64_t numPeriod = now / period_;
175 int64_t nextTime = (numPeriod + 1) * period_ + phase;
176 nextTime += refrenceTime;
177
178 // 3 / 5 just empirical value
179 if (nextTime - listener.lastTime_ < (3 * period_ / 5)) {
180 nextTime += period_;
181 }
182
183 nextTime -= wakeupDelay_;
184 return nextTime;
185 }
186
GetListenerTimeouted(int64_t now,int64_t refrenceTime)187 std::vector<VSyncGenerator::Listener> VSyncGenerator::GetListenerTimeouted(int64_t now, int64_t refrenceTime)
188 {
189 std::vector<VSyncGenerator::Listener> ret;
190 int64_t onePeriodAgo = now - period_;
191
192 for (uint32_t i = 0; i < listeners_.size(); i++) {
193 int64_t t = ComputeListenerNextVSyncTimeStamp(listeners_[i], onePeriodAgo, refrenceTime);
194 if (t < now || (t - now < errorThreshold)) {
195 listeners_[i].lastTime_ = t;
196 ret.push_back(listeners_[i]);
197 }
198 }
199 return ret;
200 }
201
UpdateMode(int64_t period,int64_t phase,int64_t refrenceTime)202 VsyncError VSyncGenerator::UpdateMode(int64_t period, int64_t phase, int64_t refrenceTime)
203 {
204 std::lock_guard<std::mutex> locker(mutex_);
205 if (period < 0 || refrenceTime < 0) {
206 return VSYNC_ERROR_INVALID_ARGUMENTS;
207 }
208 period_ = period;
209 phase_ = phase;
210 refrenceTime_ = refrenceTime;
211 con_.notify_all();
212 return VSYNC_ERROR_OK;
213 }
214
AddListener(int64_t phase,const sptr<OHOS::Rosen::VSyncGenerator::Callback> & cb)215 VsyncError VSyncGenerator::AddListener(int64_t phase, const sptr<OHOS::Rosen::VSyncGenerator::Callback>& cb)
216 {
217 std::lock_guard<std::mutex> locker(mutex_);
218 if (cb == nullptr) {
219 return VSYNC_ERROR_INVALID_ARGUMENTS;
220 }
221 Listener listener;
222 listener.phase_ = phase;
223 listener.callback_ = cb;
224 // just correct period / 2 time
225 listener.lastTime_ = GetSysTimeNs() - period_ / 2 + phase_;
226
227 listeners_.push_back(listener);
228 con_.notify_all();
229 return VSYNC_ERROR_OK;
230 }
231
RemoveListener(const sptr<OHOS::Rosen::VSyncGenerator::Callback> & cb)232 VsyncError VSyncGenerator::RemoveListener(const sptr<OHOS::Rosen::VSyncGenerator::Callback>& cb)
233 {
234 std::lock_guard<std::mutex> locker(mutex_);
235 if (cb == nullptr) {
236 return VSYNC_ERROR_INVALID_ARGUMENTS;
237 }
238 bool removeFlag = false;
239 auto it = listeners_.begin();
240 for (; it < listeners_.end(); it++) {
241 if (it->callback_ == cb) {
242 listeners_.erase(it);
243 removeFlag = true;
244 break;
245 }
246 }
247 if (!removeFlag) {
248 return VSYNC_ERROR_INVALID_ARGUMENTS;
249 }
250 con_.notify_all();
251 return VSYNC_ERROR_OK;
252 }
253
ChangePhaseOffset(const sptr<OHOS::Rosen::VSyncGenerator::Callback> & cb,int64_t offset)254 VsyncError VSyncGenerator::ChangePhaseOffset(const sptr<OHOS::Rosen::VSyncGenerator::Callback>& cb, int64_t offset)
255 {
256 std::lock_guard<std::mutex> locker(mutex_);
257 if (cb == nullptr) {
258 return VSYNC_ERROR_INVALID_ARGUMENTS;
259 }
260 auto it = listeners_.begin();
261 for (; it < listeners_.end(); it++) {
262 if (it->callback_ == cb) {
263 break;
264 }
265 }
266 if (it != listeners_.end()) {
267 it->phase_ = offset;
268 } else {
269 return VSYNC_ERROR_INVALID_OPERATING;
270 }
271 return VSYNC_ERROR_OK;
272 }
273
IsEnable()274 bool VSyncGenerator::IsEnable()
275 {
276 std::lock_guard<std::mutex> locker(mutex_);
277 return period_ > 0;
278 }
279 } // namespace impl
CreateVSyncGenerator()280 sptr<VSyncGenerator> CreateVSyncGenerator()
281 {
282 return impl::VSyncGenerator::GetInstance();
283 }
284
DestroyVSyncGenerator()285 void DestroyVSyncGenerator()
286 {
287 impl::VSyncGenerator::DeleteInstance();
288 }
289 }
290 }
291