summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/surfaceflinger/RegionSamplingThread.cpp127
-rw-r--r--services/surfaceflinger/RegionSamplingThread.h26
-rw-r--r--services/surfaceflinger/Scheduler/MessageQueue.cpp47
-rw-r--r--services/surfaceflinger/Scheduler/MessageQueue.h15
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp21
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h3
-rw-r--r--services/surfaceflinger/tests/unittests/MessageQueueTest.cpp20
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h1
8 files changed, 116 insertions, 144 deletions
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 00090d948a..653aca6cde 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -56,16 +56,14 @@ enum class samplingStep {
noWorkNeeded,
idleTimerWaiting,
waitForQuietFrame,
- waitForZeroPhase,
waitForSamplePhase,
sample
};
-constexpr auto timeForRegionSampling = 5000000ns;
-constexpr auto maxRegionSamplingSkips = 10;
constexpr auto defaultRegionSamplingWorkDuration = 3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+constexpr auto maxRegionSamplingDelay = 100ms;
// TODO: (b/127403193) duration to string conversion could probably be constexpr
template <typename Rep, typename Per>
inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
@@ -99,97 +97,22 @@ RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
}
}
-struct SamplingOffsetCallback : VSyncSource::Callback {
- SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
- std::chrono::nanoseconds targetSamplingWorkDuration)
- : mRegionSamplingThread(samplingThread),
- mTargetSamplingWorkDuration(targetSamplingWorkDuration),
- mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
- 0ns,
- /*traceVsync=*/false)) {
- mVSyncSource->setCallback(this);
- }
-
- ~SamplingOffsetCallback() { stopVsyncListener(); }
-
- SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
- SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
-
- void startVsyncListener() {
- std::lock_guard lock(mMutex);
- if (mVsyncListening) return;
-
- mPhaseIntervalSetting = Phase::ZERO;
- mVSyncSource->setVSyncEnabled(true);
- mVsyncListening = true;
- }
-
- void stopVsyncListener() {
- std::lock_guard lock(mMutex);
- stopVsyncListenerLocked();
- }
-
-private:
- void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
- if (!mVsyncListening) return;
-
- mVSyncSource->setVSyncEnabled(false);
- mVsyncListening = false;
- }
-
- void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
- nsecs_t /*deadlineTimestamp*/) final {
- std::unique_lock<decltype(mMutex)> lock(mMutex);
-
- if (mPhaseIntervalSetting == Phase::ZERO) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
- mPhaseIntervalSetting = Phase::SAMPLING;
- mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
- return;
- }
-
- if (mPhaseIntervalSetting == Phase::SAMPLING) {
- mPhaseIntervalSetting = Phase::ZERO;
- mVSyncSource->setDuration(0ns, 0ns);
- stopVsyncListenerLocked();
- lock.unlock();
- mRegionSamplingThread.notifySamplingOffset();
- return;
- }
- }
-
- RegionSamplingThread& mRegionSamplingThread;
- const std::chrono::nanoseconds mTargetSamplingWorkDuration;
- mutable std::mutex mMutex;
- enum class Phase {
- ZERO,
- SAMPLING
- } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
- = Phase::ZERO;
- bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
- std::unique_ptr<VSyncSource> mVSyncSource;
-};
-
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
- const TimingTunables& tunables)
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables)
: mFlinger(flinger),
- mScheduler(scheduler),
mTunables(tunables),
mIdleTimer(
"RegSampIdle",
std::chrono::duration_cast<std::chrono::milliseconds>(
mTunables.mSamplingTimerTimeout),
[] {}, [this] { checkForStaleLuma(); }),
- mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
- tunables.mSamplingDuration)),
- lastSampleTime(0ns) {
+ mLastSampleTime(0ns) {
mThread = std::thread([this]() { threadMain(); });
pthread_setname_np(mThread.native_handle(), "RegionSampling");
mIdleTimer.start();
}
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
- : RegionSamplingThread(flinger, scheduler,
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger)
+ : RegionSamplingThread(flinger,
TimingTunables{defaultRegionSamplingWorkDuration,
defaultRegionSamplingPeriod,
defaultRegionSamplingTimerTimeout}) {}
@@ -224,48 +147,46 @@ void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& lis
void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mThreadControlMutex);
- if (mDiscardedFrames > 0) {
- ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
- mDiscardedFrames = 0;
- mPhaseCallback->startVsyncListener();
+ if (mSampleRequestTime.has_value()) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ mSampleRequestTime.reset();
+ mFlinger.scheduleRegionSamplingThread();
}
}
-void RegionSamplingThread::notifyNewContent() {
- doSample();
-}
-
-void RegionSamplingThread::notifySamplingOffset() {
- doSample();
+void RegionSamplingThread::onCompositionComplete(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
+ doSample(samplingDeadline);
}
-void RegionSamplingThread::doSample() {
+void RegionSamplingThread::doSample(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
std::lock_guard lock(mThreadControlMutex);
- auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
- if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+ const auto now = std::chrono::steady_clock::now();
+ if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
+ // content changed, but we sampled not too long ago, so we need to sample some time in the
+ // future.
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
- if (mDiscardedFrames == 0) mDiscardedFrames++;
+ mSampleRequestTime = now;
return;
}
- if (mDiscardedFrames < maxRegionSamplingSkips) {
+ if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) {
// If there is relatively little time left for surfaceflinger
// until the next vsync deadline, defer this sampling work
// to a later frame, when hopefully there will be more time.
- const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
- if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+ if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
- mDiscardedFrames++;
+ mSampleRequestTime = mSampleRequestTime.value_or(now);
return;
}
}
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
- mDiscardedFrames = 0;
- lastSampleTime = now;
+ mSampleRequestTime.reset();
+ mLastSampleTime = now;
mIdleTimer.reset();
- mPhaseCallback->stopVsyncListener();
mSampleRequested = true;
mCondition.notify_one();
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 86632db490..2231853fcb 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -63,9 +63,8 @@ public:
struct EnvironmentTimingTunables : TimingTunables {
EnvironmentTimingTunables();
};
- explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
- const TimingTunables& tunables);
- explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger);
~RegionSamplingThread();
@@ -76,12 +75,11 @@ public:
// Remove the listener to stop receiving median luma notifications.
void removeListener(const sp<IRegionSamplingListener>& listener);
- // Notifies sampling engine that new content is available. This will trigger a sampling
- // pass at some point in the future.
- void notifyNewContent();
-
- // Notifies the sampling engine that it has a good timing window in which to sample.
- void notifySamplingOffset();
+ // Notifies sampling engine that composition is done and new content is
+ // available, and the deadline for the sampling work on the main thread to
+ // be completed without eating the budget of another frame.
+ void onCompositionComplete(
+ std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
private:
struct Descriptor {
@@ -99,7 +97,7 @@ private:
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
- void doSample();
+ void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
void binderDied(const wp<IBinder>& who) override;
void checkForStaleLuma();
@@ -107,20 +105,18 @@ private:
void threadMain();
SurfaceFlinger& mFlinger;
- Scheduler& mScheduler;
const TimingTunables mTunables;
scheduler::OneShotTimer mIdleTimer;
- std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
-
std::thread mThread;
std::mutex mThreadControlMutex;
std::condition_variable_any mCondition;
bool mRunning GUARDED_BY(mThreadControlMutex) = true;
bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
- uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
- std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
+ std::optional<std::chrono::steady_clock::time_point> mSampleRequestTime
+ GUARDED_BY(mThreadControlMutex);
+ std::chrono::steady_clock::time_point mLastSampleTime GUARDED_BY(mThreadControlMutex);
std::mutex mSamplingMutex;
std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 7ff0ddfab4..4d51125156 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -23,7 +23,6 @@
#include <utils/threads.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include "EventThread.h"
#include "FrameTimeline.h"
@@ -33,27 +32,32 @@
namespace android::impl {
void MessageQueue::Handler::dispatchRefresh() {
- if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+ if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
}
}
void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
- if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
mVsyncId = vsyncId;
mExpectedVSyncTime = expectedVSyncTimestamp;
mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
}
}
+bool MessageQueue::Handler::invalidatePending() {
+ constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
+ return (mEventMask.load() & pendingMask) != 0;
+}
+
void MessageQueue::Handler::handleMessage(const Message& message) {
switch (message.what) {
case INVALIDATE:
- android_atomic_and(~eventMaskInvalidate, &mEventMask);
+ mEventMask.fetch_and(~eventMaskInvalidate);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
case REFRESH:
- android_atomic_and(~eventMaskRefresh, &mEventMask);
+ mEventMask.fetch_and(~eventMaskRefresh);
mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
}
@@ -106,7 +110,7 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
- mVsync.mScheduled = false;
+ mVsync.scheduled = false;
}
mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime}),
@@ -131,9 +135,10 @@ void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
- if (mVsync.mScheduled) {
- mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
- mVsync.lastCallbackTime.count()});
+ if (mVsync.scheduled) {
+ mVsync.expectedWakeupTime = mVsync.registration->schedule(
+ {mVsync.workDuration.get().count(),
+ /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
}
}
@@ -176,10 +181,11 @@ void MessageQueue::invalidate() {
}
std::lock_guard lock(mVsync.mutex);
- mVsync.mScheduled = true;
- mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
- .readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.count()});
+ mVsync.scheduled = true;
+ mVsync.expectedWakeupTime =
+ mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ .readyDuration = 0,
+ .earliestVsync = mVsync.lastCallbackTime.count()});
}
void MessageQueue::refresh() {
@@ -200,4 +206,19 @@ void MessageQueue::injectorCallback() {
}
}
+std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
+ if (mHandler->invalidatePending()) {
+ return std::chrono::steady_clock::now();
+ }
+
+ std::lock_guard lock(mVsync.mutex);
+ if (mVsync.scheduled) {
+ LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
+ const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
+ return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+ }
+
+ return std::nullopt;
+}
+
} // namespace android::impl
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 2934af047c..58ce9b9a2f 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -72,6 +72,7 @@ public:
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void invalidate() = 0;
virtual void refresh() = 0;
+ virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
};
// ---------------------------------------------------------------------------
@@ -81,9 +82,13 @@ namespace impl {
class MessageQueue : public android::MessageQueue {
protected:
class Handler : public MessageHandler {
- enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
+ enum : uint32_t {
+ eventMaskInvalidate = 0x1,
+ eventMaskRefresh = 0x2,
+ eventMaskTransaction = 0x4
+ };
MessageQueue& mQueue;
- int32_t mEventMask;
+ std::atomic<uint32_t> mEventMask;
std::atomic<int64_t> mVsyncId;
std::atomic<nsecs_t> mExpectedVSyncTime;
@@ -92,6 +97,7 @@ protected:
void handleMessage(const Message& message) override;
virtual void dispatchRefresh();
virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+ virtual bool invalidatePending();
};
friend class Handler;
@@ -107,7 +113,8 @@ protected:
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
- bool mScheduled GUARDED_BY(mutex) = false;
+ bool scheduled GUARDED_BY(mutex) = false;
+ std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
@@ -141,6 +148,8 @@ public:
// sends REFRESH message at next VSYNC
void refresh() override;
+
+ std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
};
} // namespace impl
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 267dd6d9d0..41aacc0ab7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1909,6 +1909,7 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT
mRefreshPending = true;
onMessageRefresh();
}
+ notifyRegionSamplingThread();
}
bool SurfaceFlinger::handleMessageTransaction() {
@@ -2311,10 +2312,6 @@ void SurfaceFlinger::postComposition() {
}
}
- if (mLumaSampling && mRegionSamplingThread) {
- mRegionSamplingThread->notifyNewContent();
- }
-
// Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
if (ATRACE_ENABLED()) {
@@ -3067,8 +3064,7 @@ void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) {
configs.late.sfWorkDuration);
mRegionSamplingThread =
- new RegionSamplingThread(*this, *mScheduler,
- RegionSamplingThread::EnvironmentTimingTunables());
+ new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
@@ -6752,6 +6748,19 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle, bo
return layer;
}
+
+void SurfaceFlinger::scheduleRegionSamplingThread() {
+ static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
+}
+
+void SurfaceFlinger::notifyRegionSamplingThread() {
+ if (!mLumaSampling || !mRegionSamplingThread) {
+ return;
+ }
+
+ mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index cd20bd1c50..ea40cb510e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1429,6 +1429,9 @@ private:
REQUIRES(mStateLock);
std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
+
+ void scheduleRegionSamplingThread();
+ void notifyRegionSamplingThread();
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 18ebbab09a..dbd51fed78 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -104,8 +104,12 @@ TEST_F(MessageQueueTest, invalidate) {
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+ EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
}
TEST_F(MessageQueueTest, invalidateTwice) {
@@ -114,11 +118,15 @@ TEST_F(MessageQueueTest, invalidateTwice) {
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
}
TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
@@ -127,8 +135,10 @@ TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
const auto startTime = 100;
const auto endTime = startTime + mDuration.count();
@@ -141,6 +151,8 @@ TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+ EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 453c93a083..0e7b320787 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -39,6 +39,7 @@ public:
void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds));
MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
+ MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
};
} // namespace android::mock