Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 36b01af6 authored by Adithya Srinivasan's avatar Adithya Srinivasan
Browse files

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
parent 82eef326
Loading
Loading
Loading
Loading
+23 −10
Original line number Diff line number Diff line
@@ -827,13 +827,11 @@ void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRa

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 @@ void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) {
    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 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t&
    // 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 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t&
        mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
    }

    if (mSurfaceFlingerActuals.endTime > mSurfaceFlingerPredictions.endTime) {
    if (combinedEndTime > mSurfaceFlingerPredictions.endTime) {
        mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
    } else {
        mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
@@ -961,8 +966,16 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t&
                    mJankType = JankType::SurfaceFlingerScheduling;
                } else {
                    // OnTime start, Finish late, Present late
                    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
                mJankType = JankType::Unknown;
@@ -1036,7 +1049,7 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const {
        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));
    });
+8 −7
Original line number Diff line number Diff line
@@ -293,11 +293,12 @@ public:
    // 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 @@ public:
        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 @@ public:
        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 @@ public:
    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;
+1 −1
Original line number Diff line number Diff line
@@ -2173,7 +2173,7 @@ void SurfaceFlinger::postComposition() {
    // 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) {
+59 −9
Original line number Diff line number Diff line
@@ -505,6 +505,33 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
    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_displayFrameLateFinishEarlyPresent)
}

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) {
+3 −1
Original line number Diff line number Diff line
@@ -32,7 +32,9 @@ public:
    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>&));
};