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_distributor.h"
17 #include <chrono>
18 #include <condition_variable>
19 #include <algorithm>
20 #include <sched.h>
21 #include <sys/resource.h>
22 #include <scoped_bytrace.h>
23 #include "vsync_log.h"
24
25 namespace OHOS {
26 namespace Rosen {
27 namespace {
28 constexpr int32_t SOFT_VSYNC_PERIOD = 16;
29 constexpr int32_t ERRNO_EAGAIN = -1;
30 constexpr int32_t ERRNO_OTHER = -2;
31 constexpr int32_t THREAD_PRIORTY = -6;
32 constexpr int32_t SCHED_PRIORITY = 2;
33 constexpr uint32_t SOCKET_CHANNEL_SIZE = 1024;
34 }
VSyncConnection(const sptr<VSyncDistributor> & distributor,std::string name)35 VSyncConnection::VSyncConnection(const sptr<VSyncDistributor>& distributor, std::string name)
36 : rate_(-1), info_(name), distributor_(distributor)
37 {
38 socketPair_ = new LocalSocketPair();
39 int32_t err = socketPair_->CreateChannel(SOCKET_CHANNEL_SIZE, SOCKET_CHANNEL_SIZE);
40 if (err != 0) {
41 ScopedBytrace func("Create socket channel failed, errno = " + std::to_string(errno));
42 }
43 }
44
~VSyncConnection()45 VSyncConnection::~VSyncConnection()
46 {
47 }
48
RequestNextVSync()49 VsyncError VSyncConnection::RequestNextVSync()
50 {
51 if (distributor_ == nullptr) {
52 return VSYNC_ERROR_NULLPTR;
53 }
54 const sptr<VSyncDistributor> distributor = distributor_.promote();
55 if (distributor == nullptr) {
56 return VSYNC_ERROR_NULLPTR;
57 }
58 ScopedBytrace func(info_.name_ + "RequestNextVSync");
59 return distributor->RequestNextVSync(this);
60 }
61
GetReceiveFd(int32_t & fd)62 VsyncError VSyncConnection::GetReceiveFd(int32_t &fd)
63 {
64 fd = socketPair_->GetReceiveDataFd();
65 return VSYNC_ERROR_OK;
66 }
67
PostEvent(int64_t now)68 int32_t VSyncConnection::PostEvent(int64_t now)
69 {
70 ScopedBytrace func("SendVsyncTo conn: " + info_.name_ + ", now:" + std::to_string(now));
71 int32_t ret = socketPair_->SendData(&now, sizeof(int64_t));
72 if (ret > -1) {
73 ScopedBytrace successful("successful");
74 info_.postVSyncCount_++;
75 } else {
76 ScopedBytrace failed("failed");
77 }
78 return ret;
79 }
80
SetVSyncRate(int32_t rate)81 VsyncError VSyncConnection::SetVSyncRate(int32_t rate)
82 {
83 if (distributor_ == nullptr) {
84 return VSYNC_ERROR_NULLPTR;
85 }
86 const sptr<VSyncDistributor> distributor = distributor_.promote();
87 if (distributor == nullptr) {
88 return VSYNC_ERROR_NULLPTR;
89 }
90 return distributor->SetVSyncRate(rate, this);
91 }
92
VSyncDistributor(sptr<VSyncController> controller,std::string name)93 VSyncDistributor::VSyncDistributor(sptr<VSyncController> controller, std::string name)
94 : controller_(controller), mutex_(), con_(), connections_(),
95 vsyncEnabled_(false), name_(name)
96 {
97 event_.timestamp = 0;
98 event_.vsyncCount = 0;
99 vsyncThreadRunning_ = true;
100 threadLoop_ = std::thread(std::bind(&VSyncDistributor::ThreadMain, this));
101 }
102
~VSyncDistributor()103 VSyncDistributor::~VSyncDistributor()
104 {
105 {
106 std::unique_lock<std::mutex> locker(mutex_);
107 vsyncThreadRunning_ = false;
108 }
109 if (threadLoop_.joinable()) {
110 con_.notify_all();
111 threadLoop_.join();
112 }
113 }
114
AddConnection(const sptr<VSyncConnection> & connection)115 VsyncError VSyncDistributor::AddConnection(const sptr<VSyncConnection>& connection)
116 {
117 if (connection == nullptr) {
118 return VSYNC_ERROR_NULLPTR;
119 }
120 std::lock_guard<std::mutex> locker(mutex_);
121 auto it = find(connections_.begin(), connections_.end(), connection);
122 if (it != connections_.end()) {
123 return VSYNC_ERROR_INVALID_ARGUMENTS;
124 }
125 ScopedBytrace func("Add VSyncConnection: " + connection->info_.name_);
126 connections_.push_back(connection);
127 return VSYNC_ERROR_OK;
128 }
129
RemoveConnection(const sptr<VSyncConnection> & connection)130 VsyncError VSyncDistributor::RemoveConnection(const sptr<VSyncConnection>& connection)
131 {
132 if (connection == nullptr) {
133 return VSYNC_ERROR_NULLPTR;
134 }
135 std::lock_guard<std::mutex> locker(mutex_);
136 auto it = find(connections_.begin(), connections_.end(), connection);
137 if (it == connections_.end()) {
138 return VSYNC_ERROR_INVALID_ARGUMENTS;
139 }
140 ScopedBytrace func("Remove VSyncConnection: " + connection->info_.name_);
141 connections_.erase(it);
142 return VSYNC_ERROR_OK;
143 }
144
ThreadMain()145 void VSyncDistributor::ThreadMain()
146 {
147 // set thread priorty
148 setpriority(PRIO_PROCESS, 0, THREAD_PRIORTY);
149 struct sched_param param = {0};
150 param.sched_priority = SCHED_PRIORITY;
151 sched_setscheduler(0, SCHED_FIFO, ¶m);
152
153 int64_t timestamp;
154 int64_t vsyncCount;
155 while (vsyncThreadRunning_ == true) {
156 std::vector<sptr<VSyncConnection>> conns;
157 {
158 bool waitForVSync = false;
159 std::unique_lock<std::mutex> locker(mutex_);
160 timestamp = event_.timestamp;
161 event_.timestamp = 0;
162 vsyncCount = event_.vsyncCount;
163 CollectConnections(waitForVSync, timestamp, conns, vsyncCount);
164 // no vsync signal
165 if (timestamp == 0) {
166 // there is some connections request next vsync, enable vsync if vsync disable and
167 // and start the software vsync with wait_for function
168 if (waitForVSync == true && vsyncEnabled_ == false) {
169 EnableVSync();
170 if (con_.wait_for(locker, std::chrono::milliseconds(SOFT_VSYNC_PERIOD)) ==
171 std::cv_status::timeout) {
172 const auto &now = std::chrono::steady_clock::now().time_since_epoch();
173 timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
174 event_.timestamp = timestamp;
175 event_.vsyncCount++;
176 }
177 } else {
178 // just wait request or vsync signal
179 if (vsyncThreadRunning_ == true) {
180 con_.wait(locker);
181 }
182 }
183 continue;
184 } else if ((timestamp > 0) && (waitForVSync == false)) {
185 // if there is a vsync signal but no vaild connections, we should disable vsync
186 DisableVSync();
187 continue;
188 }
189 }
190 ScopedBytrace func(name_ + "_SendVsync");
191 for (uint32_t i = 0; i < conns.size(); i++) {
192 int32_t ret = conns[i]->PostEvent(timestamp);
193 VLOGD("Distributor name:%{public}s, connection name:%{public}s, ret:%{public}d",
194 name_.c_str(), conns[i]->info_.name_.c_str(), ret);
195 if (ret == 0 || ret == ERRNO_OTHER) {
196 RemoveConnection(conns[i]);
197 } else if (ret == ERRNO_EAGAIN) {
198 std::unique_lock<std::mutex> locker(mutex_);
199 // Exclude SetVSyncRate
200 if (conns[i]->rate_ < 0) {
201 conns[i]->rate_ = 0;
202 }
203 }
204 }
205 }
206 }
207
EnableVSync()208 void VSyncDistributor::EnableVSync()
209 {
210 if (controller_ != nullptr && vsyncEnabled_ == false) {
211 vsyncEnabled_ = true;
212 controller_->SetCallback(this);
213 controller_->SetEnable(true);
214 }
215 }
216
DisableVSync()217 void VSyncDistributor::DisableVSync()
218 {
219 if (controller_ != nullptr && vsyncEnabled_ == true) {
220 vsyncEnabled_ = false;
221 controller_->SetEnable(false);
222 }
223 }
224
OnVSyncEvent(int64_t now)225 void VSyncDistributor::OnVSyncEvent(int64_t now)
226 {
227 std::lock_guard<std::mutex> locker(mutex_);
228 event_.timestamp = now;
229 event_.vsyncCount++;
230 con_.notify_all();
231 }
232
CollectConnections(bool & waitForVSync,int64_t timestamp,std::vector<sptr<VSyncConnection>> & conns,int64_t vsyncCount)233 void VSyncDistributor::CollectConnections(bool &waitForVSync, int64_t timestamp,
234 std::vector<sptr<VSyncConnection>> &conns, int64_t vsyncCount)
235 {
236 for (uint32_t i = 0; i < connections_.size(); i++) {
237 int32_t rate = connections_[i]->highPriorityState_ ? connections_[i]->highPriorityRate_ :
238 connections_[i]->rate_;
239 if (rate == 0) { // for RequestNextVSync
240 waitForVSync = true;
241 if (timestamp > 0) {
242 connections_[i]->rate_ = -1;
243 conns.push_back(connections_[i]);
244 }
245 } else if (rate > 0) {
246 if (connections_[i]->rate_ == 0) { // for SetHighPriorityVSyncRate with RequestNextVSync
247 waitForVSync = true;
248 if (timestamp > 0 && (vsyncCount % rate == 0)) {
249 connections_[i]->rate_ = -1;
250 conns.push_back(connections_[i]);
251 }
252 } else if (connections_[i]->rate_ > 0) { // for SetVSyncRate
253 waitForVSync = true;
254 if (timestamp > 0 && (vsyncCount % rate == 0)) {
255 conns.push_back(connections_[i]);
256 }
257 }
258 }
259 }
260 }
261
RequestNextVSync(const sptr<VSyncConnection> & connection)262 VsyncError VSyncDistributor::RequestNextVSync(const sptr<VSyncConnection>& connection)
263 {
264 if (connection == nullptr) {
265 VLOGE("connection is nullptr");
266 return VSYNC_ERROR_NULLPTR;
267 }
268 ScopedBytrace func(connection->info_.name_ + "_RequestNextVSync");
269 std::lock_guard<std::mutex> locker(mutex_);
270 auto it = find(connections_.begin(), connections_.end(), connection);
271 if (it == connections_.end()) {
272 VLOGE("connection is invalid arguments");
273 return VSYNC_ERROR_INVALID_ARGUMENTS;
274 }
275 if (connection->rate_ < 0) {
276 connection->rate_ = 0;
277 con_.notify_all();
278 }
279 VLOGD("conn name:%{public}s, rate:%{public}d", connection->info_.name_.c_str(), connection->rate_);
280 return VSYNC_ERROR_OK;
281 }
282
SetVSyncRate(int32_t rate,const sptr<VSyncConnection> & connection)283 VsyncError VSyncDistributor::SetVSyncRate(int32_t rate, const sptr<VSyncConnection>& connection)
284 {
285 if (rate <= 0 || connection == nullptr) {
286 return VSYNC_ERROR_INVALID_ARGUMENTS;
287 }
288 std::lock_guard<std::mutex> locker(mutex_);
289 auto it = find(connections_.begin(), connections_.end(), connection);
290 if (it == connections_.end()) {
291 return VSYNC_ERROR_INVALID_ARGUMENTS;
292 }
293 if (connection->rate_ == rate) {
294 return VSYNC_ERROR_INVALID_ARGUMENTS;
295 }
296 connection->rate_ = rate;
297 VLOGD("conn name:%{public}s", connection->info_.name_.c_str());
298 con_.notify_all();
299 return VSYNC_ERROR_OK;
300 }
301
SetHighPriorityVSyncRate(int32_t highPriorityRate,const sptr<VSyncConnection> & connection)302 VsyncError VSyncDistributor::SetHighPriorityVSyncRate(int32_t highPriorityRate, const sptr<VSyncConnection>& connection)
303 {
304 if (highPriorityRate <= 0 || connection == nullptr) {
305 return VSYNC_ERROR_INVALID_ARGUMENTS;
306 }
307
308 std::lock_guard<std::mutex> locker(mutex_);
309 auto it = find(connections_.begin(), connections_.end(), connection);
310 if (it == connections_.end()) {
311 return VSYNC_ERROR_INVALID_ARGUMENTS;
312 }
313 if (connection->highPriorityRate_ == highPriorityRate) {
314 return VSYNC_ERROR_INVALID_ARGUMENTS;
315 }
316 connection->highPriorityRate_ = highPriorityRate;
317 connection->highPriorityState_ = true;
318 VLOGD("in, conn name:%{public}s, highPriorityRate:%{public}d", connection->info_.name_.c_str(),
319 connection->highPriorityRate_);
320 con_.notify_all();
321 return VSYNC_ERROR_OK;
322 }
323
GetVSyncConnectionInfos(std::vector<ConnectionInfo> & infos)324 VsyncError VSyncDistributor::GetVSyncConnectionInfos(std::vector<ConnectionInfo>& infos)
325 {
326 infos.clear();
327 for (auto &connection : connections_) {
328 infos.push_back(connection->info_);
329 }
330 return VSYNC_ERROR_OK;
331 }
332
QosGetPidByName(const std::string & name,uint32_t & pid)333 VsyncError VSyncDistributor::QosGetPidByName(const std::string& name, uint32_t& pid)
334 {
335 if (name.find("WM") == std::string::npos) {
336 return VSYNC_ERROR_INVALID_ARGUMENTS;
337 }
338 std::string::size_type pos = name.find("_");
339 if (pos == std::string::npos) {
340 return VSYNC_ERROR_INVALID_ARGUMENTS;
341 }
342 pid = (uint32_t)stoi(name.substr(pos + 1));
343 return VSYNC_ERROR_OK;
344 }
345
SetQosVSyncRate(uint32_t pid,int32_t rate)346 VsyncError VSyncDistributor::SetQosVSyncRate(uint32_t pid, int32_t rate)
347 {
348 std::lock_guard<std::mutex> locker(mutex_);
349 for (auto connection : connections_) {
350 uint32_t tmpPid;
351 if (QosGetPidByName(connection->info_.name_, tmpPid) != VSYNC_ERROR_OK) {
352 continue;
353 }
354
355 if (tmpPid == pid) {
356 if (connection->highPriorityRate_ != rate) {
357 connection->highPriorityRate_ = rate;
358 connection->highPriorityState_ = true;
359 VLOGD("in, conn name:%{public}s, highPriorityRate:%{public}d", connection->info_.name_.c_str(),
360 connection->highPriorityRate_);
361 con_.notify_all();
362 }
363 break;
364 }
365 }
366 return VSYNC_ERROR_OK;
367 }
368
GetQosVSyncRateInfos(std::vector<std::pair<uint32_t,int32_t>> & vsyncRateInfos)369 VsyncError VSyncDistributor::GetQosVSyncRateInfos(std::vector<std::pair<uint32_t, int32_t>>& vsyncRateInfos)
370 {
371 vsyncRateInfos.clear();
372
373 std::lock_guard<std::mutex> locker(mutex_);
374 for (auto &connection : connections_) {
375 uint32_t tmpPid;
376 if (QosGetPidByName(connection->info_.name_, tmpPid) != VSYNC_ERROR_OK) {
377 continue;
378 }
379 int32_t tmpRate = connection->highPriorityState_ ? connection->highPriorityRate_ : connection->rate_;
380 vsyncRateInfos.push_back(std::make_pair(tmpPid, tmpRate));
381 }
382 return VSYNC_ERROR_OK;
383 }
384 }
385 }
386