You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
7.1 KiB
193 lines
7.1 KiB
/*
|
|
* 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 <android-base/stringprintf.h>
|
|
#include <utils/Trace.h>
|
|
#include <mutex>
|
|
|
|
#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<CallbackRepeater>(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
|