Merge changes I05a42795,Ibe230906
* changes:
SF: Decouple VsyncModulator from Scheduler
SF: Rename VsyncModulator
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index c55e9df..e452187 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -176,7 +176,7 @@
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
- "Scheduler/VSyncModulator.cpp",
+ "Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 9ec6d56..0ae9fef 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -19,7 +19,7 @@
#include <unordered_map>
#include "RefreshRateConfigs.h"
-#include "VSyncModulator.h"
+#include "VsyncModulator.h"
namespace android::scheduler {
@@ -31,7 +31,7 @@
*/
class PhaseConfiguration {
public:
- using Offsets = VSyncModulator::OffsetsConfig;
+ using Offsets = VsyncModulator::OffsetsConfig;
virtual ~PhaseConfiguration();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 6a5082a..4e7a9a1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -60,28 +60,13 @@
~ISchedulerCallback() = default;
};
-struct IPhaseOffsetControl {
- virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
-
-protected:
- ~IPhaseOffsetControl() = default;
-};
-
-class Scheduler : public IPhaseOffsetControl {
+class Scheduler {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- // Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart {
- Early, // DEPRECATED. Start the transaction early. Times out on its own
- EarlyStart, // Start the transaction early and keep this config until EarlyEnd
- EarlyEnd, // End the early config started at EarlyStart
- Normal // Start the transaction at the normal time
- };
-
Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
- virtual ~Scheduler();
+ ~Scheduler();
DispSync& getPrimaryDispSync();
@@ -104,7 +89,7 @@
void onScreenReleased(ConnectionHandle);
// Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
+ void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
void getDisplayStatInfo(DisplayStatInfo* stats);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
deleted file mode 100644
index 2567c04..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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"
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
deleted file mode 100644
index ab678c9..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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 <chrono>
-#include <mutex>
-
-#include "Scheduler.h"
-
-namespace android::scheduler {
-
-/*
- * 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 for a
- // transaction. This acts as a low-pass filter in case the client isn't quick enough in
- // sending new transactions.
- static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
-
- // Number of frames we'll keep the early gl phase offsets once they are activated.
- // This acts as a low-pass filter to avoid scenarios where we rapidly
- // switch in and out of gl composition.
- static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
-
- // Margin used to account for potential data races
- static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
-
-public:
- // Wrapper for a collection of surfaceflinger/app offsets for a particular
- // configuration.
- struct Offsets {
- nsecs_t sf;
- nsecs_t app;
-
- bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
-
- bool operator!=(const Offsets& other) const { return !(*this == other); }
- };
-
- struct OffsetsConfig {
- Offsets early; // For transactions with the eEarlyWakeup flag.
- Offsets earlyGl; // As above but while compositing with GL.
- Offsets late; // Default.
-
- bool operator==(const OffsetsConfig& other) const {
- return early == other.early && earlyGl == other.earlyGl && late == other.late;
- }
-
- bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
- };
-
- VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
- ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
-
- void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
-
- // Signals that a transaction has started, and changes offsets accordingly.
- void setTransactionStart(Scheduler::TransactionStart transactionStart);
-
- // Signals that a transaction has been completed, so that we can finish
- // special handling for a transaction.
- void onTransactionHandled();
-
- // Called when we send a refresh rate change to hardware composer, so that
- // we can move into early offsets.
- void onRefreshRateChangeInitiated();
-
- // Called when we detect from vsync signals that the refresh rate changed.
- // This way we can move out of early offsets if no longer necessary.
- void onRefreshRateChangeCompleted();
-
- // Called when the display is presenting a new frame. usedRenderEngine
- // should be set to true if RenderEngine was involved with composing the new
- // frame.
- void onRefreshed(bool usedRenderEngine);
-
- // Returns the offsets that we are currently using
- Offsets getOffsets() const EXCLUDES(mMutex);
-
-private:
- friend class VSyncModulatorTest;
- // Returns the next offsets that we should be using
- const Offsets& getNextOffsets() const REQUIRES(mMutex);
- // Updates offsets and persists them into the scheduler framework.
- void updateOffsets() EXCLUDES(mMutex);
- void updateOffsetsLocked() REQUIRES(mMutex);
-
- IPhaseOffsetControl& mPhaseOffsetControl;
- const ConnectionHandle mAppConnectionHandle;
- const ConnectionHandle mSfConnectionHandle;
-
- mutable std::mutex mMutex;
- OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
-
- Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
-
- std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::Normal;
- std::atomic<bool> mRefreshRateChangePending = false;
- std::atomic<bool> mExplicitEarlyWakeup = false;
- std::atomic<int> mRemainingEarlyFrameCount = 0;
- std::atomic<int> mRemainingRenderEngineUsageCount = 0;
- std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
- std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
-
- bool mTraceDetailedInfo = false;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
new file mode 100644
index 0000000..7a1b7e4
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "VsyncModulator"
+
+#include "VsyncModulator.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cinttypes>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
+
+VsyncModulator::VsyncModulator(const OffsetsConfig& config, Now now)
+ : mOffsetsConfig(config),
+ mNow(now),
+ mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+
+VsyncModulator::Offsets VsyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mOffsetsConfig = config;
+ return updateOffsetsLocked();
+}
+
+VsyncModulator::OffsetsOpt VsyncModulator::setTransactionSchedule(TransactionSchedule schedule) {
+ switch (schedule) {
+ case Schedule::EarlyStart:
+ ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+ mExplicitEarlyWakeup = true;
+ break;
+ case Schedule::EarlyEnd:
+ ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+ mExplicitEarlyWakeup = false;
+ break;
+ case Schedule::Early:
+ case Schedule::Late:
+ // No change to mExplicitEarlyWakeup for non-explicit states.
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ }
+
+ if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) {
+ mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
+ mEarlyTransactionStartTime = mNow();
+ }
+
+ // An early transaction stays an early transaction.
+ if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) {
+ return std::nullopt;
+ }
+ mTransactionSchedule = schedule;
+ return updateOffsets();
+}
+
+VsyncModulator::OffsetsOpt VsyncModulator::onTransactionCommit() {
+ mLastTransactionCommitTime = mNow();
+ if (mTransactionSchedule == Schedule::Late) return std::nullopt;
+ mTransactionSchedule = Schedule::Late;
+ return updateOffsets();
+}
+
+VsyncModulator::OffsetsOpt VsyncModulator::onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = true;
+ return updateOffsets();
+}
+
+VsyncModulator::OffsetsOpt VsyncModulator::onRefreshRateChangeCompleted() {
+ if (!mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = false;
+ return updateOffsets();
+}
+
+VsyncModulator::OffsetsOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
+ bool updateOffsetsNeeded = false;
+
+ if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
+ mLastTransactionCommitTime.load()) {
+ if (mEarlyTransactionFrames > 0) {
+ mEarlyTransactionFrames--;
+ updateOffsetsNeeded = true;
+ }
+ }
+ if (usedGpuComposition) {
+ mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES;
+ updateOffsetsNeeded = true;
+ } else if (mEarlyGpuFrames > 0) {
+ mEarlyGpuFrames--;
+ updateOffsetsNeeded = true;
+ }
+
+ if (!updateOffsetsNeeded) return std::nullopt;
+ return 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 || mTransactionSchedule == Schedule::EarlyEnd ||
+ mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
+ return mOffsetsConfig.early;
+ } else if (mEarlyGpuFrames > 0) {
+ return mOffsetsConfig.earlyGpu;
+ } else {
+ return mOffsetsConfig.late;
+ }
+}
+
+VsyncModulator::Offsets VsyncModulator::updateOffsets() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return updateOffsetsLocked();
+}
+
+VsyncModulator::Offsets VsyncModulator::updateOffsetsLocked() {
+ const Offsets& offsets = getNextOffsets();
+ mOffsets = offsets;
+
+ if (mTraceDetailedInfo) {
+ const bool isEarly = &offsets == &mOffsetsConfig.early;
+ const bool isEarlyGpu = &offsets == &mOffsetsConfig.earlyGpu;
+ const bool isLate = &offsets == &mOffsetsConfig.late;
+
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
+ }
+
+ return offsets;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
new file mode 100644
index 0000000..f920bd2
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <chrono>
+#include <mutex>
+#include <optional>
+
+#include <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+ Late, // Default.
+ Early, // Deprecated.
+ EarlyStart,
+ EarlyEnd
+};
+
+// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
+class VsyncModulator {
+public:
+ // Number of frames to keep early offsets after an early transaction or GPU composition.
+ // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
+ // composition strategy alternates on subsequent frames.
+ static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
+ static constexpr int MIN_EARLY_GPU_FRAMES = 2;
+
+ // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
+ // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
+ static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
+
+ // Phase offsets for SF and app deadlines from VSYNC.
+ struct Offsets {
+ nsecs_t sf;
+ nsecs_t app;
+
+ bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
+ bool operator!=(const Offsets& other) const { return !(*this == other); }
+ };
+
+ using OffsetsOpt = std::optional<Offsets>;
+
+ struct OffsetsConfig {
+ Offsets early; // Used for early transactions, and during refresh rate change.
+ Offsets earlyGpu; // Used during GPU composition.
+ Offsets late; // Default.
+
+ bool operator==(const OffsetsConfig& other) const {
+ return early == other.early && earlyGpu == other.earlyGpu && late == other.late;
+ }
+
+ bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
+ };
+
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = Clock::time_point;
+ using Now = TimePoint (*)();
+
+ explicit VsyncModulator(const OffsetsConfig&, Now = Clock::now);
+
+ Offsets getOffsets() const EXCLUDES(mMutex);
+
+ [[nodiscard]] Offsets setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
+
+ // Changes offsets in response to transaction flags or commit.
+ [[nodiscard]] OffsetsOpt setTransactionSchedule(TransactionSchedule);
+ [[nodiscard]] OffsetsOpt onTransactionCommit();
+
+ // Called when we send a refresh rate change to hardware composer, so that
+ // we can move into early offsets.
+ [[nodiscard]] OffsetsOpt onRefreshRateChangeInitiated();
+
+ // Called when we detect from VSYNC signals that the refresh rate changed.
+ // This way we can move out of early offsets if no longer necessary.
+ [[nodiscard]] OffsetsOpt onRefreshRateChangeCompleted();
+
+ [[nodiscard]] OffsetsOpt onDisplayRefresh(bool usedGpuComposition);
+
+private:
+ const Offsets& getNextOffsets() const REQUIRES(mMutex);
+ [[nodiscard]] Offsets updateOffsets() EXCLUDES(mMutex);
+ [[nodiscard]] Offsets updateOffsetsLocked() REQUIRES(mMutex);
+
+ mutable std::mutex mMutex;
+ OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
+
+ Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
+
+ using Schedule = TransactionSchedule;
+ std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
+ std::atomic<bool> mExplicitEarlyWakeup = false;
+
+ std::atomic<bool> mRefreshRateChangePending = false;
+
+ std::atomic<int> mEarlyTransactionFrames = 0;
+ std::atomic<int> mEarlyGpuFrames = 0;
+ std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
+ std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
+
+ const Now mNow;
+ const bool mTraceDetailedInfo;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b74d375..e9970cf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1036,10 +1036,9 @@
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// DispSync model is locked.
- mVSyncModulator->onRefreshRateChangeInitiated();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setConfigChangePending(true);
}
@@ -1098,8 +1097,7 @@
if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) {
mTimeStats->incrementRefreshRateSwitches();
}
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
ATRACE_INT("ActiveConfigFPS", refreshRate.getFps());
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -1119,9 +1117,9 @@
const auto& refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setConfigChangePending(false);
}
@@ -1600,7 +1598,7 @@
bool periodFlushed = false;
mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
if (periodFlushed) {
- mVSyncModulator->onRefreshRateChangeCompleted();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
}
}
@@ -1790,7 +1788,7 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ return mVsyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
: mPreviousPresentFences[1];
}
@@ -1823,7 +1821,7 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
// Inflate the expected present time if we're targetting the next vsync.
- return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
+ return mVsyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
}
void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
@@ -2120,7 +2118,8 @@
}
// TODO: b/160583065 Enable skip validation when SF caches all client composition layers
- mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
+ const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
+ modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
if (mVisibleRegionsDirty) {
@@ -2416,7 +2415,7 @@
// with mStateLock held to guarantee that mCurrentState won't change
// until the transaction is committed.
- mVSyncModulator->onTransactionHandled();
+ modulateVsync(&VsyncModulator::onTransactionCommit);
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
@@ -2977,6 +2976,7 @@
mRefreshRateStats->setConfigMode(currentConfig);
mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+ mVsyncModulator.emplace(mPhaseConfiguration->getCurrentOffsets());
// start the EventThread
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
@@ -2990,8 +2990,6 @@
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
- mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -3009,8 +3007,17 @@
vsyncPeriod);
}
-void SurfaceFlinger::commitTransaction()
-{
+void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
+ setPhaseOffsets(mVsyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()));
+}
+
+void SurfaceFlinger::setPhaseOffsets(const VsyncModulator::Offsets& offsets) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, offsets.app);
+ mScheduler->setPhaseOffset(mSfConnectionHandle, offsets.sf);
+}
+
+void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
mTransactionPending = false;
mAnimTransactionPending = false;
@@ -3254,16 +3261,13 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
+ return setTransactionFlags(flags, TransactionSchedule::Late);
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
- Scheduler::TransactionStart transactionStart) {
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) {
uint32_t old = mTransactionFlags.fetch_or(flags);
- mVSyncModulator->setTransactionStart(transactionStart);
- if ((old & flags)==0) { // wake the server up
- signalTransaction();
- }
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+ if ((old & flags) == 0) signalTransaction();
return old;
}
@@ -3477,17 +3481,11 @@
mForceTraversal = true;
}
- const auto transactionStart = [](uint32_t flags) {
- if (flags & eEarlyWakeup) {
- return Scheduler::TransactionStart::Early;
- }
- if (flags & eExplicitEarlyWakeupEnd) {
- return Scheduler::TransactionStart::EarlyEnd;
- }
- if (flags & eExplicitEarlyWakeupStart) {
- return Scheduler::TransactionStart::EarlyStart;
- }
- return Scheduler::TransactionStart::Normal;
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+ if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
}(flags);
if (transactionFlags) {
@@ -3507,7 +3505,7 @@
}
// this triggers the transaction
- setTransactionFlags(transactionFlags, transactionStart);
+ setTransactionFlags(transactionFlags, schedule);
if (flags & eAnimation) {
mAnimTransactionPending = true;
@@ -3545,11 +3543,10 @@
}
}
} else {
- // even if a transaction is not needed, we need to update VsyncModulator
- // about explicit early indications
- if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd) {
- mVSyncModulator->setTransactionStart(transactionStart);
+ // Update VsyncModulator state machine even if transaction is not needed.
+ if (schedule == TransactionSchedule::EarlyStart ||
+ schedule == TransactionSchedule::EarlyEnd) {
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0c5997c..3c24158 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -58,7 +58,7 @@
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
-#include "Scheduler/VSyncModulator.h"
+#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceTracing.h"
#include "TracedOrdinal.h"
@@ -549,8 +549,6 @@
void signalLayerUpdate();
void signalRefresh();
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
struct ActiveConfigInfo {
HwcConfigIndexType configId;
Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
@@ -604,7 +602,13 @@
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
+
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+ using VsyncModulator = scheduler::VsyncModulator;
+
void initScheduler(PhysicalDisplayId primaryDisplayId);
+ void updatePhaseConfiguration(const RefreshRate&);
+ void setPhaseOffsets(const VsyncModulator::Offsets&);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -615,6 +619,8 @@
/* ------------------------------------------------------------------------
* Transactions
*/
+ using TransactionSchedule = scheduler::TransactionSchedule;
+
void applyTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
@@ -638,7 +644,7 @@
// but there is no need to try and wake up immediately to do it. Rather we rely on
// onFrameAvailable or another layer update to wake us up.
void setTraversalNeeded();
- uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+ uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
@@ -843,14 +849,13 @@
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
/* ------------------------------------------------------------------------
- * VSync
+ * VSYNC
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
- REQUIRES(mStateLock);
+ void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock);
bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
@@ -1211,8 +1216,8 @@
// Stores phase offsets configured per refresh rate.
std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
- // Optional to defer construction until scheduler connections are created.
- std::optional<scheduler::VSyncModulator> mVSyncModulator;
+ // Optional to defer construction until PhaseConfiguration is created.
+ std::optional<scheduler::VsyncModulator> mVsyncModulator;
std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
@@ -1220,6 +1225,14 @@
std::atomic<nsecs_t> mExpectedPresentTime = 0;
hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
+ template <typename... Args,
+ typename Handler = VsyncModulator::OffsetsOpt (VsyncModulator::*)(Args...)>
+ void modulateVsync(Handler handler, Args... args) {
+ if (const auto offsets = (*mVsyncModulator.*handler)(args...)) {
+ setPhaseOffsets(*offsets);
+ }
+ }
+
/* ------------------------------------------------------------------------
* Generic Layer Metadata
*/
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index c46c914..4a59c19 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -61,7 +61,7 @@
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
- "VSyncModulatorTest.cpp",
+ "VsyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 0b74682..96bb74b 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -29,8 +29,7 @@
using namespace testing;
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
public:
@@ -53,7 +52,6 @@
TestablePhaseOffsetsAsDurations mPhaseDurations;
};
-namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
@@ -71,9 +69,9 @@
EXPECT_EQ(offsets.early.app, 833'334);
- EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+ EXPECT_EQ(offsets.earlyGpu.sf, 3'166'667);
- EXPECT_EQ(offsets.earlyGl.app, 15'500'001);
+ EXPECT_EQ(offsets.earlyGpu.app, 15'500'001);
}
TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
@@ -90,9 +88,9 @@
EXPECT_EQ(offsets.early.app, 833'333);
- EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+ EXPECT_EQ(offsets.earlyGpu.sf, -2'388'889);
- EXPECT_EQ(offsets.earlyGl.app, 9'944'444);
+ EXPECT_EQ(offsets.earlyGpu.app, 9'944'444);
}
TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
@@ -107,9 +105,9 @@
EXPECT_EQ(offsets.early.app, 1'000'000);
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.sf, 1'000'000);
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.app, 1'000'000);
};
phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
@@ -136,31 +134,20 @@
EXPECT_EQ(offsets.early.app, 35'527'208);
- EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+ EXPECT_EQ(offsets.earlyGpu.sf, 54'527'208);
- EXPECT_EQ(offsets.earlyGl.app, 33'527'208);
+ EXPECT_EQ(offsets.earlyGpu.app, 33'527'208);
}
-} // namespace
+TEST(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ struct PhaseOffsets : impl::PhaseOffsets {
+ using impl::PhaseOffsets::PhaseOffsets;
+ static PhaseOffsets get() {
+ return {{60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {}, 10'000'000};
+ }
+ };
-class TestablePhaseOffsets : public impl::PhaseOffsets {
-public:
- TestablePhaseOffsets()
- : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
- 10'000'000) {}
-};
-
-class PhaseOffsetsTest : public testing::Test {
-protected:
- PhaseOffsetsTest() = default;
- ~PhaseOffsetsTest() = default;
-
- TestablePhaseOffsets mPhaseOffsets;
-};
-
-namespace {
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+ auto offsets = PhaseOffsets::get().getOffsetsForRefreshRate(14.7f);
EXPECT_EQ(offsets.late.sf, 1'000'000);
@@ -170,14 +157,12 @@
EXPECT_EQ(offsets.early.app, 1'000'000);
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.sf, 1'000'000);
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.app, 1'000'000);
}
-} // namespace
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b349144..df15516 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -218,6 +218,7 @@
/*powerMode=*/hal::PowerMode::OFF);
mFlinger->mPhaseConfiguration =
mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncModulator.emplace(mFlinger->mPhaseConfiguration->getCurrentOffsets());
constexpr bool kUseContentDetectionV2 = false;
mScheduler =
@@ -227,10 +228,6 @@
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
-
- mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
- mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
deleted file mode 100644
index af1e84b..0000000
--- a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-#define LOG_NDEBUG 0
-
-#include "Scheduler/VSyncModulator.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using namespace testing;
-
-namespace android::scheduler {
-
-class MockScheduler final : public IPhaseOffsetControl {
-public:
- nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
-
-private:
- void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) override {
- mPhaseOffset[handle] = phaseOffset;
- }
-
- std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
-};
-
-class VSyncModulatorTest : public testing::Test {
-protected:
- static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
- VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
- // Add a 1ms slack to avoid strange timer race conditions.
- static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
-
- // Used to enumerate the different offsets we have
- enum {
- SF_LATE,
- APP_LATE,
- SF_EARLY,
- APP_EARLY,
- SF_EARLY_GL,
- APP_EARLY_GL,
- };
-
- std::unique_ptr<VSyncModulator> mVSyncModulator;
- MockScheduler mMockScheduler;
- ConnectionHandle mAppConnection{1};
- ConnectionHandle mSfConnection{2};
- VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
- {SF_EARLY_GL, APP_EARLY_GL},
- {SF_LATE, APP_LATE}};
-
- void SetUp() override {
- mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
- mSfConnection, mOffsets);
- mVSyncModulator->setPhaseOffsets(mOffsets);
-
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- };
-
- void TearDown() override { mVSyncModulator.reset(); }
-};
-
-TEST_F(VSyncModulatorTest, Normal) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- }
-}
-
-TEST_F(VSyncModulatorTest, EarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStart) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
new file mode 100644
index 0000000..d7093c6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VsyncModulator.h"
+
+namespace android::scheduler {
+
+class VsyncModulatorTest : public testing::Test {
+ enum Offsets {
+ SF_LATE,
+ APP_LATE,
+ SF_EARLY,
+ APP_EARLY,
+ SF_EARLY_GPU,
+ APP_EARLY_GPU,
+ };
+
+ static VsyncModulator::TimePoint Now() {
+ static VsyncModulator::TimePoint now;
+ return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME;
+ }
+
+protected:
+ static constexpr auto MIN_EARLY_TRANSACTION_FRAMES =
+ VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES;
+
+ using Schedule = scheduler::TransactionSchedule;
+
+ const VsyncModulator::Offsets kEarly{SF_EARLY, APP_EARLY};
+ const VsyncModulator::Offsets kEarlyGpu{SF_EARLY_GPU, APP_EARLY_GPU};
+ const VsyncModulator::Offsets kLate{SF_LATE, APP_LATE};
+
+ const VsyncModulator::OffsetsConfig mOffsets = {kEarly, kEarlyGpu, kLate};
+ VsyncModulator mVsyncModulator{mOffsets, Now};
+
+ void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setPhaseOffsets(mOffsets)); }
+};
+
+#define CHECK_COMMIT(result, offsets) \
+ EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
+ EXPECT_EQ(offsets, mVsyncModulator.getOffsets());
+
+#define CHECK_REFRESH(N, result, offsets) \
+ for (int i = 0; i < N; i++) { \
+ EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
+ EXPECT_EQ(offsets, mVsyncModulator.getOffsets()); \
+ }
+
+TEST_F(VsyncModulatorTest, Late) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+
+ CHECK_COMMIT(std::nullopt, kLate);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStart) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithEarly) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::Early));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+} // namespace android::scheduler