diff options
| author | 2024-11-14 00:14:04 +0000 | |
|---|---|---|
| committer | 2024-11-14 00:14:04 +0000 | |
| commit | 3774e13e75e9a2bb40852dcad595f9fc36dc59ff (patch) | |
| tree | 9eec7972359688aa02fc88350129674c73469bfe /services/surfaceflinger | |
| parent | 921762ca6916fdd3f2b413624a06b28bf0b981f0 (diff) | |
| parent | e4002e31f2017682ec7892eda9418b4d88bc12d0 (diff) | |
Snap for 12651823 from e4002e31f2017682ec7892eda9418b4d88bc12d0 to 25Q1-release
Change-Id: I36ec23de55b6b5afe6ac7427cafd3dfb3766beb3
Diffstat (limited to 'services/surfaceflinger')
| -rw-r--r-- | services/surfaceflinger/FrameTimeline/FrameTimeline.cpp | 14 | ||||
| -rw-r--r-- | services/surfaceflinger/FrameTimeline/FrameTimeline.h | 10 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.cpp | 27 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.h | 12 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/Scheduler.cpp | 5 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/Scheduler.h | 5 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 30 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 2 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/EventThreadTest.cpp | 69 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/mock/MockEventThread.h | 1 |
10 files changed, 171 insertions, 4 deletions
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index c13e444a99..86d7388f5b 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -29,6 +29,7 @@ #include <cinttypes> #include <numeric> #include <unordered_set> +#include <vector> #include "../Jank/JankTracker.h" @@ -1002,6 +1003,11 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, finalizeCurrentDisplayFrame(); } +const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames() + const { + return mPresentFrames; +} + void FrameTimeline::onCommitNotComposited() { SFTRACE_CALL(); std::scoped_lock lock(mMutex); @@ -1521,6 +1527,7 @@ void FrameTimeline::flushPendingPresentFences() { mPendingPresentFences.erase(mPendingPresentFences.begin()); } + mPresentFrames.clear(); for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; @@ -1533,6 +1540,13 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + + // Surface frames have been jank classified and can be provided to caller + // to detect if buffer stuffing is occurring. + for (const auto& frame : displayFrame->getSurfaceFrames()) { + mPresentFrames.push_back(frame); + } + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 6cda309440..a47bd573de 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -328,6 +328,11 @@ public: virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence) = 0; + // Provides surface frames that have already been jank classified in the most recent + // flush of pending present fences. This allows buffer stuffing detection from SF. + virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() + const = 0; + // Tells FrameTimeline that a frame was committed but not composited. This is used to flush // all the associated surface frames. virtual void onCommitNotComposited() = 0; @@ -505,6 +510,8 @@ public: void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override; + const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() + const override; void onCommitNotComposited() override; void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; @@ -552,6 +559,9 @@ private: // display frame, this is a good starting size for the vector so that we can avoid the // internal vector resizing that happens with push_back. static constexpr uint32_t kNumSurfaceFramesInitial = 10; + // Presented surface frames that have been jank classified and can + // indicate of potential buffer stuffing. + std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames; }; } // namespace impl diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 7729671401..fff4284199 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -44,10 +44,8 @@ #include <common/FlagManager.h> #include <scheduler/VsyncConfig.h> -#include "DisplayHardware/DisplayMode.h" #include "FrameTimeline.h" #include "VSyncDispatch.h" -#include "VSyncTracker.h" #include "EventThread.h" @@ -482,6 +480,14 @@ void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t conne mCondition.notify_all(); } +// Merge lists of buffer stuffed Uids +void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { + std::lock_guard<std::mutex> lock(mMutex); + for (auto& [uid, count] : bufferStuffedUids) { + mBufferStuffedUids.emplace_or_replace(uid, count); + } +} + void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -721,6 +727,10 @@ void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { + // List of Uids that have been sent vsync data with queued buffer count. + // Used to keep track of which Uids can be removed from the map of + // buffer stuffed clients. + ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers; for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { @@ -730,6 +740,13 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, event.vsync.vsyncData.preferredExpectedPresentationTime(), event.vsync.vsyncData.preferredDeadlineTimestamp()); } + auto it = mBufferStuffedUids.find(consumer->mOwnerUid); + if (it != mBufferStuffedUids.end()) { + copy.vsync.vsyncData.numberQueuedBuffers = it->second; + uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid); + } else { + copy.vsync.vsyncData.numberQueuedBuffers = 0; + } switch (consumer->postEvent(copy)) { case NO_ERROR: break; @@ -745,6 +762,12 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } + // The clients that have already received the queued buffer count + // can be removed from the buffer stuffed Uid list to avoid + // being sent duplicate messages. + for (auto uid : uidsPostedQueuedBuffers) { + mBufferStuffedUids.erase(uid); + } if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { mLastCommittedVsyncTime = diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 2daf126d77..95632c7e66 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -32,7 +32,6 @@ #include <thread> #include <vector> -#include "DisplayHardware/DisplayMode.h" #include "TracedOrdinal.h" #include "VSyncDispatch.h" #include "VsyncSchedule.h" @@ -55,6 +54,7 @@ using gui::VsyncEventData; // --------------------------------------------------------------------------- using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; +using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; enum class VSyncRequest { None = -2, @@ -136,6 +136,10 @@ public: virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) = 0; + + // An elevated number of queued buffers in the server is detected. This propagates a + // flag to Choreographer indicating that buffer stuffing recovery should begin. + virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); }; struct IEventThreadCallback { @@ -188,6 +192,8 @@ public: void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) override; + void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override; + private: friend EventThreadTest; @@ -228,6 +234,10 @@ private: scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; + // All consumers that need to recover from buffer stuffing and the number + // of their queued buffers. + BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex); + IEventThreadCallback& mCallback; std::thread mThread; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 274e121a79..8c587a9376 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -951,6 +951,11 @@ bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals, return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); } +void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { + if (!mRenderEventThread) return; + mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids)); +} + void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) { std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule; { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index e77af609e2..8340880918 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -42,7 +42,6 @@ #include <ui/DisplayId.h> #include <ui/DisplayMap.h> -#include "Display/DisplayModeRequest.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "ISchedulerCallback.h" @@ -334,6 +333,10 @@ public: mPacesetterFrameDurationFractionToSkip = frameDurationFraction; } + // Propagates a flag to the EventThread indicating that buffer stuffing + // recovery should begin. + void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); + private: friend class TestableScheduler; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 0d03a6cef3..9854174bbd 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -64,11 +64,13 @@ #include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> +#include <ftl/small_map.h> #include <ftl/unit.h> #include <gui/AidlUtil.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> #include <gui/IProducerListener.h> +#include <gui/JankInfo.h> #include <gui/LayerMetadata.h> #include <gui/LayerState.h> #include <gui/Surface.h> @@ -3072,12 +3074,40 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const TimePoint presentTime = TimePoint::now(); + // The Uids of layer owners that are in buffer stuffing mode, and their elevated + // buffer counts. Messages to start recovery are sent exclusively to these Uids. + BufferStuffingMap bufferStuffedUids; + // Set presentation information before calling Layer::releasePendingBuffer, such that jank // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime, pacesetterGpuCompositionDoneFenceTime); + // Find and register any layers that are in buffer stuffing mode + const auto& presentFrames = mFrameTimeline->getPresentFrames(); + + for (const auto& frame : presentFrames) { + const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId()); + if (!layer) continue; + uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0; + int32_t jankType = frame->getJankType().value_or(JankType::None); + if (jankType & JankType::BufferStuffing && + layer->flags & layer_state_t::eRecoverableFromBufferStuffing) { + auto [it, wasEmplaced] = + bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers); + // Update with maximum number of queued buffers, allows clients drawing + // multiple windows to account for the most severely stuffed window + if (!wasEmplaced && it->second < numberQueuedBuffers) { + it->second = numberQueuedBuffers; + } + } + } + + if (!bufferStuffedUids.empty()) { + mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids)); + } + // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might // be sampled a little later than when we started doing work for this frame, // but that should be okay since CompositorTiming has snapping logic. diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index d3479b7e52..7e9d5b881f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1370,6 +1370,8 @@ private: // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; + // Tracks the number of maximum queued buffers by layer owner Uid. + using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; BufferCountTracker mBufferCountTracker; std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 625d2e68d8..268a6c416d 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -23,6 +23,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <gui/DisplayEventReceiver.h> #include <log/log.h> #include <scheduler/VsyncConfig.h> #include <utils/Errors.h> @@ -111,6 +112,8 @@ protected: void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); + void expectQueuedBufferCountReceivedByConnection( + ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount); void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime, nsecs_t deadlineTimestamp) { @@ -144,6 +147,7 @@ protected: sp<MockEventThreadConnection> mConnection; sp<MockEventThreadConnection> mThrottledConnection; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; + std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders; std::chrono::nanoseconds mVsyncPeriod; @@ -376,6 +380,14 @@ void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection( EXPECT_EQ(expectedDisplayId, event.header.displayId); } +void EventThreadTest::expectQueuedBufferCountReceivedByConnection( + ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) { + auto args = connectionEventRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers); +} + namespace { using namespace testing; @@ -868,6 +880,63 @@ TEST_F(EventThreadTest, postHcpLevelsChanged) { EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); } +TEST_F(EventThreadTest, connectionReceivesBufferStuffing) { + setupEventThread(); + + // Create a connection that will experience buffer stuffing. + ConnectionEventRecorder stuffedConnectionEventRecorder{0}; + sp<MockEventThreadConnection> stuffedConnection = + createConnection(stuffedConnectionEventRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride, + 111); + + // Add a connection and buffer count to the list of stuffed Uids that will receive + // data in the next vsync event. + BufferStuffingMap bufferStuffedUids; + bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3); + mThread->addBufferStuffedUids(bufferStuffedUids); + mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder); + + // Signal that we want the next vsync event to be posted to two connections. + mThread->requestNextVsync(mConnection); + mThread->requestNextVsync(stuffedConnection); + onVSyncEvent(123, 456, 789); + + // Vsync event data contains number of queued buffers. + expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0); + expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3); +} + +TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) { + setupEventThread(); + + // Create a connection with the same Uid as another connection. + ConnectionEventRecorder secondConnectionEventRecorder{0}; + sp<MockEventThreadConnection> secondConnection = + createConnection(secondConnectionEventRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride, + mConnectionUid); + + // Add connection Uid and buffer count to the list of stuffed Uids that will receive + // data in the next vsync event. + BufferStuffingMap bufferStuffedUids; + bufferStuffedUids.try_emplace(mConnectionUid, 3); + mThread->addBufferStuffedUids(bufferStuffedUids); + mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder); + mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder); + + // Signal that we want the next vsync event to be posted to two connections. + mThread->requestNextVsync(mConnection); + mThread->requestNextVsync(secondConnection); + onVSyncEvent(123, 456, 789); + + // Vsync event data contains number of queued buffers. + expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3); + expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 82500fef10..c976697c08 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -53,6 +53,7 @@ public: MOCK_METHOD(void, onHdcpLevelsChanged, (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), (override)); + MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override)); }; } // namespace android::mock |