| /* | 
 |  * 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 <cutils/properties.h> | 
 | #include <utils/Trace.h> | 
 |  | 
 | #include <chrono> | 
 | #include <cinttypes> | 
 | #include <mutex> | 
 |  | 
 | 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<std::mutex> 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<std::mutex> 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<std::mutex> 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" |