From 562c2718fcf8f465fe54a88c7ac948cd21a77ce5 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Fri, 7 May 2021 15:10:42 -0700 Subject: SF: region sampling scheduling optimization Schedule the region sampling thread more wisely by estimating when the next invalidate is going to happen. This makes the region sampling to be scheduled often at times where the main thread is idle, without eating the budget in an invalidate message. Bug: 181983990 Test: SF unit tests Test: observe systrace Change-Id: I1faca3aa7f882ed7c69e77e6a0877c10d57f0f1c --- services/surfaceflinger/RegionSamplingThread.cpp | 127 +++++------------------ 1 file changed, 24 insertions(+), 103 deletions(-) (limited to 'services/surfaceflinger/RegionSamplingThread.cpp') 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 inline std::string toNsString(std::chrono::duration 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 lock(mMutex); - - if (mPhaseIntervalSetting == Phase::ZERO) { - ATRACE_INT(lumaSamplingStepTag, static_cast(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 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( mTunables.mSamplingTimerTimeout), [] {}, [this] { checkForStaleLuma(); }), - mPhaseCallback(std::make_unique(*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& lis void RegionSamplingThread::checkForStaleLuma() { std::lock_guard lock(mThreadControlMutex); - if (mDiscardedFrames > 0) { - ATRACE_INT(lumaSamplingStepTag, static_cast(samplingStep::waitForZeroPhase)); - mDiscardedFrames = 0; - mPhaseCallback->startVsyncListener(); + if (mSampleRequestTime.has_value()) { + ATRACE_INT(lumaSamplingStepTag, static_cast(samplingStep::waitForSamplePhase)); + mSampleRequestTime.reset(); + mFlinger.scheduleRegionSamplingThread(); } } -void RegionSamplingThread::notifyNewContent() { - doSample(); -} - -void RegionSamplingThread::notifySamplingOffset() { - doSample(); +void RegionSamplingThread::onCompositionComplete( + std::optional samplingDeadline) { + doSample(samplingDeadline); } -void RegionSamplingThread::doSample() { +void RegionSamplingThread::doSample( + std::optional 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(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(samplingStep::waitForQuietFrame)); - mDiscardedFrames++; + mSampleRequestTime = mSampleRequestTime.value_or(now); return; } } ATRACE_INT(lumaSamplingStepTag, static_cast(samplingStep::sample)); - mDiscardedFrames = 0; - lastSampleTime = now; + mSampleRequestTime.reset(); + mLastSampleTime = now; mIdleTimer.reset(); - mPhaseCallback->stopVsyncListener(); mSampleRequested = true; mCondition.notify_one(); -- cgit v1.2.3-59-g8ed1b