/* * 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. */ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "VSyncModulator.h" #include #include #include #include #include namespace android::scheduler { VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl, Scheduler::ConnectionHandle appConnectionHandle, Scheduler::ConnectionHandle sfConnectionHandle, const OffsetsConfig& config) : mPhaseOffsetControl(phaseOffsetControl), mAppConnectionHandle(appConnectionHandle), mSfConnectionHandle(sfConnectionHandle), mOffsetsConfig(config) { char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.vsync_trace_detailed_info", value, "0"); mTraceDetailedInfo = atoi(value); } void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) { std::lock_guard lock(mMutex); mOffsetsConfig = config; updateOffsetsLocked(); } void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) { switch (transactionStart) { case Scheduler::TransactionStart::EarlyStart: ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart"); mExplicitEarlyWakeup = true; break; case Scheduler::TransactionStart::EarlyEnd: ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart"); mExplicitEarlyWakeup = false; break; case Scheduler::TransactionStart::Normal: case Scheduler::TransactionStart::Early: // Non explicit don't change the explicit early wakeup state break; } if (mTraceDetailedInfo) { ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup); } if (!mExplicitEarlyWakeup && (transactionStart == Scheduler::TransactionStart::Early || transactionStart == Scheduler::TransactionStart::EarlyEnd)) { mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION; mEarlyTxnStartTime = std::chrono::steady_clock::now(); } // An early transaction stays an early transaction. if (transactionStart == mTransactionStart || mTransactionStart == Scheduler::TransactionStart::EarlyEnd) { return; } mTransactionStart = transactionStart; updateOffsets(); } void VSyncModulator::onTransactionHandled() { mTxnAppliedTime = std::chrono::steady_clock::now(); if (mTransactionStart == Scheduler::TransactionStart::Normal) return; mTransactionStart = Scheduler::TransactionStart::Normal; updateOffsets(); } void VSyncModulator::onRefreshRateChangeInitiated() { if (mRefreshRateChangePending) { return; } mRefreshRateChangePending = true; updateOffsets(); } void VSyncModulator::onRefreshRateChangeCompleted() { if (!mRefreshRateChangePending) { return; } mRefreshRateChangePending = false; updateOffsets(); } void VSyncModulator::onRefreshed(bool usedRenderEngine) { bool updateOffsetsNeeded = false; // Apply a margin to account for potential data races // This might make us stay in early offsets for one // additional frame but it's better to be conservative here. if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) { if (mRemainingEarlyFrameCount > 0) { mRemainingEarlyFrameCount--; updateOffsetsNeeded = true; } } if (usedRenderEngine) { mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION; updateOffsetsNeeded = true; } else if (mRemainingRenderEngineUsageCount > 0) { mRemainingRenderEngineUsageCount--; updateOffsetsNeeded = true; } if (updateOffsetsNeeded) { updateOffsets(); } } VSyncModulator::Offsets VSyncModulator::getOffsets() const { std::lock_guard lock(mMutex); return mOffsets; } const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const { // Early offsets are used if we're in the middle of a refresh rate // change, or if we recently begin a transaction. if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd || mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) { return mOffsetsConfig.early; } else if (mRemainingRenderEngineUsageCount > 0) { return mOffsetsConfig.earlyGl; } else { return mOffsetsConfig.late; } } void VSyncModulator::updateOffsets() { std::lock_guard lock(mMutex); updateOffsetsLocked(); } void VSyncModulator::updateOffsetsLocked() { const Offsets& offsets = getNextOffsets(); mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf); mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app); mOffsets = offsets; if (!mTraceDetailedInfo) { return; } const bool isEarly = &offsets == &mOffsetsConfig.early; const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl; const bool isLate = &offsets == &mOffsetsConfig.late; ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly); ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl); ATRACE_INT("Vsync-LateOffsetsOn", isLate); } } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion"