716 lines
26 KiB
C++
716 lines
26 KiB
C++
|
|
/*
|
||
|
|
* Copyright 2019 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 <vector>
|
||
|
|
|
||
|
|
#include <android-base/stringprintf.h>
|
||
|
|
#include <ftl/concat.h>
|
||
|
|
#include <utils/Trace.h>
|
||
|
|
#include <log/log_main.h>
|
||
|
|
|
||
|
|
#include <scheduler/TimeKeeper.h>
|
||
|
|
|
||
|
|
#include "VSyncDispatchTimerQueue.h"
|
||
|
|
#include "VSyncTracker.h"
|
||
|
|
|
||
|
|
#ifdef MTK_AOSP_DISPLAY_BUGFIX
|
||
|
|
#include "scheduler/Timer.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef MTK_SF_MSYNC_3
|
||
|
|
#include <android-base/properties.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef MTK_SF_KICK_IDLE
|
||
|
|
#include "mediatek/KickIdleHelper.h"
|
||
|
|
#endif
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
#include <gui/TraceUtils.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#undef LOG_TAG
|
||
|
|
#define LOG_TAG "VSyncDispatch"
|
||
|
|
|
||
|
|
namespace android::scheduler {
|
||
|
|
|
||
|
|
using base::StringAppendF;
|
||
|
|
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
static bool isTraceOn() {
|
||
|
|
static bool enable = false;
|
||
|
|
static bool read = false;
|
||
|
|
if (!read) {
|
||
|
|
enable = android::base::GetBoolProperty("debug.sf.vst_trace", false);
|
||
|
|
read = true;
|
||
|
|
}
|
||
|
|
return enable;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void traceInt64If(const char* name, int64_t value) {
|
||
|
|
if (CC_UNLIKELY(isTraceOn())) {
|
||
|
|
ATRACE_INT64(name, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#define VST_TRACE(x, ...) \
|
||
|
|
{ \
|
||
|
|
if (CC_UNLIKELY(isTraceOn())) { \
|
||
|
|
ATRACE_FORMAT("VST: " x, ##__VA_ARGS__); \
|
||
|
|
} \
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
|
||
|
|
const VSyncDispatch::ScheduleTiming& timing) {
|
||
|
|
return nextVsyncTime - timing.readyDuration - timing.workDuration;
|
||
|
|
}
|
||
|
|
|
||
|
|
nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
|
||
|
|
const VSyncDispatch::ScheduleTiming& timing) {
|
||
|
|
const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
|
||
|
|
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
|
||
|
|
return getExpectedCallbackTime(nextVsyncTime, timing);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
VSyncDispatch::~VSyncDispatch() = default;
|
||
|
|
VSyncTracker::~VSyncTracker() = default;
|
||
|
|
|
||
|
|
VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string name,
|
||
|
|
VSyncDispatch::Callback callback,
|
||
|
|
nsecs_t minVsyncDistance)
|
||
|
|
: mName(std::move(name)),
|
||
|
|
mCallback(std::move(callback)),
|
||
|
|
mMinVsyncDistance(minVsyncDistance) {}
|
||
|
|
|
||
|
|
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
|
||
|
|
return mLastDispatchTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string_view VSyncDispatchTimerQueueEntry::name() const {
|
||
|
|
return mName;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
|
||
|
|
if (!mArmedInfo) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
return {mArmedInfo->mActualWakeupTime};
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const {
|
||
|
|
if (!mArmedInfo) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
return {mArmedInfo->mActualReadyTime};
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
|
||
|
|
if (!mArmedInfo) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
return {mArmedInfo->mActualVsyncTime};
|
||
|
|
}
|
||
|
|
|
||
|
|
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
|
||
|
|
VSyncTracker& tracker, nsecs_t now) {
|
||
|
|
auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
|
||
|
|
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
|
||
|
|
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
|
||
|
|
|
||
|
|
bool const wouldSkipAVsyncTarget =
|
||
|
|
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
|
||
|
|
bool const wouldSkipAWakeup =
|
||
|
|
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
|
||
|
|
if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
|
||
|
|
return getExpectedCallbackTime(nextVsyncTime, timing);
|
||
|
|
}
|
||
|
|
|
||
|
|
nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
|
||
|
|
nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
|
||
|
|
|
||
|
|
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
|
||
|
|
mScheduleTiming = timing;
|
||
|
|
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-wakeup %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualWakeupTime : 0);
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-vsync %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualVsyncTime : 0);
|
||
|
|
#endif
|
||
|
|
return getExpectedCallbackTime(nextVsyncTime, timing);
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
|
||
|
|
mWorkloadUpdateInfo = timing;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
|
||
|
|
return mWorkloadUpdateInfo.has_value();
|
||
|
|
}
|
||
|
|
|
||
|
|
nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker,
|
||
|
|
nsecs_t nextVsyncTime) const {
|
||
|
|
bool const alreadyDispatchedForVsync = mLastDispatchTime &&
|
||
|
|
((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
|
||
|
|
(*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
|
||
|
|
const nsecs_t currentPeriod = tracker.currentPeriod();
|
||
|
|
bool const nextVsyncTooClose = mLastDispatchTime &&
|
||
|
|
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
|
||
|
|
if (alreadyDispatchedForVsync) {
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
VST_TRACE("%s: name=%s, alreadyDispatchedForVsync", __func__, name().data());
|
||
|
|
#endif
|
||
|
|
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (nextVsyncTooClose) {
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
VST_TRACE("%s: name=%s, nextVsyncTooClose", __func__, name().data());
|
||
|
|
#endif
|
||
|
|
#ifdef MTK_AOSP_DISPLAY_BUGFIX
|
||
|
|
if (name().find("app") != std::string_view::npos) {
|
||
|
|
return nextVsyncTime;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod);
|
||
|
|
}
|
||
|
|
|
||
|
|
return nextVsyncTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
|
||
|
|
if (!mArmedInfo && !mWorkloadUpdateInfo) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mWorkloadUpdateInfo) {
|
||
|
|
mScheduleTiming = *mWorkloadUpdateInfo;
|
||
|
|
mWorkloadUpdateInfo.reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
|
||
|
|
const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
|
||
|
|
|
||
|
|
const auto nextVsyncTime =
|
||
|
|
adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
|
||
|
|
tracker.nextAnticipatedVSyncTimeFrom(earliestVsync));
|
||
|
|
const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
|
||
|
|
const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
|
||
|
|
|
||
|
|
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-wakeup %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualWakeupTime : 0);
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-vsync %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualVsyncTime : 0);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef MTK_SF_MSYNC
|
||
|
|
void VSyncDispatchTimerQueueEntry::setLastDispatchTime(nsecs_t vsyncTime) {
|
||
|
|
mLastDispatchTime = vsyncTime;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-mLastDispatchTime %s", name().data()).c_str(),
|
||
|
|
mLastDispatchTime ? *mLastDispatchTime : 0);
|
||
|
|
#endif
|
||
|
|
disarm();
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueueEntry::disarm() {
|
||
|
|
mArmedInfo.reset();
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-wakeup %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualWakeupTime : 0);
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-vsync %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualVsyncTime : 0);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
nsecs_t VSyncDispatchTimerQueueEntry::executing() {
|
||
|
|
mLastDispatchTime = mArmedInfo->mActualVsyncTime;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-lastWakeup %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualWakeupTime : 0);
|
||
|
|
traceInt64If(android::base::StringPrintf("VST-lastVsync %s", name().data()).c_str(),
|
||
|
|
mArmedInfo ? mArmedInfo->mActualVsyncTime : 0);
|
||
|
|
#endif
|
||
|
|
disarm();
|
||
|
|
return *mLastDispatchTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp,
|
||
|
|
nsecs_t deadlineTimestamp) {
|
||
|
|
{
|
||
|
|
std::lock_guard<std::mutex> lk(mRunningMutex);
|
||
|
|
mRunning = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp);
|
||
|
|
|
||
|
|
std::lock_guard<std::mutex> lk(mRunningMutex);
|
||
|
|
mRunning = false;
|
||
|
|
mCv.notify_all();
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
|
||
|
|
std::unique_lock<std::mutex> lk(mRunningMutex);
|
||
|
|
mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
|
||
|
|
std::lock_guard<std::mutex> lk(mRunningMutex);
|
||
|
|
std::string armedInfo;
|
||
|
|
if (mArmedInfo) {
|
||
|
|
StringAppendF(&armedInfo,
|
||
|
|
"[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
|
||
|
|
(mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
|
||
|
|
(mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
|
||
|
|
(mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
|
||
|
|
}
|
||
|
|
|
||
|
|
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
|
||
|
|
mRunning ? "(in callback function)" : "", armedInfo.c_str());
|
||
|
|
StringAppendF(&result,
|
||
|
|
"\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
|
||
|
|
"to now\n",
|
||
|
|
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
|
||
|
|
(mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
|
||
|
|
|
||
|
|
if (mLastDispatchTime) {
|
||
|
|
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
|
||
|
|
(systemTime() - *mLastDispatchTime) / 1e6f);
|
||
|
|
} else {
|
||
|
|
StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
|
||
|
|
VsyncSchedule::TrackerPtr tracker,
|
||
|
|
nsecs_t timerSlack, nsecs_t minVsyncDistance)
|
||
|
|
: mTimeKeeper(std::move(tk)),
|
||
|
|
mTracker(std::move(tracker)),
|
||
|
|
mTimerSlack(timerSlack),
|
||
|
|
mMinVsyncDistance(minVsyncDistance) {}
|
||
|
|
|
||
|
|
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
|
||
|
|
#ifdef MTK_AOSP_DISPLAY_BUGFIX
|
||
|
|
{
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
cancelTimer();
|
||
|
|
for (auto& [_, entry] : mCallbacks) {
|
||
|
|
ALOGE("Forgot to unregister a callback on VSyncDispatch!");
|
||
|
|
entry->ensureNotRunning();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
ALOGD("~VSyncDispatchTimerQueue: destroy previuos running Timer");
|
||
|
|
#endif
|
||
|
|
mTimeKeeper.reset(new Timer());
|
||
|
|
#else
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
cancelTimer();
|
||
|
|
for (auto& [_, entry] : mCallbacks) {
|
||
|
|
ALOGE("Forgot to unregister a callback on VSyncDispatch!");
|
||
|
|
entry->ensureNotRunning();
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::cancelTimer() {
|
||
|
|
mIntendedWakeupTime = kInvalidTime;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If("VST-mIntendedWakeupTime", 0);
|
||
|
|
#endif
|
||
|
|
mTimeKeeper->alarmCancel();
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
|
||
|
|
mIntendedWakeupTime = targetTime;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If("VST-mIntendedWakeupTime", mIntendedWakeupTime);
|
||
|
|
#endif
|
||
|
|
mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
|
||
|
|
mIntendedWakeupTime);
|
||
|
|
mLastTimerSchedule = mTimeKeeper->now();
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If("VST-mLastTimerSchedule", mLastTimerSchedule);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
|
||
|
|
rearmTimerSkippingUpdateFor(now, mCallbacks.end());
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
|
||
|
|
nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
|
||
|
|
std::optional<nsecs_t> min;
|
||
|
|
std::optional<nsecs_t> targetVsync;
|
||
|
|
std::optional<std::string_view> nextWakeupName;
|
||
|
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||
|
|
auto& callback = it->second;
|
||
|
|
if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (it != skipUpdateIt) {
|
||
|
|
callback->update(*mTracker, now);
|
||
|
|
}
|
||
|
|
auto const wakeupTime = *callback->wakeupTime();
|
||
|
|
if (!min || *min > wakeupTime) {
|
||
|
|
nextWakeupName = callback->name();
|
||
|
|
min = wakeupTime;
|
||
|
|
targetVsync = callback->targetVsync();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (min && min < mIntendedWakeupTime) {
|
||
|
|
if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
|
||
|
|
ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
|
||
|
|
"us; VSYNC in ", ns2us(*targetVsync - now), "us");
|
||
|
|
ATRACE_NAME(trace.c_str());
|
||
|
|
}
|
||
|
|
setTimer(*min, now);
|
||
|
|
#ifdef MTK_SF_KICK_IDLE
|
||
|
|
if (nextWakeupName && targetVsync &&
|
||
|
|
// trigger kick idle for sf but not app & appSf
|
||
|
|
(*nextWakeupName).find("app") == std::string_view::npos &&
|
||
|
|
(*nextWakeupName).find("sf") != std::string_view::npos) {
|
||
|
|
KickIdleHelper::getInstance().kickIdle(*targetVsync);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
} else {
|
||
|
|
ATRACE_NAME("cancel timer");
|
||
|
|
cancelTimer();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::timerCallback() {
|
||
|
|
struct Invocation {
|
||
|
|
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
|
||
|
|
nsecs_t vsyncTimestamp;
|
||
|
|
nsecs_t wakeupTimestamp;
|
||
|
|
nsecs_t deadlineTimestamp;
|
||
|
|
};
|
||
|
|
std::vector<Invocation> invocations;
|
||
|
|
#ifdef MTK_SF_MSYNC_3
|
||
|
|
Invocation sf_invocation = {nullptr, -1, -1, -1};
|
||
|
|
Invocation app_invocation = {nullptr, -1, -1, -1};
|
||
|
|
Invocation appSf_invocation = {nullptr, -1, -1, -1};
|
||
|
|
#endif
|
||
|
|
{
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
auto const now = mTimeKeeper->now();
|
||
|
|
mLastTimerCallback = now;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If("VST-mLastTimerCallback", mLastTimerCallback);
|
||
|
|
#endif
|
||
|
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||
|
|
auto& callback = it->second;
|
||
|
|
auto const wakeupTime = callback->wakeupTime();
|
||
|
|
if (!wakeupTime) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
auto const readyTime = callback->readyTime();
|
||
|
|
|
||
|
|
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
|
||
|
|
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
|
||
|
|
callback->executing();
|
||
|
|
invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
|
||
|
|
*wakeupTime, *readyTime});
|
||
|
|
}
|
||
|
|
#ifdef MTK_SF_MSYNC_3
|
||
|
|
else {
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
VST_TRACE("%s: name=%s, wakeupTime > mIntendedWakeupTime, mTimerSlack=%" PRId64 ", lagAllowance=%" PRId64
|
||
|
|
", wakeup in=%" PRId64 ", lastExecutedVsyncTarget=%" PRId64,
|
||
|
|
__func__, callback->name().data(), mTimerSlack, lagAllowance,
|
||
|
|
*wakeupTime - now, *callback->lastExecutedVsyncTarget());
|
||
|
|
#endif
|
||
|
|
// Before calling executing(), we should use targetVsync.
|
||
|
|
if (strncmp(callback->name().data(), "appSf", 5) == 0) {
|
||
|
|
appSf_invocation = {callback, *callback->targetVsync(),
|
||
|
|
*wakeupTime, *readyTime};
|
||
|
|
} else if (strncmp(callback->name().data(), "sf", 2) == 0) {
|
||
|
|
sf_invocation = {callback, *callback->targetVsync(),
|
||
|
|
*wakeupTime, *readyTime};
|
||
|
|
} else if (strncmp(callback->name().data(), "app", 3) == 0) {
|
||
|
|
app_invocation = {callback, *callback->targetVsync(),
|
||
|
|
*wakeupTime, *readyTime};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
mIntendedWakeupTime = kInvalidTime;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If("VST-mIntendedWakeupTime", 0);
|
||
|
|
#endif
|
||
|
|
rearmTimer(mTimeKeeper->now());
|
||
|
|
|
||
|
|
#ifdef MTK_SF_MSYNC_3
|
||
|
|
{
|
||
|
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||
|
|
auto& callback = it->second;
|
||
|
|
Invocation * _invoc = nullptr;
|
||
|
|
if (strncmp(callback->name().data(), "appSf", 5) == 0) {
|
||
|
|
_invoc = &appSf_invocation;
|
||
|
|
} else if (strncmp(callback->name().data(), "sf", 2) == 0) {
|
||
|
|
_invoc = &sf_invocation;
|
||
|
|
} else if (strncmp(callback->name().data(), "app", 3) == 0) {
|
||
|
|
_invoc = &app_invocation;
|
||
|
|
}
|
||
|
|
if (_invoc != nullptr && _invoc->vsyncTimestamp != -1 && _invoc->callback != nullptr && callback->targetVsync()) {
|
||
|
|
nsecs_t periodThreshold = static_cast<nsecs_t>(mTracker->currentPeriod() * 0.9);
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
VST_TRACE("%s: name=%s, _invoc->wakeupTimestamp=%" PRId64 ", callback->wakeupTime()=%" PRId64
|
||
|
|
", _invoc->vsyncTimestamp=%" PRId64 ", callback->lastExecutedVsyncTarget()=%" PRId64
|
||
|
|
", new wakeup in=%" PRId64 ", periodThreshold=%" PRId64,
|
||
|
|
__func__, callback->name().data(),
|
||
|
|
_invoc->wakeupTimestamp, *callback->wakeupTime(),
|
||
|
|
_invoc->vsyncTimestamp, *callback->lastExecutedVsyncTarget(),
|
||
|
|
*callback->wakeupTime() - now, periodThreshold);
|
||
|
|
#endif
|
||
|
|
// check this invoc is delayed with almost a vsync.
|
||
|
|
if (((*callback->wakeupTime() - _invoc->wakeupTimestamp) > periodThreshold) &&
|
||
|
|
// check this invoc's target vsync is not fired yet.
|
||
|
|
(_invoc->vsyncTimestamp - *callback->lastExecutedVsyncTarget() > periodThreshold) &&
|
||
|
|
// check if this invoc is about to fired before re-schedule.
|
||
|
|
(_invoc->wakeupTimestamp - now < ms2ns(2))) {
|
||
|
|
{
|
||
|
|
std::string _trace = "";
|
||
|
|
base::StringAppendF(&_trace, "detect re-schedule timer and skip a target vsync, name=%s",
|
||
|
|
callback->name().data());
|
||
|
|
ATRACE_NAME(_trace.c_str());
|
||
|
|
}
|
||
|
|
callback->executing();
|
||
|
|
invocations.emplace_back(Invocation{_invoc->callback, _invoc->vsyncTimestamp,
|
||
|
|
_invoc->wakeupTimestamp, _invoc->deadlineTimestamp});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
for (auto const& invocation : invocations) {
|
||
|
|
invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
|
||
|
|
invocation.deadlineTimestamp);
|
||
|
|
#ifdef MTK_SF_KICK_IDLE
|
||
|
|
// trigger kick idle for app but not sf & appSf
|
||
|
|
if (strncmp(invocation.callback->name().data(), "appSf", 5) != 0 &&
|
||
|
|
strncmp(invocation.callback->name().data(), "sf", 2) != 0) {
|
||
|
|
KickIdleHelper::getInstance().kickIdle(invocation.vsyncTimestamp);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
|
||
|
|
Callback callback, std::string callbackName) {
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
return CallbackToken{
|
||
|
|
mCallbacks
|
||
|
|
.emplace(++mCallbackToken,
|
||
|
|
std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
|
||
|
|
std::move(callback),
|
||
|
|
mMinVsyncDistance))
|
||
|
|
.first->first};
|
||
|
|
}
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
|
||
|
|
std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
|
||
|
|
{
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
auto it = mCallbacks.find(token);
|
||
|
|
if (it != mCallbacks.end()) {
|
||
|
|
entry = it->second;
|
||
|
|
mCallbacks.erase(it);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (entry) {
|
||
|
|
entry->ensureNotRunning();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
|
||
|
|
ScheduleTiming scheduleTiming) {
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
return scheduleLocked(token, scheduleTiming);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token,
|
||
|
|
ScheduleTiming scheduleTiming) {
|
||
|
|
auto it = mCallbacks.find(token);
|
||
|
|
if (it == mCallbacks.end()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
auto& callback = it->second;
|
||
|
|
auto const now = mTimeKeeper->now();
|
||
|
|
|
||
|
|
/* If the timer thread will run soon, we'll apply this work update via the callback
|
||
|
|
* timer recalculation to avoid cancelling a callback that is about to fire. */
|
||
|
|
auto const rearmImminent = now > mIntendedWakeupTime;
|
||
|
|
if (CC_UNLIKELY(rearmImminent)) {
|
||
|
|
callback->addPendingWorkloadUpdate(scheduleTiming);
|
||
|
|
return getExpectedCallbackTime(*mTracker, now, scheduleTiming);
|
||
|
|
}
|
||
|
|
|
||
|
|
const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now);
|
||
|
|
if (!result.has_value()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
|
||
|
|
rearmTimerSkippingUpdateFor(now, it);
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) {
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
const auto it = mCallbacks.find(token);
|
||
|
|
if (it == mCallbacks.end()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
auto& callback = it->second;
|
||
|
|
if (!callback->targetVsync().has_value()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
return scheduleLocked(token, scheduleTiming);
|
||
|
|
}
|
||
|
|
|
||
|
|
CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
|
||
|
|
auto it = mCallbacks.find(token);
|
||
|
|
if (it == mCallbacks.end()) {
|
||
|
|
return CancelResult::Error;
|
||
|
|
}
|
||
|
|
auto& callback = it->second;
|
||
|
|
|
||
|
|
auto const wakeupTime = callback->wakeupTime();
|
||
|
|
if (wakeupTime) {
|
||
|
|
callback->disarm();
|
||
|
|
|
||
|
|
if (*wakeupTime == mIntendedWakeupTime) {
|
||
|
|
mIntendedWakeupTime = kInvalidTime;
|
||
|
|
#ifdef MTK_SF_DEBUG_SUPPORT
|
||
|
|
traceInt64If("VST-mIntendedWakeupTime", 0);
|
||
|
|
#endif
|
||
|
|
rearmTimer(mTimeKeeper->now());
|
||
|
|
}
|
||
|
|
return CancelResult::Cancelled;
|
||
|
|
}
|
||
|
|
return CancelResult::TooLate;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef MTK_SF_MSYNC
|
||
|
|
void VSyncDispatchTimerQueue::setLastDispatchTime(CallbackToken token, nsecs_t vsyncTime) {
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
|
||
|
|
auto it = mCallbacks.find(token);
|
||
|
|
if (it == mCallbacks.end()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
auto& callback = it->second;
|
||
|
|
callback->setLastDispatchTime(vsyncTime);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void VSyncDispatchTimerQueue::dump(std::string& result) const {
|
||
|
|
std::lock_guard lock(mMutex);
|
||
|
|
StringAppendF(&result, "\tTimer:\n");
|
||
|
|
mTimeKeeper->dump(result);
|
||
|
|
StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
|
||
|
|
mMinVsyncDistance / 1e6f);
|
||
|
|
StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
|
||
|
|
(mIntendedWakeupTime - mTimeKeeper->now()) / 1e6f);
|
||
|
|
StringAppendF(&result, "\tmLastTimerCallback: %.2fms ago mLastTimerSchedule: %.2fms ago\n",
|
||
|
|
(mTimeKeeper->now() - mLastTimerCallback) / 1e6f,
|
||
|
|
(mTimeKeeper->now() - mLastTimerSchedule) / 1e6f);
|
||
|
|
StringAppendF(&result, "\tCallbacks:\n");
|
||
|
|
for (const auto& [token, entry] : mCallbacks) {
|
||
|
|
entry->dump(result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
VSyncCallbackRegistration::VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,
|
||
|
|
VSyncDispatch::Callback callback,
|
||
|
|
std::string callbackName)
|
||
|
|
: mDispatch(std::move(dispatch)),
|
||
|
|
mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))) {}
|
||
|
|
|
||
|
|
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
|
||
|
|
: mDispatch(std::move(other.mDispatch)), mToken(std::exchange(other.mToken, std::nullopt)) {}
|
||
|
|
|
||
|
|
VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
|
||
|
|
if (this == &other) return *this;
|
||
|
|
if (mToken) {
|
||
|
|
mDispatch->unregisterCallback(*mToken);
|
||
|
|
}
|
||
|
|
mDispatch = std::move(other.mDispatch);
|
||
|
|
mToken = std::exchange(other.mToken, std::nullopt);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
VSyncCallbackRegistration::~VSyncCallbackRegistration() {
|
||
|
|
if (mToken) mDispatch->unregisterCallback(*mToken);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
|
||
|
|
if (!mToken) {
|
||
|
|
return std::nullopt;
|
||
|
|
}
|
||
|
|
return mDispatch->schedule(*mToken, scheduleTiming);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
|
||
|
|
if (!mToken) {
|
||
|
|
return std::nullopt;
|
||
|
|
}
|
||
|
|
return mDispatch->update(*mToken, scheduleTiming);
|
||
|
|
}
|
||
|
|
|
||
|
|
CancelResult VSyncCallbackRegistration::cancel() {
|
||
|
|
if (!mToken) {
|
||
|
|
return CancelResult::Error;
|
||
|
|
}
|
||
|
|
return mDispatch->cancel(*mToken);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef MTK_SF_MSYNC
|
||
|
|
void VSyncCallbackRegistration::setLastDispatchTime(nsecs_t vsyncTime) {
|
||
|
|
if (!mToken) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
mDispatch->setLastDispatchTime(*mToken, vsyncTime);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
} // namespace android::scheduler
|