Consider GPU comp info for jank classification
We now have gpu composition information in FrameTimeline. This change
updates the jank classification to consider this case as well.
Bug: 169876734
Test: libsurfaceflinger_unittest
Change-Id: I7bae2f9db52318f717196cf6033a1d2bb3596830
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b1f5001..7468ac3 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -827,13 +827,11 @@
void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence,
- bool gpuComposition) {
+ const std::shared_ptr<FenceTime>& gpuFence) {
ATRACE_CALL();
std::scoped_lock lock(mMutex);
mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
- if (gpuComposition) {
- mCurrentDisplayFrame->setGpuComposition();
- }
+ mCurrentDisplayFrame->setGpuFence(gpuFence);
mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
flushPendingPresentFences();
finalizeCurrentDisplayFrame();
@@ -871,8 +869,8 @@
mSurfaceFlingerActuals.endTime = actualEndTime;
}
-void FrameTimeline::DisplayFrame::setGpuComposition() {
- mGpuComposition = true;
+void FrameTimeline::DisplayFrame::setGpuFence(const std::shared_ptr<FenceTime>& gpuFence) {
+ mGpuFence = gpuFence;
}
void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
@@ -889,7 +887,14 @@
// Delta between the expected present and the actual present
const nsecs_t presentDelta =
mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
- deadlineDelta = mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime;
+ // Sf actual end time represents the CPU end time. In case of HWC, SF's end time would have
+ // included the time for composition. However, for GPU composition, the final end time is max(sf
+ // end time, gpu fence time).
+ nsecs_t combinedEndTime = mSurfaceFlingerActuals.endTime;
+ if (mGpuFence != FenceTime::NO_FENCE) {
+ combinedEndTime = std::max(combinedEndTime, mGpuFence->getSignalTime());
+ }
+ deadlineDelta = combinedEndTime - mSurfaceFlingerPredictions.endTime;
// How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
// was a prediction error or not.
@@ -904,7 +909,7 @@
mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
}
- if (mSurfaceFlingerActuals.endTime > mSurfaceFlingerPredictions.endTime) {
+ if (combinedEndTime > mSurfaceFlingerPredictions.endTime) {
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
} else {
mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
@@ -961,7 +966,15 @@
mJankType = JankType::SurfaceFlingerScheduling;
} else {
// OnTime start, Finish late, Present late
- mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ if (mGpuFence != FenceTime::NO_FENCE &&
+ mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
+ mRefreshRate.getPeriodNsecs()) {
+ // If SF was in GPU composition and the CPU work finished before the vsync
+ // period, classify it as GPU deadline missed.
+ mJankType = JankType::SurfaceFlingerGpuDeadlineMissed;
+ } else {
+ mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ }
}
} else {
// Finish time unknown
@@ -1036,7 +1049,7 @@
actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
FrameReadyMetadata::OnTimeFinish);
- actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition);
+ actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE);
actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState));
});
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 373216b..41f4978 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -293,11 +293,12 @@
// the token and sets the actualSfWakeTime for the current DisplayFrame.
virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0;
- // Sets the sfPresentTime, gpuComposition and finalizes the current DisplayFrame. Tracks the
+ // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the
// given present fence until it's signaled, and updates the present timestamps of all presented
- // SurfaceFrames in that vsync.
+ // SurfaceFrames in that vsync. If a gpuFence was also provided, its tracked in the
+ // corresponding DisplayFrame.
virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
- bool gpuComposition) = 0;
+ const std::shared_ptr<FenceTime>& gpuFence) = 0;
// Args:
// -jank : Dumps only the Display Frames that are either janky themselves
@@ -376,7 +377,7 @@
void setPredictions(PredictionState predictionState, TimelineItem predictions);
void setActualStartTime(nsecs_t actualStartTime);
void setActualEndTime(nsecs_t actualEndTime);
- void setGpuComposition();
+ void setGpuFence(const std::shared_ptr<FenceTime>& gpuFence);
// BaseTime is the smallest timestamp in a DisplayFrame.
// Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -417,8 +418,8 @@
PredictionState mPredictionState = PredictionState::None;
// Bitmask for the type of jank
int32_t mJankType = JankType::None;
- // Indicates if this frame was composited by the GPU or not
- bool mGpuComposition = false;
+ // A valid gpu fence indicates that the DisplayFrame was composited by the GPU
+ std::shared_ptr<FenceTime> mGpuFence = FenceTime::NO_FENCE;
// Enum for the type of present
FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
// Enum for the type of finish
@@ -445,7 +446,7 @@
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
- bool gpuComposition = false) override;
+ const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
float computeFps(const std::unordered_set<int32_t>& layerIds) override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cbf8db8..41f211f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2173,7 +2173,7 @@
// 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(systemTime(), mPreviousPresentFences[0].fenceTime,
- glCompositionDoneFenceTime != FenceTime::NO_FENCE);
+ glCompositionDoneFenceTime);
nsecs_t dequeueReadyTime = systemTime();
for (const auto& layer : mLayersWithQueuedFrames) {
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index b3f47cc..7727052 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -505,6 +505,33 @@
mFrameTimeline->setSfPresent(62, presentFence1);
}
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) {
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::SurfaceFlingerGpuDeadlineMissed, 4, 10,
+ 0}));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne, /*isBuffer*/ true);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ gpuFence1->signalForTest(64);
+ presentFence1->signalForTest(70);
+
+ mFrameTimeline->setSfPresent(59, presentFence1, gpuFence1);
+}
+
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
Fps refreshRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
@@ -1456,24 +1483,47 @@
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+ /*
+ * Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed
+ * Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed
+ */
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ // case 1 - cpu time = 33 - 12 = 21, vsync period = 11
mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
- mFrameTimeline->setSfPresent(36, presentFence1);
- auto displayFrame = getDisplayFrame(0);
+ mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
+ auto displayFrame0 = getDisplayFrame(0);
+ gpuFence1->signalForTest(36);
presentFence1->signalForTest(52);
// Fences haven't been flushed yet, so it should be 0
- EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
+
+ // case 2 - cpu time = 56 - 52 = 4, vsync period = 11
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
+ auto displayFrame1 = getDisplayFrame(1);
+ gpuFence2->signalForTest(66);
+ presentFence2->signalForTest(71);
+
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ // Fences have flushed for first displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame0->getActuals().presentTime, 52);
+ EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
addEmptyDisplayFrame();
- displayFrame = getDisplayFrame(0);
- // Fences have flushed, so the present timestamps should be updated
- EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
- EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
- EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
- EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+ // Fences have flushed for second displayFrame, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 71);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 5707978..5dc48c3 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -32,7 +32,9 @@
MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
- MOCK_METHOD3(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&, bool));
+ MOCK_METHOD3(setSfPresent,
+ void(nsecs_t, const std::shared_ptr<FenceTime>&,
+ const std::shared_ptr<FenceTime>&));
MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
};