/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "vsync_receiver.h" #include <memory> #include <mutex> #include <unistd.h> #include <scoped_bytrace.h> #include <fcntl.h> #include <hitrace_meter.h> #include "event_handler.h" #include "graphic_common.h" #include "rs_frame_report_ext.h" #include "vsync_log.h" #include "sandbox_utils.h" #include <rs_trace.h> #include "qos.h" namespace OHOS { namespace Rosen { namespace { constexpr int32_t INVALID_FD = -1; } void VSyncCallBackListener::OnReadable(int32_t fileDescriptor) { HitracePerfScoped perfTrace(ScopedDebugTrace::isEnabled(), HITRACE_TAG_GRAPHIC_AGP, "OnReadablePerfCount"); if (fileDescriptor < 0) { VLOGE("OnReadable Invalid fileDescriptor:%{public}d", fileDescriptor); return; } // 3 is array size. int64_t data[3]; ssize_t dataCount = 0; if (ReadFdInternal(fileDescriptor, data, dataCount) != VSYNC_ERROR_OK) { return; } HandleVsyncCallbacks(data, dataCount, fileDescriptor); } void VSyncCallBackListener::OnShutdown(int32_t fileDescriptor) { VLOGI("OnShutdown, fileDescriptor:%{public}d", fileDescriptor); std::lock_guard<std::mutex> locker(cbMutex_); if (fdShutDownCallback_ != nullptr) { fdShutDownCallback_(fileDescriptor); } } VsyncError VSyncCallBackListener::ReadFdInternal(int32_t fd, int64_t (&data)[3], ssize_t &dataCount) { std::lock_guard<std::mutex> locker(fdMutex_); if (fdClosed_) { return VSYNC_ERROR_API_FAILED; } ssize_t ret = 0; do { // only take the latest timestamp ret = read(fd, data, sizeof(data)); if (ret == 0) { VLOGE("ReadFdInternal, ret is 0, read fd:%{public}d failed, errno:%{public}d", fd, errno); return VSYNC_ERROR_OK; } if (ret == -1) { if (errno == EINTR) { ret = 0; continue; } else if (errno != EAGAIN) { VLOGE("ReadFdInternal, read fd:%{public}d failed, errno:%{public}d", fd, errno); } } else { dataCount += ret; } } while (ret != -1); return VSYNC_ERROR_OK; } void VSyncCallBackListener::HandleVsyncCallbacks(int64_t data[], ssize_t dataCount, int32_t fileDescriptor) { VSyncCallback cb = nullptr; VSyncCallbackWithId cbWithId = nullptr; void *userData = nullptr; int64_t now = 0; int64_t expectedEnd = 0; std::vector<FrameCallback> callbacks; { std::lock_guard<std::mutex> locker(mtx_); cb = vsyncCallbacks_; cbWithId = vsyncCallbacksWithId_; userData = userData_; RNVFlag_ = false; now = data[0]; period_ = data[1]; periodShared_ = data[1]; timeStamp_ = data[0]; timeStampShared_ = data[0]; expectedEnd = CalculateExpectedEndLocked(now); callbacks = frameCallbacks_; frameCallbacks_.clear(); } VLOGD("dataCount:%{public}d, cb == nullptr:%{public}d", dataCount, (cb == nullptr)); // 1, 2: index of array data. RS_TRACE_NAME_FMT("ReceiveVsync dataCount: %ldbytes now: %ld expectedEnd: %ld vsyncId: %ld, fd:%d", dataCount, now, expectedEnd, data[2], fileDescriptor); // data[2] is vsyncId if (callbacks.empty() && dataCount > 0 && (cbWithId != nullptr || cb != nullptr)) { // data[2] is frameCount cbWithId != nullptr ? cbWithId(now, data[2], userData) : cb(now, userData); } for (const auto& cb : callbacks) { if (cb.callback_ != nullptr) { cb.callback_(now, cb.userData_); } else if (cb.callbackWithId_ != nullptr) { cb.callbackWithId_(now, data[2], cb.userData_); // data[2] is vsyncId } } if (OHOS::Rosen::RsFrameReportExt::GetInstance().GetEnable()) { OHOS::Rosen::RsFrameReportExt::GetInstance().ReceiveVSync(); } } int64_t VSyncCallBackListener::CalculateExpectedEndLocked(int64_t now) { int64_t expectedEnd = 0; if (period_ < 0 || now < period_ || now > INT64_MAX - period_) { RS_TRACE_NAME_FMT("invalid timestamps, now:%ld, period_:%ld", now, period_); VLOGE("invalid timestamps, now:" VPUBI64 ", period_:" VPUBI64, now, period_); return 0; } expectedEnd = now + period_; if (name_ == "rs") { // rs vsync offset is 5000000ns expectedEnd = expectedEnd + period_ - 5000000; } return expectedEnd; } void VSyncCallBackListener::SetFdClosedFlagLocked(bool fdClosed) { fdClosed_ = fdClosed; } void VSyncCallBackListener::RegisterFdShutDownCallback(FdShutDownCallback cb) { std::lock_guard<std::mutex> locker(cbMutex_); fdShutDownCallback_ = cb; } VSyncReceiver::VSyncReceiver(const sptr<IVSyncConnection>& conn, const sptr<IRemoteObject>& token, const std::shared_ptr<OHOS::AppExecFwk::EventHandler>& looper, const std::string& name) : connection_(conn), token_(token), looper_(looper), listener_(std::make_shared<VSyncCallBackListener>()), init_(false), fd_(INVALID_FD), name_(name) { }; VsyncError VSyncReceiver::Init() { std::lock_guard<std::mutex> locker(initMutex_); if (init_) { return VSYNC_ERROR_OK; } if (connection_ == nullptr) { return VSYNC_ERROR_NULLPTR; } VsyncError ret = connection_->GetReceiveFd(fd_); if (ret != VSYNC_ERROR_OK) { return ret; } int32_t retVal = fcntl(fd_, F_SETFL, O_NONBLOCK); // set fd to NonBlock mode if (retVal != 0) { VLOGW("%{public}s fcntl set fd_:%{public}d NonBlock failed, retVal:%{public}d, errno:%{public}d", __func__, fd_, retVal, errno); } listener_->SetName(name_); listener_->RegisterFdShutDownCallback([this](int32_t fileDescriptor) { std::lock_guard<std::mutex> locker(initMutex_); if (fileDescriptor != fd_) { VLOGE("OnShutdown Invalid fileDescriptor:%{public}d, fd_:%{public}d", fileDescriptor, fd_); return; } RemoveAndCloseFdLocked(); }); if (looper_ == nullptr) { std::shared_ptr<AppExecFwk::EventRunner> runner = AppExecFwk::EventRunner::Create("OS_VSyncThread"); looper_ = std::make_shared<AppExecFwk::EventHandler>(runner); runner->Run(); looper_->PostTask([] { SetThreadQos(QOS::QosLevel::QOS_USER_INTERACTIVE); }); } looper_->AddFileDescriptorListener(fd_, AppExecFwk::FILE_DESCRIPTOR_INPUT_EVENT, listener_, "vSyncTask"); init_ = true; return VSYNC_ERROR_OK; } VSyncReceiver::~VSyncReceiver() { listener_->RegisterFdShutDownCallback(nullptr); std::lock_guard<std::mutex> locker(initMutex_); RemoveAndCloseFdLocked(); DestroyLocked(); } void VSyncReceiver::RemoveAndCloseFdLocked() { if (looper_ != nullptr) { looper_->RemoveFileDescriptorListener(fd_); VLOGI("%{public}s looper remove fd listener, fd=%{public}d", __func__, fd_); } std::lock_guard<std::mutex> locker(listener_->fdMutex_); if (fd_ >= 0) { close(fd_); listener_->SetFdClosedFlagLocked(true); fd_ = INVALID_FD; } } VsyncError VSyncReceiver::RequestNextVSync(FrameCallback callback) { return RequestNextVSync(callback, "unknown", 0); } VsyncError VSyncReceiver::RequestNextVSync(FrameCallback callback, const std::string &fromWhom, int64_t lastVSyncTS) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { VLOGE("%{public}s not init", __func__); return VSYNC_ERROR_NOT_INIT; } listener_->SetCallback(callback); listener_->SetRNVFlag(true); ScopedDebugTrace func("VSyncReceiver::RequestNextVSync:" + name_); if (OHOS::Rosen::RsFrameReportExt::GetInstance().GetEnable()) { OHOS::Rosen::RsFrameReportExt::GetInstance().RequestNextVSync(); } return connection_->RequestNextVSync(fromWhom, lastVSyncTS); } VsyncError VSyncReceiver::RequestNextVSyncWithMultiCallback(FrameCallback callback) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { VLOGE("%{public}s not init", __func__); return VSYNC_ERROR_NOT_INIT; } listener_->AddCallback(callback); listener_->SetRNVFlag(true); ScopedDebugTrace func("VSyncReceiver::RequestNextVSync:" + name_); if (OHOS::Rosen::RsFrameReportExt::GetInstance().GetEnable()) { OHOS::Rosen::RsFrameReportExt::GetInstance().RequestNextVSync(); } return connection_->RequestNextVSync(); } VsyncError VSyncReceiver::SetVSyncRate(FrameCallback callback, int32_t rate) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { return VSYNC_ERROR_API_FAILED; } listener_->SetCallback(callback); return connection_->SetVSyncRate(rate); } /* 莅丞舟罸鎵у��莪� */ VsyncError VSyncReceiver::SetVsyncCallBackForEveryFrame(FrameCallback callback, bool isOpen) { if (isOpen) { return SetVSyncRate(callback, 1); } else { return SetVSyncRate(callback, -1); } } VsyncError VSyncReceiver::GetVSyncPeriod(int64_t &period) { int64_t timeStamp; return GetVSyncPeriodAndLastTimeStamp(period, timeStamp); } VsyncError VSyncReceiver::GetVSyncPeriodAndLastTimeStamp(int64_t &period, int64_t &timeStamp, bool isThreadShared) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { VLOGE("%{public}s not init", __func__); return VSYNC_ERROR_NOT_INIT; } if (isThreadShared == false) { int64_t periodNotShared = listener_->GetPeriod(); int64_t timeStampNotShared = listener_->GetTimeStamp(); if (periodNotShared == 0 || timeStampNotShared == 0) { VLOGD("%{public}s Hardware vsync is not available. please try again later!", __func__); return VSYNC_ERROR_UNKOWN; } period = periodNotShared; timeStamp = timeStampNotShared; } else { int64_t periodShared = listener_->GetPeriodShared(); int64_t timeStampShared = listener_->GetTimeStampShared(); if (periodShared == 0 || timeStampShared == 0) { VLOGD("%{public}s Hardware vsync is not available. please try again later!", __func__); return VSYNC_ERROR_UNKOWN; } period = periodShared; timeStamp = timeStampShared; } RS_TRACE_NAME_FMT("VSyncReceiver:period:%ld timeStamp:%ld isThreadShared:%d", period, timeStamp, isThreadShared); return VSYNC_ERROR_OK; } void VSyncReceiver::CloseVsyncReceiverFd() { std::lock_guard<std::mutex> locker(initMutex_); RemoveAndCloseFdLocked(); } VsyncError VSyncReceiver::DestroyLocked() { if (connection_ == nullptr) { return VSYNC_ERROR_API_FAILED; } return connection_->Destroy(); } bool VSyncReceiver::IsRequestedNextVSync() { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { return false; } return listener_->GetRNVFlag(); } VsyncError VSyncReceiver::SetUiDvsyncSwitch(bool dvsyncSwitch) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { return VSYNC_ERROR_API_FAILED; } return connection_->SetUiDvsyncSwitch(dvsyncSwitch); } VsyncError VSyncReceiver::SetUiDvsyncConfig(int32_t bufferCount) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { return VSYNC_ERROR_API_FAILED; } VLOGI("%{public}s bufferCount:%{public}d", __func__, bufferCount); return connection_->SetUiDvsyncConfig(bufferCount); } VsyncError VSyncReceiver::SetNativeDVSyncSwitch(bool dvsyncSwitch) { std::lock_guard<std::mutex> locker(initMutex_); if (!init_) { return VSYNC_ERROR_API_FAILED; } return connection_->SetNativeDVSyncSwitch(dvsyncSwitch); } } // namespace Rosen } // namespace OHOS