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