/* * Copyright 2018 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 "DispSyncSource.h" #include #include #include #include "EventThread.h" #include "VsyncController.h" namespace android::scheduler { using base::StringAppendF; using namespace std::chrono_literals; class CallbackRepeater { public: CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, std::chrono::nanoseconds notBefore) : mName(name), mCallback(cb), mRegistration(dispatch, std::bind(&CallbackRepeater::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), mName), mStarted(false), mWorkDuration(workDuration), mReadyDuration(readyDuration), mLastCallTime(notBefore) {} ~CallbackRepeater() { std::lock_guard lock(mMutex); mRegistration.cancel(); } void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { std::lock_guard lock(mMutex); mStarted = true; mWorkDuration = workDuration; mReadyDuration = readyDuration; auto const scheduleResult = mRegistration.schedule({.workDuration = mWorkDuration.count(), .readyDuration = mReadyDuration.count(), .earliestVsync = mLastCallTime.count()}); LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback"); } void stop() { std::lock_guard lock(mMutex); LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped"); mStarted = false; mRegistration.cancel(); } void dump(std::string& result) const { std::lock_guard lock(mMutex); const auto relativeLastCallTime = mLastCallTime - std::chrono::steady_clock::now().time_since_epoch(); StringAppendF(&result, "\t%s: ", mName.c_str()); StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ", mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f); StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f, mStarted ? "running" : "stopped"); } private: void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { { std::lock_guard lock(mMutex); mLastCallTime = std::chrono::nanoseconds(vsyncTime); } mCallback(vsyncTime, wakeupTime, readyTime); { std::lock_guard lock(mMutex); if (!mStarted) { return; } auto const scheduleResult = mRegistration.schedule({.workDuration = mWorkDuration.count(), .readyDuration = mReadyDuration.count(), .earliestVsync = vsyncTime}); LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback"); } } const std::string mName; scheduler::VSyncDispatch::Callback mCallback; mutable std::mutex mMutex; VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex); bool mStarted GUARDED_BY(mMutex) = false; std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns; std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns; std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns; }; DispSyncSource::DispSyncSource(scheduler::VSyncDispatch& vSyncDispatch, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name) : mName(name), mValue(base::StringPrintf("VSYNC-%s", name), 0), mTraceVsync(traceVsync), mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration), mReadyDuration(readyDuration) { mCallbackRepeater = std::make_unique(vSyncDispatch, std::bind(&DispSyncSource::onVsyncCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), name, workDuration, readyDuration, std::chrono::steady_clock::now().time_since_epoch()); } DispSyncSource::~DispSyncSource() = default; void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { mCallbackRepeater->start(mWorkDuration, mReadyDuration); // ATRACE_INT(mVsyncOnLabel.c_str(), 1); } else { mCallbackRepeater->stop(); // ATRACE_INT(mVsyncOnLabel.c_str(), 0); } mEnabled = enable; } void DispSyncSource::setCallback(VSyncSource::Callback* callback) { std::lock_guard lock(mCallbackMutex); mCallback = callback; } void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { std::lock_guard lock(mVsyncMutex); mWorkDuration = workDuration; mReadyDuration = readyDuration; // If we're not enabled, we don't need to mess with the listeners if (!mEnabled) { return; } mCallbackRepeater->start(mWorkDuration, mReadyDuration); } void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { VSyncSource::Callback* callback; { std::lock_guard lock(mCallbackMutex); callback = mCallback; } if (mTraceVsync) { mValue = (mValue + 1) % 2; } if (callback != nullptr) { callback->onVSyncEvent(targetWakeupTime, vsyncTime, readyTime); } } void DispSyncSource::dump(std::string& result) const { std::lock_guard lock(mVsyncMutex); StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled"); } } // namespace android::scheduler