/* * 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 #undef LOG_TAG #define LOG_TAG "VSyncReactor" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include "../TracedOrdinal.h" #include "VSyncDispatch.h" #include "VSyncReactor.h" #include "VSyncTracker.h" #ifdef MTK_SF_MSYNC #include #endif namespace android::scheduler { #ifdef MTK_SF_DEBUG_SUPPORT static bool isTraceOn() { static bool enable = false; static bool read = false; if (!read) { enable = android::base::GetBoolProperty("debug.sf.vsr_trace", false); read = true; } return enable; } static void traceInt64If(const char* name, int64_t value) { if (CC_UNLIKELY(isTraceOn())) { ATRACE_INT64(name, value); } } #define VSR_TRACE(x, ...) \ { \ if (CC_UNLIKELY(isTraceOn())) { \ ATRACE_FORMAT("VSR: " x, ##__VA_ARGS__); \ } \ } #endif using base::StringAppendF; VsyncController::~VsyncController() = default; nsecs_t SystemClock::now() const { return systemTime(SYSTEM_TIME_MONOTONIC); } VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr clock, VSyncTracker& tracker, size_t pendingFenceLimit, bool supportKernelIdleTimer) : mId(id), mClock(std::move(clock)), mTracker(tracker), mPendingLimit(pendingFenceLimit), mSupportKernelIdleTimer(supportKernelIdleTimer) {} VSyncReactor::~VSyncReactor() = default; bool VSyncReactor::addPresentFence(std::shared_ptr fence) { #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: enter", __func__); #endif if (!fence) { #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: return falsse, no fence", __func__); #endif return false; } nsecs_t const signalTime = fence->getCachedSignalTime(); if (signalTime == Fence::SIGNAL_TIME_INVALID) { #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: return true, current fence is SIGNAL_TIME_INVALID", __func__); #endif return true; } std::lock_guard lock(mMutex); if (mExternalIgnoreFences || mInternalIgnoreFences) { #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: return true, mExternalIgnoreFences=%d, mInternalIgnoreFences=%d", __func__, mExternalIgnoreFences, mInternalIgnoreFences); #endif return true; } bool timestampAccepted = true; for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) { auto const time = (*it)->getCachedSignalTime(); if (time == Fence::SIGNAL_TIME_PENDING) { it++; } else if (time == Fence::SIGNAL_TIME_INVALID) { it = mUnfiredFences.erase(it); } else { timestampAccepted &= mTracker.addVsyncTimestamp(time); it = mUnfiredFences.erase(it); } } if (signalTime == Fence::SIGNAL_TIME_PENDING) { if (mPendingLimit == mUnfiredFences.size()) { mUnfiredFences.erase(mUnfiredFences.begin()); } mUnfiredFences.push_back(std::move(fence)); } else { timestampAccepted &= mTracker.addVsyncTimestamp(signalTime); } #ifdef MTK_SF_DEBUG_SUPPORT traceInt64If("VSR-mUnfiredFences", static_cast(mUnfiredFences.size())); #endif if (!timestampAccepted) { mMoreSamplesNeeded = true; setIgnorePresentFencesInternal(true); mPeriodConfirmationInProgress = true; } #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: leave, timestampAccepted=%d, mMoreSamplesNeeded=%d", __func__, timestampAccepted, mMoreSamplesNeeded); #endif return mMoreSamplesNeeded; } void VSyncReactor::setIgnorePresentFences(bool ignore) { std::lock_guard lock(mMutex); mExternalIgnoreFences = ignore; updateIgnorePresentFencesInternal(); } void VSyncReactor::setIgnorePresentFencesInternal(bool ignore) { mInternalIgnoreFences = ignore; updateIgnorePresentFencesInternal(); } void VSyncReactor::updateIgnorePresentFencesInternal() { if (mExternalIgnoreFences || mInternalIgnoreFences) { mUnfiredFences.clear(); #ifdef MTK_SF_DEBUG_SUPPORT traceInt64If("VSR-mUnfiredFences", static_cast(mUnfiredFences.size())); #endif } } void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) { ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodConfirmationInProgress = true; mPeriodTransitioningTo = newPeriod; mMoreSamplesNeeded = true; setIgnorePresentFencesInternal(true); } void VSyncReactor::endPeriodTransition() { ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodTransitioningTo.reset(); mPeriodConfirmationInProgress = false; mLastHwVsync.reset(); } void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) { ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period); std::lock_guard lock(mMutex); mLastHwVsync.reset(); if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; } else { startPeriodTransitionInternal(period); } } bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional HwcVsyncPeriod) { if (!mPeriodConfirmationInProgress) { return false; } if (mDisplayPowerMode == hal::PowerMode::DOZE || mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) { return true; } if (!mLastHwVsync && !HwcVsyncPeriod) { return false; } const bool periodIsChanging = mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod()); if (mSupportKernelIdleTimer && !periodIsChanging) { // Clear out the Composer-provided period and use the allowance logic below HwcVsyncPeriod = {}; } auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod(); static constexpr int allowancePercent = 10; static constexpr std::ratio allowancePercentRatio; auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den; if (HwcVsyncPeriod) { return std::abs(*HwcVsyncPeriod - period) < allowance; } auto const distance = vsync_timestamp - *mLastHwVsync; return std::abs(distance - period) < allowance; } bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional hwcVsyncPeriod, bool* periodFlushed) { assert(periodFlushed); std::lock_guard lock(mMutex); #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: enter, timestamp=%" PRId64 ", hwcVsyncPeriod=%" PRId64, __func__, timestamp, hwcVsyncPeriod ? *hwcVsyncPeriod : 0); #endif if (periodConfirmed(timestamp, hwcVsyncPeriod)) { ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value); #ifdef MTK_SF_DEBUG_SUPPORT traceInt64If("VSR-addHwVsyncTimestamp", 3); #endif if (mPeriodTransitioningTo) { mTracker.setPeriod(*mPeriodTransitioningTo); *periodFlushed = true; } if (mLastHwVsync) { mTracker.addVsyncTimestamp(*mLastHwVsync); } mTracker.addVsyncTimestamp(timestamp); endPeriodTransition(); mMoreSamplesNeeded = mTracker.needsMoreSamples(); } else if (mPeriodConfirmationInProgress) { ATRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value); #ifdef MTK_SF_DEBUG_SUPPORT traceInt64If("VSR-addHwVsyncTimestamp", 1); #endif mLastHwVsync = timestamp; mMoreSamplesNeeded = true; *periodFlushed = false; } else { ATRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value); #ifdef MTK_SF_DEBUG_SUPPORT traceInt64If("VSR-addHwVsyncTimestamp", 5); #endif *periodFlushed = false; mTracker.addVsyncTimestamp(timestamp); mMoreSamplesNeeded = mTracker.needsMoreSamples(); } if (!mMoreSamplesNeeded) { #ifdef MTK_SF_DEBUG_SUPPORT traceInt64If("VSR-addHwVsyncTimestamp", 0); #endif setIgnorePresentFencesInternal(false); } #ifdef MTK_SF_DEBUG_SUPPORT VSR_TRACE("%s: leave, mMoreSamplesNeeded=%d, periodFlushed=%d", __func__, mMoreSamplesNeeded, *periodFlushed); #endif return mMoreSamplesNeeded; } void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) { std::scoped_lock lock(mMutex); mDisplayPowerMode = powerMode; } void VSyncReactor::dump(std::string& result) const { std::lock_guard lock(mMutex); StringAppendF(&result, "VsyncReactor in use\n"); StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size()); StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n", mInternalIgnoreFences, mExternalIgnoreFences); StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n", mMoreSamplesNeeded, mPeriodConfirmationInProgress); if (mPeriodTransitioningTo) { StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo); } else { StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n"); } if (mLastHwVsync) { StringAppendF(&result, "Last HW vsync was %.2fms ago\n", (mClock->now() - *mLastHwVsync) / 1e6f); } else { StringAppendF(&result, "No Last HW vsync\n"); } StringAppendF(&result, "VSyncTracker:\n"); mTracker.dump(result); } } // namespace android::scheduler