SurfaceFlinger: throttle applications based on uid Add the ability for SurfaceFlinger to be able to throttle down to a divider of the refresh rate (i.e. for 30/45 for 90Hz) Change-Id: I6bfd6f43ee1f30e771a136c558d8ae9a6d7fbe0f Test: Manually via 1039 SF backdoor Bug: 170502573 Bug: 169270763 Bug: 169271059
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index d302f98..fa75ffa 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp
@@ -393,6 +393,15 @@ nsecs_t expectedPresentTime) { ATRACE_CALL(); + // If this is not a valid vsync for the layer's uid, return and try again later + const bool isVsyncValidForUid = + mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid); + if (!isVsyncValidForUid) { + ATRACE_NAME("!isVsyncValidForUid"); + mFlinger->setTransactionFlags(eTraversalNeeded); + return false; + } + bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); if (refreshRequired) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index bf2a509..bf5be47 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -31,6 +31,8 @@ #include <android-base/stringprintf.h> +#include <binder/IPCThreadState.h> + #include <cutils/compiler.h> #include <cutils/sched_policy.h> @@ -123,11 +125,12 @@ } // namespace -EventThreadConnection::EventThreadConnection(EventThread* eventThread, +EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) : resyncCallback(std::move(resyncCallback)), mConfigChanged(configChanged), + mOwnerUid(callingUid), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} @@ -170,10 +173,12 @@ EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource, android::frametimeline::TokenManager* tokenManager, - InterceptVSyncsCallback interceptVSyncsCallback) + InterceptVSyncsCallback interceptVSyncsCallback, + ThrottleVsyncCallback throttleVsyncCallback) : mVSyncSource(std::move(vsyncSource)), mTokenManager(tokenManager), mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)), + mThrottleVsyncCallback(std::move(throttleVsyncCallback)), mThreadName(mVSyncSource->getName()) { mVSyncSource->setCallback(this); @@ -216,8 +221,9 @@ sp<EventThreadConnection> EventThread::createEventConnection( ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const { - return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback), - configChanged); + return new EventThreadConnection(const_cast<EventThread*>(this), + IPCThreadState::self()->getCallingUid(), + std::move(resyncCallback), configChanged); } status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) { @@ -443,6 +449,11 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, const sp<EventThreadConnection>& connection) const { + const auto throttleVsync = [&] { + return mThrottleVsyncCallback && + mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid); + }; + switch (event.header.type) { case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: return true; @@ -458,12 +469,22 @@ case VSyncRequest::SingleSuppressCallback: connection->vsyncRequest = VSyncRequest::None; return false; - case VSyncRequest::Single: + case VSyncRequest::Single: { + if (throttleVsync()) { + return false; + } connection->vsyncRequest = VSyncRequest::SingleSuppressCallback; return true; + } case VSyncRequest::Periodic: + if (throttleVsync()) { + return false; + } return true; default: + // We don't throttle vsync if the app set a vsync request rate + // since there is no easy way to do that and this is a very + // rare case return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0; }
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index e42ca05..2e2d989 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,7 +81,7 @@ class EventThreadConnection : public BnDisplayEventConnection { public: - EventThreadConnection(EventThread*, ResyncCallback, + EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback, ISurfaceComposer::ConfigChanged configChanged); virtual ~EventThreadConnection(); @@ -98,6 +98,8 @@ const ISurfaceComposer::ConfigChanged mConfigChanged = ISurfaceComposer::ConfigChanged::eConfigChangedSuppress; + const uid_t mOwnerUid; + private: virtual void onFirstRef(); EventThread* const mEventThread; @@ -143,9 +145,10 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { public: using InterceptVSyncsCallback = std::function<void(nsecs_t)>; + using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>; - EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, - InterceptVSyncsCallback); + EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback, + ThrottleVsyncCallback); ~EventThread(); sp<EventThreadConnection> createEventConnection( @@ -196,6 +199,7 @@ frametimeline::TokenManager* const mTokenManager; const InterceptVSyncsCallback mInterceptVSyncsCallback; + const ThrottleVsyncCallback mThrottleVsyncCallback; const char* const mThreadName; std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 7ab49a9..7d97e72 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -625,4 +625,36 @@ return RefreshRateConfigs::KernelIdleTimerAction::TurnOn; } +void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) { + if (refreshRateHz > 0 && refreshRateHz < 1) { + return; + } + + std::lock_guard lock(mLock); + if (refreshRateHz != 0) { + mPreferredRefreshRateForUid[uid] = refreshRateHz; + } else { + mPreferredRefreshRateForUid.erase(uid); + } +} + +int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const { + constexpr float kThreshold = 0.1f; + std::lock_guard lock(mLock); + + const auto iter = mPreferredRefreshRateForUid.find(uid); + if (iter == mPreferredRefreshRateForUid.end()) { + return 1; + } + + const auto refreshRateHz = iter->second; + const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz; + const auto numPeriodsRounded = std::round(numPeriods); + if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) { + return 1; + } + + return static_cast<int>(numPeriods); +} + } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 280ed62..5cf7d07 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -311,6 +311,13 @@ // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; + // Stores the preferred refresh rate that an app should run at. + // refreshRate == 0 means no preference. + void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock); + + // Returns a divider for the current refresh rate + int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock); + private: friend class RefreshRateConfigsTest; @@ -368,6 +375,8 @@ Policy mDisplayManagerPolicy GUARDED_BY(mLock); std::optional<Policy> mOverridePolicy GUARDED_BY(mLock); + std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock); + // The min and max refresh rates supported by the device. // This will not change at runtime. const RefreshRate* mMinSupportedRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 7b8448f..a14019e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -212,13 +212,26 @@ readyDuration, traceVsync, name); } +bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const { + const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid); + if (divider <= 1) { + return true; + } + + return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider); +} + Scheduler::ConnectionHandle Scheduler::createConnection( const char* connectionName, frametimeline::TokenManager* tokenManager, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, impl::EventThread::InterceptVSyncsCallback interceptCallback) { auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration); + auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) { + return !isVsyncValid(expectedVsyncTimestamp, uid); + }; auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager, - std::move(interceptCallback)); + std::move(interceptCallback), + std::move(throttleVsync)); return createConnection(std::move(eventThread)); } @@ -379,7 +392,8 @@ auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), /*tokenManager=*/nullptr, - impl::EventThread::InterceptVSyncsCallback()); + impl::EventThread::InterceptVSyncsCallback(), + impl::EventThread::ThrottleVsyncCallback()); mInjectorConnectionHandle = createConnection(std::move(eventThread)); }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 47ce4a4..4c86d26 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -139,6 +139,10 @@ scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; } + // Returns true if a given vsync timestamp is considered valid vsync + // for a given uid + bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const; + void dump(std::string&) const; void dump(ConnectionHandle, std::string&) const; void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index e90edf7..75d1e6f 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -27,6 +27,9 @@ #include <chrono> #include <sstream> +#undef LOG_TAG +#define LOG_TAG "VSyncPredictor" + namespace android::scheduler { using base::StringAppendF; @@ -66,7 +69,7 @@ nsecs_t VSyncPredictor::currentPeriod() const { std::lock_guard lock(mMutex); - return std::get<0>(mRateMap.find(mIdealPeriod)->second); + return mRateMap.find(mIdealPeriod)->second.slope; } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { @@ -118,7 +121,7 @@ // normalizing to the oldest timestamp cuts down on error in calculating the intercept. auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end()); auto it = mRateMap.find(mIdealPeriod); - auto const currentPeriod = std::get<0>(it->second); + auto const currentPeriod = it->second.slope; // TODO (b/144707443): its important that there's some precision in the mean of the ordinals // for the intercept calculation, so scale the ordinals by 1000 to continue // fixed point calculation. Explore expanding @@ -172,10 +175,8 @@ return true; } -nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { - std::lock_guard lock(mMutex); - - auto const [slope, intercept] = getVSyncPredictionModel(lock); +nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const { + auto const [slope, intercept] = getVSyncPredictionModelLocked(); if (mTimestamps.empty()) { traceInt64If("VSP-mode", 1); @@ -210,13 +211,71 @@ return prediction; } -std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const { +nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { std::lock_guard lock(mMutex); - return VSyncPredictor::getVSyncPredictionModel(lock); + return nextAnticipatedVSyncTimeFromLocked(timePoint); } -std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel( - std::lock_guard<std::mutex> const&) const { +/* + * Returns whether a given vsync timestamp is in phase with a vsync divider. + * For example, if the vsync timestamps are (0,16,32,48): + * isVSyncInPhase(0, 2) = true + * isVSyncInPhase(16, 2) = false + * isVSyncInPhase(32, 2) = true + */ +bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const { + struct VsyncError { + nsecs_t vsyncTimestamp; + float error; + + bool operator<(const VsyncError& other) const { return error < other.error; } + }; + + std::lock_guard lock(mMutex); + if (divider <= 1) { + return true; + } + + const nsecs_t period = mRateMap[mIdealPeriod].slope; + const nsecs_t justBeforeTimePoint = timePoint - period / 2; + const nsecs_t dividedPeriod = mIdealPeriod / divider; + + // If this is the first time we have asked about this divider with the + // current vsync period, it is considered in phase and we store the closest + // vsync timestamp + const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod); + if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) { + const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint); + mRateDividerKnownTimestampMap[dividedPeriod] = vsync; + return true; + } + + // Find the next N vsync timestamp where N is the divider. + // One of these vsyncs will be in phase. We return the one which is + // the most aligned with the last known in phase vsync + std::vector<VsyncError> vsyncs(static_cast<size_t>(divider)); + const nsecs_t knownVsync = knownTimestampIter->second; + nsecs_t point = justBeforeTimePoint; + for (size_t i = 0; i < divider; i++) { + const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point); + const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider); + const auto error = std::abs(std::round(numPeriods) - numPeriods); + vsyncs[i] = {vsync, error}; + point = vsync + 1; + } + + const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end()); + mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp; + return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2; +} + +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { + std::lock_guard lock(mMutex); + const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); + return {model.slope, model.intercept}; +} + +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { return mRateMap.find(mIdealPeriod)->second; } @@ -269,8 +328,8 @@ for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) { StringAppendF(&result, "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n", - idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f, - std::get<1>(periodInterceptTuple)); + idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f, + periodInterceptTuple.intercept); } }
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 5f2ec49..381cf81 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,10 +38,10 @@ uint32_t outlierTolerancePercent); ~VSyncPredictor(); - bool addVsyncTimestamp(nsecs_t timestamp) final; - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final; - nsecs_t currentPeriod() const final; - void resetModel() final; + bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex); + nsecs_t currentPeriod() const final EXCLUDES(mMutex); + void resetModel() final EXCLUDES(mMutex); /* * Inform the model that the period is anticipated to change to a new value. @@ -50,16 +50,23 @@ * * \param [in] period The new period that should be used. */ - void setPeriod(nsecs_t period) final; + void setPeriod(nsecs_t period) final EXCLUDES(mMutex); /* Query if the model is in need of more samples to make a prediction. * \return True, if model would benefit from more samples, False if not. */ - bool needsMoreSamples() const final; + bool needsMoreSamples() const final EXCLUDES(mMutex); - std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const; + struct Model { + nsecs_t slope; + nsecs_t intercept; + }; - void dump(std::string& result) const final; + VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex); + + bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex); + + void dump(std::string& result) const final EXCLUDES(mMutex); private: VSyncPredictor(VSyncPredictor const&) = delete; @@ -76,13 +83,19 @@ std::mutex mutable mMutex; size_t next(size_t i) const REQUIRES(mMutex); bool validate(nsecs_t timestamp) const REQUIRES(mMutex); - std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const - REQUIRES(mMutex); + + Model getVSyncPredictionModelLocked() const REQUIRES(mMutex); + + nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); nsecs_t mIdealPeriod GUARDED_BY(mMutex); std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); - std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex); + // Map between ideal vsync period and the calculated model + std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex); + + // Map between the divided vsync period and the last known vsync timestamp + std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex); size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 107c540..2cd9b3d 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -68,6 +68,14 @@ virtual bool needsMoreSamples() const = 0; + /* + * Checks if a vsync timestamp is in phase for a given divider. + * + * \param [in] timePoint A vsync timestamp + * \param [in] divider The divider to check for + */ + virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0; + virtual void dump(std::string& result) const = 0; protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index fde38c9..1119fce 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4915,7 +4915,7 @@ } // Numbers from 1000 to 1038 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1038) { + if (code >= 1000 && code <= 1039) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -5280,6 +5280,13 @@ mFrameTimeline->setMaxDisplayFrames(n); return NO_ERROR; } + case 1039: { + // The first parameter is the uid + n = data.readInt32(); + const float refreshRateHz = data.readFloat(); + mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz); + } + return NO_ERROR; } } return err;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index a4f7449..0911712 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -134,13 +134,15 @@ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(eventThread.get(), ResyncCallback(), + new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index db05d5a..f0311bd 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -70,12 +70,14 @@ void DisplayTransactionTest::injectMockScheduler() { EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mSFEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index f680bdb..3aafd45 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -59,9 +59,11 @@ protected: class MockEventThreadConnection : public EventThreadConnection { public: - MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback, + MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid, + ResyncCallback&& resyncCallback, ISurfaceComposer::ConfigChanged configChanged) - : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {} + : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback), + configChanged) {} MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); }; @@ -73,7 +75,8 @@ void createThread(std::unique_ptr<VSyncSource>); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, - ISurfaceComposer::ConfigChanged configChanged); + ISurfaceComposer::ConfigChanged configChanged, + uid_t ownerUid = mConnectionUid); void expectVSyncSetEnabledCallReceived(bool expectedState); void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration, @@ -89,6 +92,7 @@ void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId, nsecs_t expectedVsyncPeriod); + void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t); AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; @@ -96,12 +100,18 @@ mVSyncSetDurationCallRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; + ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; MockVSyncSource* mVSyncSource; VSyncSource::Callback* mCallback = nullptr; std::unique_ptr<impl::EventThread> mThread; sp<MockEventThreadConnection> mConnection; + sp<MockEventThreadConnection> mThrottledConnection; + + static constexpr uid_t mConnectionUid = 443; + static constexpr uid_t mThrottledConnectionUid = 177; }; EventThreadTest::EventThreadTest() { @@ -124,6 +134,9 @@ createThread(std::move(vsyncSource)); mConnection = createConnection(mConnectionEventCallRecorder, ISurfaceComposer::eConfigChangedDispatch); + mThrottledConnection = + createConnection(mThrottledConnectionEventCallRecorder, + ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid); // A display must be connected for VSYNC events to be delivered. mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true); @@ -140,9 +153,15 @@ } void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { + const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) { + mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid); + return (uid == mThrottledConnectionUid); + }; + mThread = std::make_unique<impl::EventThread>(std::move(source), /*tokenManager=*/nullptr, - mInterceptVSyncCallRecorder.getInvocable()); + mInterceptVSyncCallRecorder.getInvocable(), + throttleVsync); // EventThread should register itself as VSyncSource callback. mCallback = expectVSyncSetCallbackCallReceived(); @@ -150,10 +169,11 @@ } sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( - ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) { + ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged, + uid_t ownerUid) { sp<MockEventThreadConnection> connection = - new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(), - configChanged); + new MockEventThreadConnection(mThread.get(), ownerUid, + mResyncCallRecorder.getInvocable(), configChanged); EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable())); return connection; } @@ -183,6 +203,13 @@ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); } +void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) { + auto args = mThrottleVsyncCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); + EXPECT_EQ(uid, std::get<1>(args.value())); +} + void EventThreadTest::expectVsyncEventReceivedByConnection( const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount) { @@ -267,13 +294,15 @@ // The interceptor should receive the event, as well as the connection. mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); + expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // Use the received callback to signal a second vsync event. - // The interceptor should receive the event, but the the connection should + // The interceptor should receive the event, but the connection should // not as it was only interested in the first. mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); // EventThread should also detect that at this point that it does not need @@ -323,16 +352,19 @@ // interceptor, and the connection. mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); + expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // A second event should go to the same places. mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); + expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); // A third event should go to the same places. mCallback->onVSyncEvent(789, 777, 111); expectInterceptCallReceived(789); + expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); } @@ -346,16 +378,19 @@ mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The second event will be seen by the interceptor and the connection. mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); expectVsyncEventReceivedByConnection(456, 2u); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The third event will be seen by the interceptor, and not the connection. mCallback->onVSyncEvent(789, 777, 744); expectInterceptCallReceived(789); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The fourth event will be seen by the interceptor and the connection. mCallback->onVSyncEvent(101112, 7847, 86); @@ -408,19 +443,19 @@ } TEST_F(EventThreadTest, tracksEventConnections) { - EXPECT_EQ(1, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(2, mThread->getEventThreadConnectionCount()); ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY}; sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder, ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, errorConnection); - EXPECT_EQ(2, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(3, mThread->getEventThreadConnectionCount()); ConnectionEventRecorder secondConnectionEventRecorder{0}; sp<MockEventThreadConnection> secondConnection = createConnection(secondConnectionEventRecorder, ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, secondConnection); - EXPECT_EQ(3, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(4, mThread->getEventThreadConnectionCount()); // EventThread should enable vsync callbacks. expectVSyncSetEnabledCallReceived(true); @@ -432,7 +467,7 @@ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123, 1u); - EXPECT_EQ(2, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(3, mThread->getEventThreadConnectionCount()); } TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { @@ -514,5 +549,35 @@ ASSERT_FALSE(args.has_value()); } +TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) { + // Signal that we want the next vsync event to be posted to the throttled connection + mThread->requestNextVsync(mThrottledConnection); + + // EventThread should immediately request a resync. + EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value()); + + // EventThread should enable vsync callbacks. + expectVSyncSetEnabledCallReceived(true); + + // Use the received callback to signal a first vsync event. + // The interceptor should receive the event, but not the connection. + mCallback->onVSyncEvent(123, 456, 789); + expectInterceptCallReceived(123); + expectThrottleVsyncReceived(456, mThrottledConnectionUid); + mThrottledConnectionEventCallRecorder.waitForUnexpectedCall(); + + // Use the received callback to signal a second vsync event. + // The interceptor should receive the event, but the connection should + // not as it was only interested in the first. + mCallback->onVSyncEvent(456, 123, 0); + expectInterceptCallReceived(456); + expectThrottleVsyncReceived(123, mThrottledConnectionUid); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should not change the vsync state as it didn't send the event + // yet + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); +} + } // namespace } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 1f6f166..4762fd4 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1471,6 +1471,34 @@ EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); } +TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, + /*currentConfigId=*/HWC_CONFIG_ID_30); + EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234)); +} + +TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, + /*currentConfigId=*/HWC_CONFIG_ID_30); + const uid_t uid = 1234; + refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30); + EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60); + EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72); + EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); + EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120); + EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid)); +} + } // namespace } // namespace scheduler } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp index 2c8178e..8cd8372 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -131,12 +131,14 @@ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index eee9400..509858a 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -42,7 +42,7 @@ class MockEventThreadConnection : public android::EventThreadConnection { public: explicit MockEventThreadConnection(EventThread* eventThread) - : EventThreadConnection(eventThread, ResyncCallback(), + : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress) {} ~MockEventThreadConnection() = default;
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index efee826..e25d501 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -171,12 +171,14 @@ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 760bf65..68cf330 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -66,13 +66,15 @@ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(eventThread.get(), ResyncCallback(), + new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 1e5139c..0af5f30 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,6 +52,7 @@ void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } + bool isVSyncInPhase(nsecs_t, int) const final { return false; } void dump(std::string&) const final {} private: @@ -88,6 +89,7 @@ void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } + bool isVSyncInPhase(nsecs_t, int) const final { return false; } void dump(std::string&) const final {} private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 69731fd..72b5396 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -48,6 +48,7 @@ MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index d4cd11d..3d60479 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -59,16 +59,16 @@ }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { - auto [slope, intercept] = tracker.getVSyncPredictionModel(); + auto model = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(mPeriod)); - EXPECT_THAT(intercept, Eq(0)); + EXPECT_THAT(model.slope, Eq(mPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); auto const changedPeriod = 2000; tracker.setPeriod(changedPeriod); - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(changedPeriod)); - EXPECT_THAT(intercept, Eq(0)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(changedPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); } TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) { @@ -264,17 +264,17 @@ } auto const mMaxRoundingError = 100; - auto [slope, intercept] = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError)); - EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError)); + auto model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError)); + EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError)); tracker.setPeriod(slowPeriod); for (auto const& timestamp : simulatedVsyncsSlow) { tracker.addVsyncTimestamp(timestamp); } - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError)); - EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError)); + EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError)); } TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { @@ -296,9 +296,9 @@ for (auto const& timestamp : simulatedVsyncsFast) { tracker.addVsyncTimestamp(timestamp); } - auto [slope, intercept] = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(fastPeriod)); - EXPECT_THAT(intercept, Eq(0)); + auto model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(fastPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); tracker.setPeriod(slowPeriod); for (auto const& timestamp : simulatedVsyncsSlow) { @@ -308,16 +308,16 @@ // we had a model for 100ns mPeriod before, use that until the new samples are // sufficiently built up tracker.setPeriod(idealPeriod); - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(fastPeriod)); - EXPECT_THAT(intercept, Eq(0)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(fastPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); for (auto const& timestamp : simulatedVsyncsFast2) { tracker.addVsyncTimestamp(timestamp); } - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(fastPeriod2)); - EXPECT_THAT(intercept, Eq(0)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(fastPeriod2)); + EXPECT_THAT(model.intercept, Eq(0)); } TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) { @@ -407,11 +407,9 @@ tracker.addVsyncTimestamp(i * realPeriod); } - EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()), - IsCloseTo(realPeriod, mMaxRoundingError)); + EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError)); tracker.resetModel(); - EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()), - IsCloseTo(idealPeriod, mMaxRoundingError)); + EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError)); } TEST_F(VSyncPredictorTest, slopeAlwaysValid) { @@ -450,6 +448,33 @@ EXPECT_THAT(intercept, Eq(0)); } +TEST_F(VSyncPredictorTest, isVSyncInPhase) { + auto last = mNow; + auto const bias = 10; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod - bias; + last = mNow; + tracker.addVsyncTimestamp(mNow); + mNow += bias; + } + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias)); + + const auto maxDivider = 5; + const auto maxPeriods = 15; + for (int divider = 1; divider < maxDivider; divider++) { + for (int i = 0; i < maxPeriods; i++) { + const bool expectedInPhase = (i % divider) == 0; + EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider)) + << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is " + << (expectedInPhase ? "not " : "") << "in phase for divider " << divider; + } + } +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 0dcaf26..a7568e4 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -42,6 +42,7 @@ MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); MOCK_CONST_METHOD1(dump, void(std::string&)); };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 03ddc85..de98025 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -33,6 +33,7 @@ MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); MOCK_CONST_METHOD1(dump, void(std::string&)); };