|  | /* | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <utils/Errors.h> | 
|  |  | 
|  | #include <mutex> | 
|  |  | 
|  | using namespace android::surfaceflinger; | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | /* | 
|  | * Modulates the vsync-offsets depending on current SurfaceFlinger state. | 
|  | */ | 
|  | class VSyncModulator { | 
|  | private: | 
|  | // Number of frames we'll keep the early phase offsets once they are activated. This acts as a | 
|  | // low-pass filter in case the client isn't quick enough in sending new transactions. | 
|  | const int MIN_EARLY_FRAME_COUNT = 2; | 
|  |  | 
|  | public: | 
|  | struct Offsets { | 
|  | nsecs_t sf; | 
|  | nsecs_t app; | 
|  | }; | 
|  |  | 
|  | // Sets the phase offsets | 
|  | // | 
|  | // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction | 
|  | //          as early. May be the same as late, in which case we don't shift offsets. | 
|  | // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition | 
|  | //            and the transaction was marked as early, we'll use sfEarly. | 
|  | // sfLate: The regular SF vsync phase offset. | 
|  | // appEarly: Like sfEarly, but for the app-vsync | 
|  | // appEarlyGl: Like sfEarlyGl, but for the app-vsync. | 
|  | // appLate: The regular app vsync phase offset. | 
|  | void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) { | 
|  | mEarlyOffsets = early; | 
|  | mEarlyGlOffsets = earlyGl; | 
|  | mLateOffsets = late; | 
|  | mOffsets = late; | 
|  | } | 
|  |  | 
|  | Offsets getEarlyOffsets() const { return mEarlyOffsets; } | 
|  |  | 
|  | Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; } | 
|  |  | 
|  | void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { | 
|  | mSfEventThread = sfEventThread; | 
|  | mAppEventThread = appEventThread; | 
|  | } | 
|  |  | 
|  | void setSchedulerAndHandles(Scheduler* scheduler, | 
|  | Scheduler::ConnectionHandle* appConnectionHandle, | 
|  | Scheduler::ConnectionHandle* sfConnectionHandle) { | 
|  | mScheduler = scheduler; | 
|  | mAppConnectionHandle = appConnectionHandle; | 
|  | mSfConnectionHandle = sfConnectionHandle; | 
|  | } | 
|  |  | 
|  | void setTransactionStart(Scheduler::TransactionStart transactionStart) { | 
|  | if (transactionStart == Scheduler::TransactionStart::EARLY) { | 
|  | mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT; | 
|  | } | 
|  |  | 
|  | // An early transaction stays an early transaction. | 
|  | if (transactionStart == mTransactionStart || | 
|  | mTransactionStart == Scheduler::TransactionStart::EARLY) { | 
|  | return; | 
|  | } | 
|  | mTransactionStart = transactionStart; | 
|  | updateOffsets(); | 
|  | } | 
|  |  | 
|  | void onTransactionHandled() { | 
|  | if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return; | 
|  | mTransactionStart = Scheduler::TransactionStart::NORMAL; | 
|  | updateOffsets(); | 
|  | } | 
|  |  | 
|  | void onRefreshed(bool usedRenderEngine) { | 
|  | bool updateOffsetsNeeded = false; | 
|  | if (mRemainingEarlyFrameCount > 0) { | 
|  | mRemainingEarlyFrameCount--; | 
|  | updateOffsetsNeeded = true; | 
|  | } | 
|  | if (usedRenderEngine != mLastFrameUsedRenderEngine) { | 
|  | mLastFrameUsedRenderEngine = usedRenderEngine; | 
|  | updateOffsetsNeeded = true; | 
|  | } | 
|  | if (updateOffsetsNeeded) { | 
|  | updateOffsets(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | void updateOffsets() { | 
|  | const Offsets desired = getOffsets(); | 
|  | const Offsets current = mOffsets; | 
|  |  | 
|  | bool changed = false; | 
|  | if (desired.sf != current.sf) { | 
|  | if (mSfConnectionHandle != nullptr) { | 
|  | mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf); | 
|  | } else { | 
|  | mSfEventThread->setPhaseOffset(desired.sf); | 
|  | } | 
|  | changed = true; | 
|  | } | 
|  | if (desired.app != current.app) { | 
|  | if (mSfConnectionHandle != nullptr) { | 
|  | mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app); | 
|  | } else { | 
|  | mAppEventThread->setPhaseOffset(desired.app); | 
|  | } | 
|  | changed = true; | 
|  | } | 
|  |  | 
|  | if (changed) { | 
|  | mOffsets = desired; | 
|  | } | 
|  | } | 
|  |  | 
|  | Offsets getOffsets() { | 
|  | if (mTransactionStart == Scheduler::TransactionStart::EARLY || | 
|  | mRemainingEarlyFrameCount > 0) { | 
|  | return mEarlyOffsets; | 
|  | } else if (mLastFrameUsedRenderEngine) { | 
|  | return mEarlyGlOffsets; | 
|  | } else { | 
|  | return mLateOffsets; | 
|  | } | 
|  | } | 
|  |  | 
|  | Offsets mLateOffsets; | 
|  | Offsets mEarlyOffsets; | 
|  | Offsets mEarlyGlOffsets; | 
|  |  | 
|  | EventThread* mSfEventThread = nullptr; | 
|  | EventThread* mAppEventThread = nullptr; | 
|  |  | 
|  | Scheduler* mScheduler = nullptr; | 
|  | Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr; | 
|  | Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr; | 
|  |  | 
|  | std::atomic<Offsets> mOffsets; | 
|  |  | 
|  | std::atomic<Scheduler::TransactionStart> mTransactionStart = | 
|  | Scheduler::TransactionStart::NORMAL; | 
|  | std::atomic<bool> mLastFrameUsedRenderEngine = false; | 
|  | std::atomic<int> mRemainingEarlyFrameCount = 0; | 
|  | }; | 
|  |  | 
|  | } // namespace android |