Loading libs/gui/include/gui/JankInfo.h +13 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ namespace android { // Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry. // Jank type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry. enum JankType { // No Jank None = 0x0, Loading Loading @@ -50,4 +50,16 @@ enum JankType { Dropped = 0x200, }; // Jank severity type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry. enum class JankSeverityType { // Unknown: not enough information to classify the severity of a jank Unknown = 0, // None: no jank None = 1, // Partial: jank caused by missing the deadline by less than the app's frame interval Partial = 2, // Full: jank caused by missing the deadline by more than the app's frame interval Full = 3, }; } // namespace android services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +41 −2 Original line number Diff line number Diff line Loading @@ -280,6 +280,19 @@ int32_t jankTypeBitmaskToProto(int32_t jankType) { return protoJank; } FrameTimelineEvent::JankSeverityType toProto(JankSeverityType jankSeverityType) { switch (jankSeverityType) { case JankSeverityType::Unknown: return FrameTimelineEvent::SEVERITY_UNKNOWN; case JankSeverityType::None: return FrameTimelineEvent::SEVERITY_NONE; case JankSeverityType::Partial: return FrameTimelineEvent::SEVERITY_PARTIAL; case JankSeverityType::Full: return FrameTimelineEvent::SEVERITY_FULL; } } // Returns the smallest timestamp from the set of predictions and actuals. nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, TimelineItem actuals) { Loading Loading @@ -389,6 +402,15 @@ std::optional<int32_t> SurfaceFrame::getJankType() const { return mJankType; } std::optional<JankSeverityType> SurfaceFrame::getJankSeverityType() const { std::scoped_lock lock(mMutex); if (mActuals.presentTime == 0) { // Frame hasn't been presented yet. return std::nullopt; } return mJankSeverityType; } nsecs_t SurfaceFrame::getBaseTime() const { std::scoped_lock lock(mMutex); return getMinTime(mPredictionState, mPredictions, mActuals); Loading Loading @@ -505,10 +527,11 @@ std::string SurfaceFrame::miniDump() const { } void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, nsecs_t& deadlineDelta) { Fps displayFrameRenderRate, nsecs_t& deadlineDelta) { if (mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) { // Cannot do any classification for invalid present time. mJankType = JankType::Unknown; mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = -1; return; } Loading @@ -519,6 +542,7 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r // reasonable app, so prediction expire would mean a huge scheduling delay. mJankType = mPresentState != PresentState::Presented ? JankType::Dropped : JankType::AppDeadlineMissed; mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = -1; return; } Loading @@ -543,6 +567,11 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; // Jank that is missing by less than the render rate period is classified as partial jank, // otherwise it is a full jank. mJankSeverityType = std::abs(presentDelta) < displayFrameRenderRate.getPeriodNsecs() ? JankSeverityType::Partial : JankSeverityType::Full; } else { mFramePresentMetadata = FramePresentMetadata::OnTimePresent; } Loading Loading @@ -613,6 +642,7 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r mJankType = JankType::Dropped; // Since frame was not presented, lets drop any present value mActuals.presentTime = 0; mJankSeverityType = JankSeverityType::Unknown; } } Loading @@ -625,7 +655,7 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, mActuals.presentTime = presentTime; nsecs_t deadlineDelta = 0; classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta); classifyJankLocked(displayFrameJankType, refreshRate, displayFrameRenderRate, deadlineDelta); if (mPredictionState != PredictionState::None) { // Only update janky frames if the app used vsync predictions Loading Loading @@ -718,6 +748,7 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffse actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState)); actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer); actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); // Actual timeline end Loading Loading @@ -910,6 +941,7 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& // Cannot do jank classification with expired predictions or invalid signal times. Set the // deltas to 0 as both negative and positive deltas are used as real values. mJankType = JankType::Unknown; mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = 0; deltaToVsync = 0; if (!presentTimeValid) { Loading Loading @@ -941,6 +973,11 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; // Jank that is missing by less than the render rate period is classified as partial jank, // otherwise it is a full jank. mJankSeverityType = std::abs(presentDelta) < mRenderRate.getPeriodNsecs() ? JankSeverityType::Partial : JankSeverityType::Full; } else { mFramePresentMetadata = FramePresentMetadata::OnTimePresent; } Loading Loading @@ -1119,6 +1156,7 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs actualDisplayFrameStartEvent->set_prediction_type(toProto(PredictionState::Valid)); actualDisplayFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(JankType::Dropped)); actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None)); }); // Actual timeline end Loading Loading @@ -1160,6 +1198,7 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState)); actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); // Actual timeline end Loading services/surfaceflinger/FrameTimeline/FrameTimeline.h +7 −1 Original line number Diff line number Diff line Loading @@ -168,6 +168,7 @@ public: // Returns std::nullopt if the frame hasn't been classified yet. // Used by both SF and FrameTimeline. std::optional<int32_t> getJankType() const; std::optional<JankSeverityType> getJankSeverityType() const; // Functions called by SF int64_t getToken() const { return mToken; }; Loading Loading @@ -232,7 +233,7 @@ private: void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, nsecs_t& deadlineDelta) REQUIRES(mMutex); Fps displayFrameRenderRate, nsecs_t& deadlineDelta) REQUIRES(mMutex); const int64_t mToken; const int32_t mInputEventId; Loading @@ -252,6 +253,8 @@ private: mutable std::mutex mMutex; // Bitmask for the type of jank int32_t mJankType GUARDED_BY(mMutex) = JankType::None; // Enum for the severity of jank JankSeverityType mJankSeverityType GUARDED_BY(mMutex) = JankSeverityType::None; // Indicates if this frame was composited by the GPU or not bool mGpuComposition GUARDED_BY(mMutex) = false; // Refresh rate for this frame. Loading Loading @@ -404,6 +407,7 @@ public: FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; }; FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; }; int32_t getJankType() const { return mJankType; } JankSeverityType getJankSeverityType() const { return mJankSeverityType; } const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const { return mSurfaceFrames; } Loading Loading @@ -435,6 +439,8 @@ public: PredictionState mPredictionState = PredictionState::None; // Bitmask for the type of jank int32_t mJankType = JankType::None; // Enum for the severity of jank JankSeverityType mJankSeverityType = JankSeverityType::None; // 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 Loading services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +67 −2 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/gui/include/gui/JankInfo.h +13 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ namespace android { // Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry. // Jank type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry. enum JankType { // No Jank None = 0x0, Loading Loading @@ -50,4 +50,16 @@ enum JankType { Dropped = 0x200, }; // Jank severity type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry. enum class JankSeverityType { // Unknown: not enough information to classify the severity of a jank Unknown = 0, // None: no jank None = 1, // Partial: jank caused by missing the deadline by less than the app's frame interval Partial = 2, // Full: jank caused by missing the deadline by more than the app's frame interval Full = 3, }; } // namespace android
services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +41 −2 Original line number Diff line number Diff line Loading @@ -280,6 +280,19 @@ int32_t jankTypeBitmaskToProto(int32_t jankType) { return protoJank; } FrameTimelineEvent::JankSeverityType toProto(JankSeverityType jankSeverityType) { switch (jankSeverityType) { case JankSeverityType::Unknown: return FrameTimelineEvent::SEVERITY_UNKNOWN; case JankSeverityType::None: return FrameTimelineEvent::SEVERITY_NONE; case JankSeverityType::Partial: return FrameTimelineEvent::SEVERITY_PARTIAL; case JankSeverityType::Full: return FrameTimelineEvent::SEVERITY_FULL; } } // Returns the smallest timestamp from the set of predictions and actuals. nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, TimelineItem actuals) { Loading Loading @@ -389,6 +402,15 @@ std::optional<int32_t> SurfaceFrame::getJankType() const { return mJankType; } std::optional<JankSeverityType> SurfaceFrame::getJankSeverityType() const { std::scoped_lock lock(mMutex); if (mActuals.presentTime == 0) { // Frame hasn't been presented yet. return std::nullopt; } return mJankSeverityType; } nsecs_t SurfaceFrame::getBaseTime() const { std::scoped_lock lock(mMutex); return getMinTime(mPredictionState, mPredictions, mActuals); Loading Loading @@ -505,10 +527,11 @@ std::string SurfaceFrame::miniDump() const { } void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, nsecs_t& deadlineDelta) { Fps displayFrameRenderRate, nsecs_t& deadlineDelta) { if (mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) { // Cannot do any classification for invalid present time. mJankType = JankType::Unknown; mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = -1; return; } Loading @@ -519,6 +542,7 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r // reasonable app, so prediction expire would mean a huge scheduling delay. mJankType = mPresentState != PresentState::Presented ? JankType::Dropped : JankType::AppDeadlineMissed; mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = -1; return; } Loading @@ -543,6 +567,11 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; // Jank that is missing by less than the render rate period is classified as partial jank, // otherwise it is a full jank. mJankSeverityType = std::abs(presentDelta) < displayFrameRenderRate.getPeriodNsecs() ? JankSeverityType::Partial : JankSeverityType::Full; } else { mFramePresentMetadata = FramePresentMetadata::OnTimePresent; } Loading Loading @@ -613,6 +642,7 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r mJankType = JankType::Dropped; // Since frame was not presented, lets drop any present value mActuals.presentTime = 0; mJankSeverityType = JankSeverityType::Unknown; } } Loading @@ -625,7 +655,7 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, mActuals.presentTime = presentTime; nsecs_t deadlineDelta = 0; classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta); classifyJankLocked(displayFrameJankType, refreshRate, displayFrameRenderRate, deadlineDelta); if (mPredictionState != PredictionState::None) { // Only update janky frames if the app used vsync predictions Loading Loading @@ -718,6 +748,7 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffse actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState)); actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer); actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); // Actual timeline end Loading Loading @@ -910,6 +941,7 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& // Cannot do jank classification with expired predictions or invalid signal times. Set the // deltas to 0 as both negative and positive deltas are used as real values. mJankType = JankType::Unknown; mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = 0; deltaToVsync = 0; if (!presentTimeValid) { Loading Loading @@ -941,6 +973,11 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; // Jank that is missing by less than the render rate period is classified as partial jank, // otherwise it is a full jank. mJankSeverityType = std::abs(presentDelta) < mRenderRate.getPeriodNsecs() ? JankSeverityType::Partial : JankSeverityType::Full; } else { mFramePresentMetadata = FramePresentMetadata::OnTimePresent; } Loading Loading @@ -1119,6 +1156,7 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs actualDisplayFrameStartEvent->set_prediction_type(toProto(PredictionState::Valid)); actualDisplayFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(JankType::Dropped)); actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None)); }); // Actual timeline end Loading Loading @@ -1160,6 +1198,7 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState)); actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); // Actual timeline end Loading
services/surfaceflinger/FrameTimeline/FrameTimeline.h +7 −1 Original line number Diff line number Diff line Loading @@ -168,6 +168,7 @@ public: // Returns std::nullopt if the frame hasn't been classified yet. // Used by both SF and FrameTimeline. std::optional<int32_t> getJankType() const; std::optional<JankSeverityType> getJankSeverityType() const; // Functions called by SF int64_t getToken() const { return mToken; }; Loading Loading @@ -232,7 +233,7 @@ private: void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, nsecs_t& deadlineDelta) REQUIRES(mMutex); Fps displayFrameRenderRate, nsecs_t& deadlineDelta) REQUIRES(mMutex); const int64_t mToken; const int32_t mInputEventId; Loading @@ -252,6 +253,8 @@ private: mutable std::mutex mMutex; // Bitmask for the type of jank int32_t mJankType GUARDED_BY(mMutex) = JankType::None; // Enum for the severity of jank JankSeverityType mJankSeverityType GUARDED_BY(mMutex) = JankSeverityType::None; // Indicates if this frame was composited by the GPU or not bool mGpuComposition GUARDED_BY(mMutex) = false; // Refresh rate for this frame. Loading Loading @@ -404,6 +407,7 @@ public: FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; }; FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; }; int32_t getJankType() const { return mJankType; } JankSeverityType getJankSeverityType() const { return mJankSeverityType; } const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const { return mSurfaceFrames; } Loading Loading @@ -435,6 +439,8 @@ public: PredictionState mPredictionState = PredictionState::None; // Bitmask for the type of jank int32_t mJankType = JankType::None; // Enum for the severity of jank JankSeverityType mJankSeverityType = JankSeverityType::None; // 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 Loading
services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +67 −2 File changed.Preview size limit exceeded, changes collapsed. Show changes