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