/* * Copyright (C) 2009 The Android Open Source Project * * 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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #include #include "EventThread.h" #include "FrameTimeline.h" #include "MessageQueue.h" namespace android::impl { void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) { if (!mFramePending.exchange(true)) { mVsyncId = vsyncId; mExpectedVsyncTime = expectedVsyncTime; mQueue.mLooper->sendMessage(this, Message()); } } bool MessageQueue::Handler::isFramePending() const { return mFramePending.load(); } void MessageQueue::Handler::handleMessage(const Message&) { mFramePending.store(false); const nsecs_t frameTime = systemTime(); auto& compositor = mQueue.mCompositor; if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) { return; } compositor.composite(frameTime, mVsyncId); compositor.sample(); } MessageQueue::MessageQueue(ICompositor& compositor) : MessageQueue(compositor, sp::make(*this)) {} constexpr bool kAllowNonCallbacks = true; MessageQueue::MessageQueue(ICompositor& compositor, sp handler) : mCompositor(compositor), mLooper(sp::make(kAllowNonCallbacks)), mHandler(std::move(handler)) {} // TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly // and remove the EventThread from MessageQueue void MessageQueue::setInjector(sp connection) { auto& tube = mInjector.tube; if (const int fd = tube.getFd(); fd >= 0) { mLooper->removeFd(fd); } if (connection) { // The EventThreadConnection is retained when disabling injection, so avoid subsequently // stealing invalid FDs. Note that the stolen FDs are kept open. if (tube.getFd() < 0) { connection->stealReceiveChannel(&tube); } else { ALOGW("Recycling channel for VSYNC injection."); } mLooper->addFd( tube.getFd(), 0, Looper::EVENT_INPUT, [](int, int, void* data) { reinterpret_cast(data)->injectorCallback(); return 1; // Keep registration. }, this); } std::lock_guard lock(mInjector.mutex); mInjector.connection = std::move(connection); } void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { ATRACE_CALL(); // Trace VSYNC-sf mVsync.value = (mVsync.value + 1) % 2; { std::lock_guard lock(mVsync.mutex); mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime); mVsync.scheduledFrameTime.reset(); } const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions( {targetWakeupTime, readyTime, vsyncTime}); mHandler->dispatchFrame(vsyncId, vsyncTime); } void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, frametimeline::TokenManager& tokenManager, std::chrono::nanoseconds workDuration) { setDuration(workDuration); mVsync.tokenManager = &tokenManager; mVsync.registration = std::make_unique< scheduler::VSyncCallbackRegistration>(dispatch, std::bind(&MessageQueue::vsyncCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), "sf"); } void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { ATRACE_CALL(); std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; if (mVsync.scheduledFrameTime) { mVsync.scheduledFrameTime = mVsync.registration->schedule( {mVsync.workDuration.get().count(), /*readyDuration=*/0, mVsync.lastCallbackTime.count()}); } } void MessageQueue::waitMessage() { do { IPCThreadState::self()->flushCommands(); int32_t ret = mLooper->pollOnce(-1); switch (ret) { case Looper::POLL_WAKE: case Looper::POLL_CALLBACK: continue; case Looper::POLL_ERROR: ALOGE("Looper::POLL_ERROR"); continue; case Looper::POLL_TIMEOUT: // timeout (should not happen) continue; default: // should not happen ALOGE("Looper::pollOnce() returned unknown status %d", ret); continue; } } while (true); } void MessageQueue::postMessage(sp&& handler) { mLooper->sendMessage(handler, Message()); } void MessageQueue::scheduleFrame() { ATRACE_CALL(); { std::lock_guard lock(mInjector.mutex); if (CC_UNLIKELY(mInjector.connection)) { ALOGD("%s while injecting VSYNC", __FUNCTION__); mInjector.connection->requestNextVsync(); return; } } std::lock_guard lock(mVsync.mutex); mVsync.scheduledFrameTime = mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), .readyDuration = 0, .earliestVsync = mVsync.lastCallbackTime.count()}); } void MessageQueue::injectorCallback() { ssize_t n; DisplayEventReceiver::Event buffer[8]; while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) { for (int i = 0; i < n; i++) { if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { auto& vsync = buffer[i].vsync; mHandler->dispatchFrame(vsync.vsyncData.preferredVsyncId(), vsync.vsyncData.preferredExpectedPresentationTime()); break; } } } } auto MessageQueue::getScheduledFrameTime() const -> std::optional { if (mHandler->isFramePending()) { return Clock::now(); } std::lock_guard lock(mVsync.mutex); if (const auto time = mVsync.scheduledFrameTime) { return Clock::time_point(std::chrono::nanoseconds(*time)); } return std::nullopt; } } // namespace android::impl