From 32a88b18e1e3a162e58be39f4d1a129605f76c9e Mon Sep 17 00:00:00 2001 From: ramindani Date: Wed, 31 Jan 2024 18:45:30 -0800 Subject: [SF] Send NotifyExpectedPresentHint at the transaction time BUG: 314199179 BUG: 315989692 BUG: 284845445 Test: atest NotifyExpectedPresentTest Change-Id: Ief7fff69e8b92f0affd228b2bdcdc5009806f905 --- services/surfaceflinger/SurfaceFlinger.cpp | 64 +++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 10 deletions(-) (limited to 'services/surfaceflinger/SurfaceFlinger.cpp') diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index edba50bde6..0436c3c7f6 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2759,7 +2759,11 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( // TODO(b/255601557) Update frameInterval per display refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime()); - refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); + const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult(); + const auto scheduledFrameTimeOpt = scheduledFrameResultOpt + ? std::optional{scheduledFrameResultOpt->callbackTime} + : std::nullopt; + refreshArgs.scheduledFrameTime = scheduledFrameTimeOpt; refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; // Store the present time just before calling to the composition engine so we could notify // the scheduler. @@ -4258,7 +4262,8 @@ void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId auto hintStatus = data.hintStatus.load(); if (!expectedPresentWithinTimeout) { - if (hintStatus != NotifyExpectedPresentHintStatus::Sent || + if ((hintStatus != NotifyExpectedPresentHintStatus::Sent && + hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnTx) || (timeoutOpt && timeoutOpt->ns() == 0)) { // Send the hint immediately if timeout, as the hint gets // delayed otherwise, as the frame is scheduled close @@ -4267,11 +4272,16 @@ void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId .compare_exchange_strong(hintStatus, NotifyExpectedPresentHintStatus::ScheduleOnTx)) { scheduleNotifyExpectedPresentHint(displayId); + return; } - return; } } + if (hintStatus == NotifyExpectedPresentHintStatus::Sent && + data.hintStatus.compare_exchange_strong(hintStatus, + NotifyExpectedPresentHintStatus::ScheduleOnTx)) { + return; + } if (hintStatus != NotifyExpectedPresentHintStatus::Start) { return; } @@ -4279,7 +4289,8 @@ void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId mScheduler->scheduleFrame(); } -void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) { +void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId, + VsyncId vsyncId) { auto itr = mNotifyExpectedPresentMap.find(displayId); if (itr == mNotifyExpectedPresentMap.end()) { return; @@ -4288,10 +4299,30 @@ void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId display const char* const whence = __func__; const auto sendHint = [=, this]() { auto& data = mNotifyExpectedPresentMap.at(displayId); - data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent); - const auto status = - getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp, - data.lastFrameInterval); + TimePoint expectedPresentTime = data.lastExpectedPresentTimestamp; + if (ftl::to_underlying(vsyncId) != FrameTimelineInfo::INVALID_VSYNC_ID) { + const auto predictionOpt = mFrameTimeline->getTokenManager()->getPredictionsForToken( + ftl::to_underlying(vsyncId)); + const auto expectedPresentTimeOnPredictor = TimePoint::fromNs( + predictionOpt ? predictionOpt->presentTime : expectedPresentTime.ns()); + const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult(); + const auto expectedPresentTimeOnScheduler = scheduledFrameResultOpt.has_value() + ? scheduledFrameResultOpt->vsyncTime + : TimePoint::fromNs(0); + expectedPresentTime = + std::max(expectedPresentTimeOnPredictor, expectedPresentTimeOnScheduler); + } + + if (expectedPresentTime < TimePoint::now()) { + expectedPresentTime = + mScheduler->getVsyncSchedule()->vsyncDeadlineAfter(TimePoint::now()); + if (mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration > + mScheduler->getVsyncSchedule(displayId)->period()) { + expectedPresentTime += mScheduler->getVsyncSchedule(displayId)->period(); + } + } + const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime, + data.lastFrameInterval); if (status != NO_ERROR) { ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence, displayId.value); @@ -4309,7 +4340,11 @@ void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId display } })); } - sendHint(); + auto scheduleHintOnPresent = NotifyExpectedPresentHintStatus::ScheduleOnPresent; + if (itr->second.hintStatus.compare_exchange_strong(scheduleHintOnPresent, + NotifyExpectedPresentHintStatus::Sent)) { + sendHint(); + } } void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { @@ -5112,6 +5147,11 @@ status_t SurfaceFlinger::setTransactionState( const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone; mTransactionHandler.queueTransaction(std::move(state)); + for (const auto& [displayId, data] : mNotifyExpectedPresentMap) { + if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) { + scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId}); + } + } setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint); return NO_ERROR; } @@ -8820,7 +8860,11 @@ void SurfaceFlinger::sample() { return; } - mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime()); + const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult(); + const auto scheduleFrameTimeOpt = scheduledFrameResultOpt + ? std::optional{scheduledFrameResultOpt->callbackTime} + : std::nullopt; + mRegionSamplingThread->onCompositionComplete(scheduleFrameTimeOpt); } void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) { -- cgit v1.2.3-59-g8ed1b From 4babfc4ee6d27318d79c1c628d16c07b0b6cd0b5 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Fri, 16 Feb 2024 12:28:40 -0500 Subject: SF: Remove ConnectionHandle concept in Scheduler This abstraction never made sense, and only incurs pointless locking and hash map lookups. This is a step toward replacing the two EventThreads with a single EventDispatcher. Start the Scheduler's timers after initialization. This solves the same bug as Ib2617b914145bc4180cc7ca27203c59dbd625c94, which avoided crashes without fixing the root cause of the race. In a few cases, initScheduler indirectly calls Scheduler functions that attempt EventThread dispatch before EventThread creation, so skip those EventThread accesses using hasEventThreads for now. Further refactoring will untangle circular dependencies and remove hasEventThreads. Bug: 241285191 Bug: 241285945 Test: presubmit Change-Id: I89b93b47540ab24bb5675edc79370f38bc48114f --- services/surfaceflinger/Scheduler/Scheduler.cpp | 161 ++++++--------------- services/surfaceflinger/Scheduler/Scheduler.h | 96 +++++------- services/surfaceflinger/SurfaceFlinger.cpp | 68 ++++----- services/surfaceflinger/SurfaceFlinger.h | 5 - .../tests/unittests/SchedulerTest.cpp | 86 ++--------- .../tests/unittests/TestableScheduler.h | 16 +- .../tests/unittests/TestableSurfaceFlinger.h | 6 +- 7 files changed, 134 insertions(+), 304 deletions(-) (limited to 'services/surfaceflinger/SurfaceFlinger.cpp') diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 6979f0399b..e6c58a304e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -60,14 +60,6 @@ #include "VsyncController.h" #include "VsyncSchedule.h" -#define RETURN_IF_INVALID_HANDLE(handle, ...) \ - do { \ - if (mConnections.count(handle) == 0) { \ - ALOGE("Invalid connection handle %" PRIuPTR, handle.id); \ - return __VA_ARGS__; \ - } \ - } while (false) - namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, @@ -344,40 +336,26 @@ void Scheduler::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { } } -ConnectionHandle Scheduler::createEventThread(Cycle cycle, - frametimeline::TokenManager* tokenManager, - std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration) { +void Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* tokenManager, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) { auto eventThread = std::make_unique(cycle == Cycle::Render ? "app" : "appSf", getVsyncSchedule(), tokenManager, *this, workDuration, readyDuration); - auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle; - handle = createConnection(std::move(eventThread)); - return handle; -} - -ConnectionHandle Scheduler::createConnection(std::unique_ptr eventThread) { - const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++}; - ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id); - - auto connection = eventThread->createEventConnection(); - - std::lock_guard lock(mConnectionsLock); - mConnections.emplace(handle, Connection{connection, std::move(eventThread)}); - return handle; + if (cycle == Cycle::Render) { + mRenderEventThread = std::move(eventThread); + mRenderEventConnection = mRenderEventThread->createEventConnection(); + } else { + mLastCompositeEventThread = std::move(eventThread); + mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); + } } sp Scheduler::createDisplayEventConnection( - ConnectionHandle handle, EventRegistrationFlags eventRegistration, - const sp& layerHandle) { - const auto connection = [&]() -> sp { - std::scoped_lock lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle, nullptr); - - return mConnections[handle].thread->createEventConnection(eventRegistration); - }(); + Cycle cycle, EventRegistrationFlags eventRegistration, const sp& layerHandle) { + const auto connection = eventThreadFor(cycle).createEventConnection(eventRegistration); const auto layerId = static_cast(LayerHandle::getLayerId(layerHandle)); if (layerId != static_cast(UNASSIGNED_LAYER_ID)) { @@ -397,85 +375,48 @@ sp Scheduler::createDisplayEventConnection( return connection; } -sp Scheduler::getEventConnection(ConnectionHandle handle) { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle, nullptr); - return mConnections[handle].connection; -} - -void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId, - bool connected) { - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); +void Scheduler::onHotplugReceived(Cycle cycle, PhysicalDisplayId displayId, bool connected) { + if (hasEventThreads()) { + eventThreadFor(cycle).onHotplugReceived(displayId, connected); } - - thread->onHotplugReceived(displayId, connected); } -void Scheduler::onHotplugConnectionError(ConnectionHandle handle, int32_t errorCode) { - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); +void Scheduler::onHotplugConnectionError(Cycle cycle, int32_t errorCode) { + if (hasEventThreads()) { + eventThreadFor(cycle).onHotplugConnectionError(errorCode); } - - thread->onHotplugConnectionError(errorCode); } void Scheduler::enableSyntheticVsync(bool enable) { - // TODO(b/241285945): Remove connection handles. - const ConnectionHandle handle = mAppConnectionHandle; - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); - } - thread->enableSyntheticVsync(enable); + eventThreadFor(Cycle::Render).enableSyntheticVsync(enable); } -void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { +void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) { const bool supportsFrameRateOverrideByContent = pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent(); std::vector overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); - } - thread->onFrameRateOverridesChanged(displayId, std::move(overrides)); + eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides)); } -void Scheduler::onHdcpLevelsChanged(ConnectionHandle handle, PhysicalDisplayId displayId, +void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) { - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); - } - thread->onHdcpLevelsChanged(displayId, connectedLevel, maxLevel); + eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel); } -void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { +void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) { { std::lock_guard lock(mPolicyLock); // Cache the last reported modes for primary display. - mPolicy.cachedModeChangedParams = {handle, mode}; + mPolicy.cachedModeChangedParams = {cycle, mode}; // Invalidate content based refresh rate selection so it could be calculated // again for the new refresh rate. mPolicy.contentRequirements.clear(); } - onNonPrimaryDisplayModeChanged(handle, mode); + onNonPrimaryDisplayModeChanged(cycle, mode); } void Scheduler::dispatchCachedReportedMode() { @@ -502,39 +443,25 @@ void Scheduler::dispatchCachedReportedMode() { } mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt; - onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle, + onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle, mPolicy.cachedModeChangedParams->mode); } -void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); +void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) { + if (hasEventThreads()) { + eventThreadFor(cycle).onModeChanged(mode); } - thread->onModeChanged(mode); } -void Scheduler::dump(ConnectionHandle handle, std::string& result) const { - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections.at(handle).thread.get(); - } - thread->dump(result); +void Scheduler::dump(Cycle cycle, std::string& result) const { + eventThreadFor(cycle).dump(result); } -void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration, +void Scheduler::setDuration(Cycle cycle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { - android::EventThread* thread; - { - std::lock_guard lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); + if (hasEventThreads()) { + eventThreadFor(cycle).setDuration(workDuration, readyDuration); } - thread->setDuration(workDuration, readyDuration); } void Scheduler::updatePhaseConfiguration(Fps refreshRate) { @@ -557,10 +484,10 @@ void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powe } void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) { - setDuration(mAppConnectionHandle, + setDuration(Cycle::Render, /* workDuration */ config.appWorkDuration, /* readyDuration */ config.sfWorkDuration); - setDuration(mSfConnectionHandle, + setDuration(Cycle::LastComposite, /* workDuration */ vsyncPeriod, /* readyDuration */ config.sfWorkDuration); setDuration(config.sfWorkDuration); @@ -1027,16 +954,10 @@ std::shared_ptr Scheduler::promotePacesetterDisplayLocked( void Scheduler::applyNewVsyncSchedule(std::shared_ptr vsyncSchedule) { onNewVsyncSchedule(vsyncSchedule->getDispatch()); - std::vector threads; - { - std::lock_guard lock(mConnectionsLock); - threads.reserve(mConnections.size()); - for (auto& [_, connection] : mConnections) { - threads.push_back(connection.thread.get()); - } - } - for (auto* thread : threads) { - thread->onNewVsyncSchedule(vsyncSchedule); + + if (hasEventThreads()) { + eventThreadFor(Cycle::Render).onNewVsyncSchedule(vsyncSchedule); + eventThreadFor(Cycle::LastComposite).onNewVsyncSchedule(vsyncSchedule); } } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 9f29e9f696..71df507a00 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -56,35 +56,6 @@ #include -namespace android::scheduler { - -// Opaque handle to scheduler connection. -struct ConnectionHandle { - using Id = std::uintptr_t; - static constexpr Id INVALID_ID = static_cast(-1); - - Id id = INVALID_ID; - - explicit operator bool() const { return id != INVALID_ID; } -}; - -inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) { - return lhs.id == rhs.id; -} - -} // namespace android::scheduler - -namespace std { - -template <> -struct hash { - size_t operator()(android::scheduler::ConnectionHandle handle) const { - return hash()(handle.id); - } -}; - -} // namespace std - namespace android { class FenceTime; @@ -106,6 +77,11 @@ class RefreshRateStats; class VsyncConfiguration; class VsyncSchedule; +enum class Cycle { + Render, // Surface rendering. + LastComposite // Ahead of display compositing by one refresh period. +}; + class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { using Impl = android::impl::MessageQueue; @@ -154,36 +130,32 @@ public: return std::move(future); } - enum class Cycle { - Render, // Surface rendering. - LastComposite // Ahead of display compositing by one refresh period. - }; - - ConnectionHandle createEventThread(Cycle, frametimeline::TokenManager*, - std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration); + void createEventThread(Cycle, frametimeline::TokenManager*, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration); sp createDisplayEventConnection( - ConnectionHandle, EventRegistrationFlags eventRegistration = {}, + Cycle, EventRegistrationFlags eventRegistration = {}, const sp& layerHandle = nullptr) EXCLUDES(mChoreographerLock); - sp getEventConnection(ConnectionHandle); + const sp& getEventConnection(Cycle cycle) const { + return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection; + } - void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onHotplugConnectionError(ConnectionHandle, int32_t errorCode); + void onHotplugReceived(Cycle, PhysicalDisplayId, bool connected); + void onHotplugConnectionError(Cycle, int32_t errorCode); - void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock); - void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&); + void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock); + void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&); void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); - void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId) - EXCLUDES(mConnectionsLock); + void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId); - void onHdcpLevelsChanged(ConnectionHandle, PhysicalDisplayId, int32_t, int32_t); + void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t); // Modifies work duration in the event thread. - void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, + void setDuration(Cycle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); VsyncModulator& vsyncModulator() { return *mVsyncModulator; } @@ -288,7 +260,7 @@ public: bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const; void dump(utils::Dumper&) const; - void dump(ConnectionHandle, std::string&) const; + void dump(Cycle, std::string&) const; void dumpVsync(std::string&) const EXCLUDES(mDisplayLock); // Returns the preferred refresh rate and frame rate for the pacesetter display. @@ -369,8 +341,15 @@ private: void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override REQUIRES(kMainThreadContext, mDisplayLock); - // Create a connection on the given EventThread. - ConnectionHandle createConnection(std::unique_ptr); + // Used to skip event dispatch before EventThread creation during boot. + // TODO: b/241285191 - Reorder Scheduler initialization to avoid this. + bool hasEventThreads() const { + return CC_LIKELY(mRenderEventThread && mLastCompositeEventThread); + } + + EventThread& eventThreadFor(Cycle cycle) const { + return *(cycle == Cycle::Render ? mRenderEventThread : mLastCompositeEventThread); + } // Update feature state machine to given state when corresponding timer resets or expires. void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock); @@ -460,18 +439,11 @@ private: void resync() override EXCLUDES(mDisplayLock); void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); - // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. - struct Connection { - sp connection; - std::unique_ptr thread; - }; - - ConnectionHandle::Id mNextConnectionHandleId = 0; - mutable std::mutex mConnectionsLock; - std::unordered_map mConnections GUARDED_BY(mConnectionsLock); + std::unique_ptr mRenderEventThread; + sp mRenderEventConnection; - ConnectionHandle mAppConnectionHandle; - ConnectionHandle mSfConnectionHandle; + std::unique_ptr mLastCompositeEventThread; + sp mLastCompositeEventConnection; std::atomic mLastResyncTime = 0; @@ -585,7 +557,7 @@ private: ftl::Optional modeOpt; struct ModeChangedParams { - ConnectionHandle handle; + Cycle cycle; FrameRateMode mode; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index edba50bde6..4344494e38 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2092,12 +2092,11 @@ status_t SurfaceFlinger::getDisplayDecorationSupport( sp SurfaceFlinger::createDisplayEventConnection( gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration, const sp& layerHandle) { - const auto& handle = - vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger - ? mSfConnectionHandle - : mAppConnectionHandle; + const auto cycle = vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger + ? scheduler::Cycle::LastComposite + : scheduler::Cycle::Render; - return mScheduler->createDisplayEventConnection(handle, eventRegistration, layerHandle); + return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle); } void SurfaceFlinger::scheduleCommit(FrameHint hint) { @@ -2139,7 +2138,7 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t const int32_t hotplugErrorCode = static_cast(-timestamp); ALOGD("SurfaceFlinger got hotplugErrorCode=%d for display %" PRIu64, hotplugErrorCode, hwcDisplayId); - mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode); + mScheduler->onHotplugConnectionError(scheduler::Cycle::Render, hotplugErrorCode); return; } @@ -2190,7 +2189,7 @@ void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, if (FlagManager::getInstance().hotplug2()) { ALOGD("SurfaceFlinger got hotplug event=%d", static_cast(event)); // TODO(b/311403559): use enum type instead of int - mScheduler->onHotplugConnectionError(mAppConnectionHandle, static_cast(event)); + mScheduler->onHotplugConnectionError(scheduler::Cycle::Render, static_cast(event)); } } @@ -3480,7 +3479,7 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, if (!activeMode) { ALOGE("Failed to hotplug display %s", to_string(displayId).c_str()); if (FlagManager::getInstance().hotplug2()) { - mScheduler->onHotplugConnectionError(mAppConnectionHandle, + mScheduler->onHotplugConnectionError(scheduler::Cycle::Render, static_cast( DisplayHotplugEvent::ERROR_UNKNOWN)); } @@ -3533,8 +3532,8 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, } void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) { - mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected); - mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected); + mScheduler->onHotplugReceived(scheduler::Cycle::Render, displayId, connected); + mScheduler->onHotplugReceived(scheduler::Cycle::LastComposite, displayId, connected); } void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId, @@ -3544,7 +3543,7 @@ void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId, ? &scheduler::Scheduler::onPrimaryDisplayModeChanged : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged; - ((*mScheduler).*onDisplayModeChanged)(mAppConnectionHandle, mode); + ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode); } sp SurfaceFlinger::setupNewDisplayDeviceInternal( @@ -4196,7 +4195,7 @@ void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { return getDefaultDisplayDeviceLocked()->getPhysicalId(); }(); - mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); + mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); } void SurfaceFlinger::notifyCpuLoadUp() { @@ -4361,24 +4360,22 @@ void SurfaceFlinger::initScheduler(const sp& display) { mScheduler = std::make_unique(static_cast(*this), static_cast(*this), features, getFactory(), activeRefreshRate, *mTimeStats); + + // The pacesetter must be registered before EventThread creation below. mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); if (FlagManager::getInstance().vrr_config()) { mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps); } - mScheduler->startTimers(); const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs(); - mAppConnectionHandle = - mScheduler->createEventThread(Scheduler::Cycle::Render, - mFrameTimeline->getTokenManager(), - /* workDuration */ configs.late.appWorkDuration, - /* readyDuration */ configs.late.sfWorkDuration); - mSfConnectionHandle = - mScheduler->createEventThread(Scheduler::Cycle::LastComposite, - mFrameTimeline->getTokenManager(), - /* workDuration */ activeRefreshRate.getPeriod(), - /* readyDuration */ configs.late.sfWorkDuration); + mScheduler->createEventThread(scheduler::Cycle::Render, mFrameTimeline->getTokenManager(), + /* workDuration */ configs.late.appWorkDuration, + /* readyDuration */ configs.late.sfWorkDuration); + mScheduler->createEventThread(scheduler::Cycle::LastComposite, + mFrameTimeline->getTokenManager(), + /* workDuration */ activeRefreshRate.getPeriod(), + /* readyDuration */ configs.late.sfWorkDuration); mScheduler->initVsync(*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); @@ -4386,6 +4383,9 @@ void SurfaceFlinger::initScheduler(const sp& display) { sp::make(*this, RegionSamplingThread::EnvironmentTimingTunables()); mFpsReporter = sp::make(*mFrameTimeline); + + // Timer callbacks may fire, so do this last. + mScheduler->startTimers(); } void SurfaceFlinger::doCommitTransactions() { @@ -6322,7 +6322,7 @@ void SurfaceFlinger::dumpScheduler(std::string& result) const { } void SurfaceFlinger::dumpEvents(std::string& result) const { - mScheduler->dump(mAppConnectionHandle, result); + mScheduler->dump(scheduler::Cycle::Render, result); } void SurfaceFlinger::dumpVsync(std::string& result) const { @@ -7094,14 +7094,15 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mForceFullDamage = n != 0; return NO_ERROR; } - case 1018: { // Modify Choreographer's duration + case 1018: { // Set the render deadline as a duration until VSYNC. n = data.readInt32(); - mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns); + mScheduler->setDuration(scheduler::Cycle::Render, std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } - case 1019: { // Modify SurfaceFlinger's duration + case 1019: { // Set the deadline of the last composite as a duration until VSYNC. n = data.readInt32(); - mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns); + mScheduler->setDuration(scheduler::Cycle::LastComposite, + std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } case 1020: { // Unused @@ -7333,7 +7334,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r auto inUid = static_cast(data.readInt32()); const auto refreshRate = data.readFloat(); mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate}); - mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); + mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); return NO_ERROR; } // Toggle caching feature @@ -8473,10 +8474,10 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might // be depending in this callback. if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) { - mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); + mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode); toggleKernelIdleTimer(); } else { - mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode); + mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode); } auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode); @@ -8664,7 +8665,7 @@ status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate }(); mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast(uid), frameRate}); - mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); + mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId); return NO_ERROR; } @@ -8926,7 +8927,8 @@ void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t con Mutex::Autolock lock(mStateLock); display->setSecure(connectedLevel >= 2 /* HDCP_V1 */); } - mScheduler->onHdcpLevelsChanged(mAppConnectionHandle, displayId, connectedLevel, maxLevel); + mScheduler->onHdcpLevelsChanged(scheduler::Cycle::Render, displayId, connectedLevel, + maxLevel); })); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index be057979f9..b01d1aa0e8 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1356,12 +1356,7 @@ private: const std::string mHwcServiceName; - /* - * Scheduler - */ std::unique_ptr mScheduler; - scheduler::ConnectionHandle mAppConnectionHandle; - scheduler::ConnectionHandle mSfConnectionHandle; scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index b0595257a9..10e2220ece 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -99,7 +99,6 @@ protected: TestableScheduler* mScheduler = new TestableScheduler{mSelector, mFlinger, mSchedulerCallback}; surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; - ConnectionHandle mConnectionHandle; MockEventThread* mEventThread; sp mEventThreadConnection; }; @@ -116,54 +115,13 @@ SchedulerTest::SchedulerTest() { EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillRepeatedly(Return(mEventThreadConnection)); - mConnectionHandle = mScheduler->createConnection(std::move(eventThread)); - EXPECT_TRUE(mConnectionHandle); + mScheduler->setEventThread(Cycle::Render, std::move(eventThread)); mFlinger.resetScheduler(mScheduler); } } // namespace -TEST_F(SchedulerTest, invalidConnectionHandle) { - ConnectionHandle handle; - - const sp connection = mScheduler->createDisplayEventConnection(handle); - - EXPECT_FALSE(connection); - EXPECT_FALSE(mScheduler->getEventConnection(handle)); - - // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. - EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); - mScheduler->onHotplugReceived(handle, kDisplayId1, false); - - std::string output; - EXPECT_CALL(*mEventThread, dump(_)).Times(0); - mScheduler->dump(handle, output); - EXPECT_TRUE(output.empty()); - - EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0); - mScheduler->setDuration(handle, 10ns, 20ns); -} - -TEST_F(SchedulerTest, validConnectionHandle) { - const sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle); - - ASSERT_EQ(mEventThreadConnection, connection); - EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle)); - - EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1); - mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false); - - std::string output("dump"); - EXPECT_CALL(*mEventThread, dump(output)).Times(1); - mScheduler->dump(mConnectionHandle, output); - EXPECT_FALSE(output.empty()); - - EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1); - mScheduler->setDuration(mConnectionHandle, 10ns, 20ns); -} - TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) { // Hardware VSYNC should not change if the display is already registered. EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId1, false)).Times(0); @@ -235,22 +193,6 @@ TEST_F(SchedulerTest, dispatchCachedReportedMode) { EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode()); } -TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) { - const auto mode = DisplayMode::Builder(hal::HWConfigId(0)) - .setId(DisplayModeId(111)) - .setPhysicalDisplayId(kDisplayId1) - .setVsyncPeriod(111111) - .build(); - - // If the handle is incorrect, the function should return before - // onModeChange is called. - ConnectionHandle invalidHandle = {.id = 123}; - EXPECT_NO_FATAL_FAILURE( - mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, - {90_Hz, ftl::as_non_null(mode)})); - EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0); -} - TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms)); EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms)); @@ -753,7 +695,7 @@ TEST_F(AttachedChoreographerTest, registerSingle) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); const sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size()); ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence())); @@ -782,9 +724,9 @@ TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) { .WillOnce(Return(mockConnection2)); const sp connection1 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, handle); const sp connection2 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, handle); EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size()); ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence())); @@ -802,9 +744,9 @@ TEST_F(AttachedChoreographerTest, registerMultipleOnDifferentLayers) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2); const sp connection1 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer1->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer1->getHandle()); const sp connection2 = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer2->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer2->getHandle()); EXPECT_EQ(2u, mScheduler->mutableAttachedChoreographers().size()); @@ -831,7 +773,7 @@ TEST_F(AttachedChoreographerTest, removedWhenConnectionIsGone) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence())); EXPECT_EQ(1u, @@ -861,7 +803,7 @@ TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); const sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); layer.clear(); mFlinger.mutableLayersPendingRemoval().clear(); @@ -875,7 +817,7 @@ void AttachedChoreographerTest::frameRateTestScenario(Fps layerFps, int8_t frame EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); RequestedLayerState layerState(LayerCreationArgs(layer->getSequence())); LayerHierarchy hierarchy(&layerState); @@ -935,7 +877,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateParent) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -962,7 +904,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateParent2Children) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -997,7 +939,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateParentConflictingChildren) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -1031,7 +973,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateChild) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); @@ -1057,7 +999,7 @@ TEST_F(AttachedChoreographerTest, setsFrameRateChildNotOverriddenByParent) { EXPECT_CALL(mSchedulerCallback, onChoreographerAttached); sp connection = - mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle()); + mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); RequestedLayerState parentState(LayerCreationArgs(parent->getSequence())); LayerHierarchy parentHierarchy(&parentState); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 25a85dfa20..1472ebf009 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -71,9 +71,14 @@ public: Scheduler::onFrameSignal(compositor, vsyncId, TimePoint()); } - // Used to inject mock event thread. - ConnectionHandle createConnection(std::unique_ptr eventThread) { - return Scheduler::createConnection(std::move(eventThread)); + void setEventThread(Cycle cycle, std::unique_ptr eventThreadPtr) { + if (cycle == Cycle::Render) { + mRenderEventThread = std::move(eventThreadPtr); + mRenderEventConnection = mRenderEventThread->createEventConnection(); + } else { + mLastCompositeEventThread = std::move(eventThreadPtr); + mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); + } } auto refreshRateSelector() { return pacesetterSelectorPtr(); } @@ -124,7 +129,6 @@ public: using Scheduler::resyncAllToHardwareVsync; - auto& mutableAppConnectionHandle() { return mAppConnectionHandle; } auto& mutableLayerHistory() { return mLayerHistory; } auto& mutableAttachedChoreographers() { return mAttachedChoreographers; } @@ -180,10 +184,6 @@ public: mPolicy.cachedModeChangedParams.reset(); } - void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) { - Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); - } - void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) { auto schedule = getVsyncSchedule(id); std::lock_guard lock(schedule->mHwVsyncLock); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 46a079cfa1..bce7729d80 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -258,11 +258,9 @@ public: mScheduler->initVsync(*mTokenManager, 0ms); - mScheduler->mutableAppConnectionHandle() = - mScheduler->createConnection(std::move(appEventThread)); + mScheduler->setEventThread(scheduler::Cycle::Render, std::move(appEventThread)); + mScheduler->setEventThread(scheduler::Cycle::LastComposite, std::move(sfEventThread)); - mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle(); - mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); resetScheduler(mScheduler); } -- cgit v1.2.3-59-g8ed1b