• 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_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, &param);
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