Loading services/surfaceflinger/BufferQueueLayer.cpp +4 −2 Original line number Original line Diff line number Diff line Loading @@ -442,7 +442,8 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { } } auto surfaceFrame = auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId); mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName, mFrameTimelineVsyncId); surfaceFrame->setActualQueueTime(systemTime()); surfaceFrame->setActualQueueTime(systemTime()); mQueueItems.push_back({item, std::move(surfaceFrame)}); mQueueItems.push_back({item, std::move(surfaceFrame)}); Loading Loading @@ -480,7 +481,8 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { } } auto surfaceFrame = auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId); mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName, mFrameTimelineVsyncId); surfaceFrame->setActualQueueTime(systemTime()); surfaceFrame->setActualQueueTime(systemTime()); mQueueItems[mQueueItems.size() - 1].item = item; mQueueItems[mQueueItems.size() - 1].item = item; mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); Loading services/surfaceflinger/BufferStateLayer.cpp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -277,7 +277,7 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence const int32_t layerId = getSequence(); const int32_t layerId = getSequence(); mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(), mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(), postTime); mOwnerUid, postTime); desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; Loading services/surfaceflinger/FrameTimeline/Android.bp +2 −0 Original line number Original line Diff line number Diff line Loading @@ -5,10 +5,12 @@ cc_library_static { "FrameTimeline.cpp", "FrameTimeline.cpp", ], ], shared_libs: [ shared_libs: [ "android.hardware.graphics.composer@2.4", "libbase", "libbase", "libcutils", "libcutils", "liblog", "liblog", "libgui", "libgui", "libtimestats", "libui", "libui", "libutils", "libutils", ], ], Loading services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +62 −36 Original line number Original line Diff line number Diff line Loading @@ -93,19 +93,19 @@ std::string toString(PredictionState predictionState) { } } } } std::string toString(JankType jankType) { std::string toString(TimeStats::JankType jankType) { switch (jankType) { switch (jankType) { case JankType::None: case TimeStats::JankType::None: return "None"; return "None"; case JankType::Display: case TimeStats::JankType::Display: return "Composer/Display - outside SF and App"; return "Composer/Display - outside SF and App"; case JankType::SurfaceFlingerDeadlineMissed: case TimeStats::JankType::SurfaceFlingerDeadlineMissed: return "SurfaceFlinger Deadline Missed"; return "SurfaceFlinger Deadline Missed"; case JankType::AppDeadlineMissed: case TimeStats::JankType::AppDeadlineMissed: return "App Deadline Missed"; return "App Deadline Missed"; case JankType::PredictionExpired: case TimeStats::JankType::PredictionExpired: return "Prediction Expired"; return "Prediction Expired"; case JankType::SurfaceFlingerEarlyLatch: case TimeStats::JankType::SurfaceFlingerEarlyLatch: return "SurfaceFlinger Early Latch"; return "SurfaceFlinger Early Latch"; default: default: return "Unclassified"; return "Unclassified"; Loading Loading @@ -177,15 +177,18 @@ void TokenManager::flushTokens(nsecs_t flushTime) { } } } } SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState, SurfaceFrame::SurfaceFrame(uid_t ownerUid, std::string layerName, std::string debugName, PredictionState predictionState, frametimeline::TimelineItem&& predictions) frametimeline::TimelineItem&& predictions) : mLayerName(layerName), : mOwnerUid(ownerUid), mLayerName(std::move(layerName)), mDebugName(std::move(debugName)), mPresentState(PresentState::Unknown), mPresentState(PresentState::Unknown), mPredictionState(predictionState), mPredictionState(predictionState), mPredictions(predictions), mPredictions(predictions), mActuals({0, 0, 0}), mActuals({0, 0, 0}), mActualQueueTime(0), mActualQueueTime(0), mJankType(JankType::None), mJankType(TimeStats::JankType::None), mJankMetadata(0) {} mJankMetadata(0) {} void SurfaceFrame::setPresentState(PresentState state) { void SurfaceFrame::setPresentState(PresentState state) { Loading Loading @@ -227,17 +230,25 @@ void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) { mActuals.presentTime = presentTime; mActuals.presentTime = presentTime; } } void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) { void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); mJankType = jankType; mJankType = jankType; mJankMetadata = jankMetadata; mJankMetadata = jankMetadata; } } JankType SurfaceFrame::getJankType() const { TimeStats::JankType SurfaceFrame::getJankType() const { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); return mJankType; return mJankType; } } uid_t SurfaceFrame::getOwnerUid() const { return mOwnerUid; } const std::string& SurfaceFrame::getName() const { return mLayerName; } nsecs_t SurfaceFrame::getBaseTime() const { nsecs_t SurfaceFrame::getBaseTime() const { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); Loading Loading @@ -267,8 +278,8 @@ std::string presentStateToString(SurfaceFrame::PresentState presentState) { void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "Layer - %s", mLayerName.c_str()); StringAppendF(&result, "Layer - %s", mDebugName.c_str()); if (mJankType != JankType::None) { if (mJankType != TimeStats::JankType::None) { // Easily identify a janky Surface Frame in the dump // Easily identify a janky Surface Frame in the dump StringAppendF(&result, " [*] "); StringAppendF(&result, " [*] "); } } Loading @@ -285,33 +296,35 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); } } FrameTimeline::FrameTimeline() FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats) : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), mMaxDisplayFrames(kDefaultMaxDisplayFrames) {} mMaxDisplayFrames(kDefaultMaxDisplayFrames), mTimeStats(std::move(timeStats)) {} FrameTimeline::DisplayFrame::DisplayFrame() FrameTimeline::DisplayFrame::DisplayFrame() : surfaceFlingerPredictions(TimelineItem()), : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()), surfaceFlingerActuals(TimelineItem()), predictionState(PredictionState::None), predictionState(PredictionState::None), jankType(JankType::None), jankType(TimeStats::JankType::None), jankMetadata(0) { jankMetadata(0) { this->surfaceFrames.reserve(kNumSurfaceFramesInitial); this->surfaceFrames.reserve(kNumSurfaceFramesInitial); } } std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) { uid_t uid, std::string layerName, std::string debugName, std::optional<int64_t> token) { ATRACE_CALL(); ATRACE_CALL(); if (!token) { if (!token) { return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None, return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), TimelineItem()); PredictionState::None, TimelineItem()); } } std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); if (predictions) { if (predictions) { return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid, return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), PredictionState::Valid, std::move(*predictions)); std::move(*predictions)); } } return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired, return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), TimelineItem()); PredictionState::Expired, TimelineItem()); } } void FrameTimeline::addSurfaceFrame( void FrameTimeline::addSurfaceFrame( Loading Loading @@ -359,6 +372,7 @@ void FrameTimeline::flushPendingPresentFences() { } } } } if (signalTime != Fence::SIGNAL_TIME_INVALID) { if (signalTime != Fence::SIGNAL_TIME_INVALID) { int32_t totalJankReasons = TimeStats::JankType::None; auto& displayFrame = pendingPresentFence.second; auto& displayFrame = pendingPresentFence.second; displayFrame->surfaceFlingerActuals.presentTime = signalTime; displayFrame->surfaceFlingerActuals.presentTime = signalTime; Loading @@ -377,21 +391,26 @@ void FrameTimeline::flushPendingPresentFences() { displayFrame->jankMetadata |= EarlyFinish; displayFrame->jankMetadata |= EarlyFinish; } } if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) { if ((displayFrame->jankMetadata & EarlyFinish) && displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch; (displayFrame->jankMetadata & EarlyPresent)) { } else if (displayFrame->jankMetadata & LateFinish & LatePresent) { displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed; } else if ((displayFrame->jankMetadata & LateFinish) && (displayFrame->jankMetadata & LatePresent)) { displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed; } else if (displayFrame->jankMetadata & EarlyPresent || } else if (displayFrame->jankMetadata & EarlyPresent || displayFrame->jankMetadata & LatePresent) { displayFrame->jankMetadata & LatePresent) { // Cases where SF finished early but frame was presented late and vice versa // Cases where SF finished early but frame was presented late and vice versa displayFrame->jankType = JankType::Display; displayFrame->jankType = TimeStats::JankType::Display; } } } } if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { displayFrame->jankMetadata |= displayFrame->jankMetadata |= sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; } } totalJankReasons |= displayFrame->jankType; for (auto& surfaceFrame : displayFrame->surfaceFrames) { for (auto& surfaceFrame : displayFrame->surfaceFrames) { if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { // Only presented SurfaceFrames need to be updated // Only presented SurfaceFrames need to be updated Loading @@ -401,13 +420,13 @@ void FrameTimeline::flushPendingPresentFences() { const auto& predictionState = surfaceFrame->getPredictionState(); const auto& predictionState = surfaceFrame->getPredictionState(); if (predictionState == PredictionState::Expired) { if (predictionState == PredictionState::Expired) { // Jank analysis cannot be done on apps that don't use predictions // Jank analysis cannot be done on apps that don't use predictions surfaceFrame->setJankInfo(JankType::PredictionExpired, 0); surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0); continue; continue; } else if (predictionState == PredictionState::Valid) { } else if (predictionState == PredictionState::Valid) { const auto& actuals = surfaceFrame->getActuals(); const auto& actuals = surfaceFrame->getActuals(); const auto& predictions = surfaceFrame->getPredictions(); const auto& predictions = surfaceFrame->getPredictions(); int32_t jankMetadata = 0; int32_t jankMetadata = 0; JankType jankType = JankType::None; TimeStats::JankType jankType = TimeStats::JankType::None; if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish : EarlyFinish; : EarlyFinish; Loading @@ -419,19 +438,26 @@ void FrameTimeline::flushPendingPresentFences() { : EarlyPresent; : EarlyPresent; } } if (jankMetadata & EarlyPresent) { if (jankMetadata & EarlyPresent) { jankType = JankType::SurfaceFlingerEarlyLatch; jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; } else if (jankMetadata & LatePresent) { } else if (jankMetadata & LatePresent) { if (jankMetadata & EarlyFinish) { if (jankMetadata & EarlyFinish) { // TODO(b/169890654): Classify this properly // TODO(b/169890654): Classify this properly jankType = JankType::Display; jankType = TimeStats::JankType::Display; } else { } else { jankType = JankType::AppDeadlineMissed; jankType = TimeStats::JankType::AppDeadlineMissed; } } } } totalJankReasons |= jankType; mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(), surfaceFrame->getName(), jankType | displayFrame->jankType); surfaceFrame->setJankInfo(jankType, jankMetadata); surfaceFrame->setJankInfo(jankType, jankMetadata); } } } } } } mTimeStats->incrementJankyFrames(totalJankReasons); } } mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); Loading Loading @@ -467,7 +493,7 @@ nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& display void FrameTimeline::dumpDisplayFrame(std::string& result, void FrameTimeline::dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>& displayFrame, const std::shared_ptr<DisplayFrame>& displayFrame, nsecs_t baseTime) { nsecs_t baseTime) { if (displayFrame->jankType != JankType::None) { if (displayFrame->jankType != TimeStats::JankType::None) { // Easily identify a janky Display Frame in the dump // Easily identify a janky Display Frame in the dump StringAppendF(&result, " [*] "); StringAppendF(&result, " [*] "); } } Loading Loading @@ -501,11 +527,11 @@ void FrameTimeline::dumpJank(std::string& result) { nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); for (size_t i = 0; i < mDisplayFrames.size(); i++) { for (size_t i = 0; i < mDisplayFrames.size(); i++) { const auto& displayFrame = mDisplayFrames[i]; const auto& displayFrame = mDisplayFrames[i]; if (displayFrame->jankType == JankType::None) { if (displayFrame->jankType == TimeStats::JankType::None) { // Check if any Surface Frame has been janky // Check if any Surface Frame has been janky bool isJanky = false; bool isJanky = false; for (const auto& surfaceFrame : displayFrame->surfaceFrames) { for (const auto& surfaceFrame : displayFrame->surfaceFrames) { if (surfaceFrame->getJankType() != JankType::None) { if (surfaceFrame->getJankType() != TimeStats::JankType::None) { isJanky = true; isJanky = true; break; break; } } Loading services/surfaceflinger/FrameTimeline/FrameTimeline.h +21 −31 Original line number Original line Diff line number Diff line Loading @@ -16,9 +16,7 @@ #pragma once #pragma once #include <deque> #include <../TimeStats/TimeStats.h> #include <mutex> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposer.h> #include <ui/FenceTime.h> #include <ui/FenceTime.h> #include <utils/RefBase.h> #include <utils/RefBase.h> Loading @@ -26,26 +24,10 @@ #include <utils/Timers.h> #include <utils/Timers.h> #include <utils/Vector.h> #include <utils/Vector.h> namespace android::frametimeline { #include <deque> #include <mutex> /* namespace android::frametimeline { * The type of jank that is associated with a Display/Surface frame */ enum class JankType { // No Jank None, // Jank not related to SurfaceFlinger or the App Display, // SF took too long on the CPU SurfaceFlingerDeadlineMissed, // Either App or GPU took too long on the frame AppDeadlineMissed, // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame. PredictionExpired, // Latching a buffer early might cause an early present of the frame SurfaceFlingerEarlyLatch, }; enum JankMetadata { enum JankMetadata { // Frame was presented earlier than expected // Frame was presented earlier than expected Loading Loading @@ -147,8 +129,10 @@ public: // Create a new surface frame, set the predictions based on a token and return it to the caller. // Create a new surface frame, set the predictions based on a token and return it to the caller. // Sets the PredictionState of SurfaceFrame. // Sets the PredictionState of SurfaceFrame. // Debug name is the human-readable debugging string for dumpsys. virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) = 0; uid_t uid, std::string layerName, std::string debugName, std::optional<int64_t> token) = 0; // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // composited into one display frame. // composited into one display frame. Loading Loading @@ -206,8 +190,8 @@ private: class SurfaceFrame : public android::frametimeline::SurfaceFrame { class SurfaceFrame : public android::frametimeline::SurfaceFrame { public: public: SurfaceFrame(const std::string& layerName, PredictionState predictionState, SurfaceFrame(uid_t uid, std::string layerName, std::string debugName, TimelineItem&& predictions); PredictionState predictionState, TimelineItem&& predictions); ~SurfaceFrame() = default; ~SurfaceFrame() = default; TimelineItem getPredictions() const override { return mPredictions; }; TimelineItem getPredictions() const override { return mPredictions; }; Loading @@ -221,32 +205,37 @@ public: void setAcquireFenceTime(nsecs_t acquireFenceTime) override; void setAcquireFenceTime(nsecs_t acquireFenceTime) override; void setPresentState(PresentState state) override; void setPresentState(PresentState state) override; void setActualPresentTime(nsecs_t presentTime); void setActualPresentTime(nsecs_t presentTime); void setJankInfo(JankType jankType, int32_t jankMetadata); void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata); JankType getJankType() const; TimeStats::JankType getJankType() const; nsecs_t getBaseTime() const; nsecs_t getBaseTime() const; uid_t getOwnerUid() const; const std::string& getName() const; // All the timestamps are dumped relative to the baseTime // All the timestamps are dumped relative to the baseTime void dump(std::string& result, const std::string& indent, nsecs_t baseTime); void dump(std::string& result, const std::string& indent, nsecs_t baseTime); private: private: const uid_t mOwnerUid; const std::string mLayerName; const std::string mLayerName; const std::string mDebugName; PresentState mPresentState GUARDED_BY(mMutex); PresentState mPresentState GUARDED_BY(mMutex); const PredictionState mPredictionState; const PredictionState mPredictionState; const TimelineItem mPredictions; const TimelineItem mPredictions; TimelineItem mActuals GUARDED_BY(mMutex); TimelineItem mActuals GUARDED_BY(mMutex); nsecs_t mActualQueueTime GUARDED_BY(mMutex); nsecs_t mActualQueueTime GUARDED_BY(mMutex); mutable std::mutex mMutex; mutable std::mutex mMutex; JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank }; }; class FrameTimeline : public android::frametimeline::FrameTimeline { class FrameTimeline : public android::frametimeline::FrameTimeline { public: public: FrameTimeline(); FrameTimeline(std::shared_ptr<TimeStats> timeStats); ~FrameTimeline() = default; ~FrameTimeline() = default; frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) override; uid_t ownerUid, std::string layerName, std::string debugName, std::optional<int64_t> token) override; void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) override; SurfaceFrame::PresentState state) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; Loading Loading @@ -278,7 +267,7 @@ private: std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; PredictionState predictionState; PredictionState predictionState; JankType jankType = JankType::None; // Enum for the type of jank TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank int32_t jankMetadata = 0x0; // Additional details about the jank int32_t jankMetadata = 0x0; // Additional details about the jank }; }; Loading @@ -300,6 +289,7 @@ private: TokenManager mTokenManager; TokenManager mTokenManager; std::mutex mMutex; std::mutex mMutex; uint32_t mMaxDisplayFrames; uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // number doesn't represent any bounds on the number of surface frames that can go in a display // number doesn't represent any bounds on the number of surface frames that can go in a display Loading Loading
services/surfaceflinger/BufferQueueLayer.cpp +4 −2 Original line number Original line Diff line number Diff line Loading @@ -442,7 +442,8 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { } } auto surfaceFrame = auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId); mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName, mFrameTimelineVsyncId); surfaceFrame->setActualQueueTime(systemTime()); surfaceFrame->setActualQueueTime(systemTime()); mQueueItems.push_back({item, std::move(surfaceFrame)}); mQueueItems.push_back({item, std::move(surfaceFrame)}); Loading Loading @@ -480,7 +481,8 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { } } auto surfaceFrame = auto surfaceFrame = mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId); mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName, mFrameTimelineVsyncId); surfaceFrame->setActualQueueTime(systemTime()); surfaceFrame->setActualQueueTime(systemTime()); mQueueItems[mQueueItems.size() - 1].item = item; mQueueItems[mQueueItems.size() - 1].item = item; mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); Loading
services/surfaceflinger/BufferStateLayer.cpp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -277,7 +277,7 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence const int32_t layerId = getSequence(); const int32_t layerId = getSequence(); mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(), mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(), postTime); mOwnerUid, postTime); desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; Loading
services/surfaceflinger/FrameTimeline/Android.bp +2 −0 Original line number Original line Diff line number Diff line Loading @@ -5,10 +5,12 @@ cc_library_static { "FrameTimeline.cpp", "FrameTimeline.cpp", ], ], shared_libs: [ shared_libs: [ "android.hardware.graphics.composer@2.4", "libbase", "libbase", "libcutils", "libcutils", "liblog", "liblog", "libgui", "libgui", "libtimestats", "libui", "libui", "libutils", "libutils", ], ], Loading
services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +62 −36 Original line number Original line Diff line number Diff line Loading @@ -93,19 +93,19 @@ std::string toString(PredictionState predictionState) { } } } } std::string toString(JankType jankType) { std::string toString(TimeStats::JankType jankType) { switch (jankType) { switch (jankType) { case JankType::None: case TimeStats::JankType::None: return "None"; return "None"; case JankType::Display: case TimeStats::JankType::Display: return "Composer/Display - outside SF and App"; return "Composer/Display - outside SF and App"; case JankType::SurfaceFlingerDeadlineMissed: case TimeStats::JankType::SurfaceFlingerDeadlineMissed: return "SurfaceFlinger Deadline Missed"; return "SurfaceFlinger Deadline Missed"; case JankType::AppDeadlineMissed: case TimeStats::JankType::AppDeadlineMissed: return "App Deadline Missed"; return "App Deadline Missed"; case JankType::PredictionExpired: case TimeStats::JankType::PredictionExpired: return "Prediction Expired"; return "Prediction Expired"; case JankType::SurfaceFlingerEarlyLatch: case TimeStats::JankType::SurfaceFlingerEarlyLatch: return "SurfaceFlinger Early Latch"; return "SurfaceFlinger Early Latch"; default: default: return "Unclassified"; return "Unclassified"; Loading Loading @@ -177,15 +177,18 @@ void TokenManager::flushTokens(nsecs_t flushTime) { } } } } SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState, SurfaceFrame::SurfaceFrame(uid_t ownerUid, std::string layerName, std::string debugName, PredictionState predictionState, frametimeline::TimelineItem&& predictions) frametimeline::TimelineItem&& predictions) : mLayerName(layerName), : mOwnerUid(ownerUid), mLayerName(std::move(layerName)), mDebugName(std::move(debugName)), mPresentState(PresentState::Unknown), mPresentState(PresentState::Unknown), mPredictionState(predictionState), mPredictionState(predictionState), mPredictions(predictions), mPredictions(predictions), mActuals({0, 0, 0}), mActuals({0, 0, 0}), mActualQueueTime(0), mActualQueueTime(0), mJankType(JankType::None), mJankType(TimeStats::JankType::None), mJankMetadata(0) {} mJankMetadata(0) {} void SurfaceFrame::setPresentState(PresentState state) { void SurfaceFrame::setPresentState(PresentState state) { Loading Loading @@ -227,17 +230,25 @@ void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) { mActuals.presentTime = presentTime; mActuals.presentTime = presentTime; } } void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) { void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); mJankType = jankType; mJankType = jankType; mJankMetadata = jankMetadata; mJankMetadata = jankMetadata; } } JankType SurfaceFrame::getJankType() const { TimeStats::JankType SurfaceFrame::getJankType() const { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); return mJankType; return mJankType; } } uid_t SurfaceFrame::getOwnerUid() const { return mOwnerUid; } const std::string& SurfaceFrame::getName() const { return mLayerName; } nsecs_t SurfaceFrame::getBaseTime() const { nsecs_t SurfaceFrame::getBaseTime() const { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); Loading Loading @@ -267,8 +278,8 @@ std::string presentStateToString(SurfaceFrame::PresentState presentState) { void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "Layer - %s", mLayerName.c_str()); StringAppendF(&result, "Layer - %s", mDebugName.c_str()); if (mJankType != JankType::None) { if (mJankType != TimeStats::JankType::None) { // Easily identify a janky Surface Frame in the dump // Easily identify a janky Surface Frame in the dump StringAppendF(&result, " [*] "); StringAppendF(&result, " [*] "); } } Loading @@ -285,33 +296,35 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); } } FrameTimeline::FrameTimeline() FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats) : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), mMaxDisplayFrames(kDefaultMaxDisplayFrames) {} mMaxDisplayFrames(kDefaultMaxDisplayFrames), mTimeStats(std::move(timeStats)) {} FrameTimeline::DisplayFrame::DisplayFrame() FrameTimeline::DisplayFrame::DisplayFrame() : surfaceFlingerPredictions(TimelineItem()), : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()), surfaceFlingerActuals(TimelineItem()), predictionState(PredictionState::None), predictionState(PredictionState::None), jankType(JankType::None), jankType(TimeStats::JankType::None), jankMetadata(0) { jankMetadata(0) { this->surfaceFrames.reserve(kNumSurfaceFramesInitial); this->surfaceFrames.reserve(kNumSurfaceFramesInitial); } } std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) { uid_t uid, std::string layerName, std::string debugName, std::optional<int64_t> token) { ATRACE_CALL(); ATRACE_CALL(); if (!token) { if (!token) { return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None, return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), TimelineItem()); PredictionState::None, TimelineItem()); } } std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); if (predictions) { if (predictions) { return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid, return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), PredictionState::Valid, std::move(*predictions)); std::move(*predictions)); } } return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired, return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), TimelineItem()); PredictionState::Expired, TimelineItem()); } } void FrameTimeline::addSurfaceFrame( void FrameTimeline::addSurfaceFrame( Loading Loading @@ -359,6 +372,7 @@ void FrameTimeline::flushPendingPresentFences() { } } } } if (signalTime != Fence::SIGNAL_TIME_INVALID) { if (signalTime != Fence::SIGNAL_TIME_INVALID) { int32_t totalJankReasons = TimeStats::JankType::None; auto& displayFrame = pendingPresentFence.second; auto& displayFrame = pendingPresentFence.second; displayFrame->surfaceFlingerActuals.presentTime = signalTime; displayFrame->surfaceFlingerActuals.presentTime = signalTime; Loading @@ -377,21 +391,26 @@ void FrameTimeline::flushPendingPresentFences() { displayFrame->jankMetadata |= EarlyFinish; displayFrame->jankMetadata |= EarlyFinish; } } if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) { if ((displayFrame->jankMetadata & EarlyFinish) && displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch; (displayFrame->jankMetadata & EarlyPresent)) { } else if (displayFrame->jankMetadata & LateFinish & LatePresent) { displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed; } else if ((displayFrame->jankMetadata & LateFinish) && (displayFrame->jankMetadata & LatePresent)) { displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed; } else if (displayFrame->jankMetadata & EarlyPresent || } else if (displayFrame->jankMetadata & EarlyPresent || displayFrame->jankMetadata & LatePresent) { displayFrame->jankMetadata & LatePresent) { // Cases where SF finished early but frame was presented late and vice versa // Cases where SF finished early but frame was presented late and vice versa displayFrame->jankType = JankType::Display; displayFrame->jankType = TimeStats::JankType::Display; } } } } if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { displayFrame->jankMetadata |= displayFrame->jankMetadata |= sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; } } totalJankReasons |= displayFrame->jankType; for (auto& surfaceFrame : displayFrame->surfaceFrames) { for (auto& surfaceFrame : displayFrame->surfaceFrames) { if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { // Only presented SurfaceFrames need to be updated // Only presented SurfaceFrames need to be updated Loading @@ -401,13 +420,13 @@ void FrameTimeline::flushPendingPresentFences() { const auto& predictionState = surfaceFrame->getPredictionState(); const auto& predictionState = surfaceFrame->getPredictionState(); if (predictionState == PredictionState::Expired) { if (predictionState == PredictionState::Expired) { // Jank analysis cannot be done on apps that don't use predictions // Jank analysis cannot be done on apps that don't use predictions surfaceFrame->setJankInfo(JankType::PredictionExpired, 0); surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0); continue; continue; } else if (predictionState == PredictionState::Valid) { } else if (predictionState == PredictionState::Valid) { const auto& actuals = surfaceFrame->getActuals(); const auto& actuals = surfaceFrame->getActuals(); const auto& predictions = surfaceFrame->getPredictions(); const auto& predictions = surfaceFrame->getPredictions(); int32_t jankMetadata = 0; int32_t jankMetadata = 0; JankType jankType = JankType::None; TimeStats::JankType jankType = TimeStats::JankType::None; if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish : EarlyFinish; : EarlyFinish; Loading @@ -419,19 +438,26 @@ void FrameTimeline::flushPendingPresentFences() { : EarlyPresent; : EarlyPresent; } } if (jankMetadata & EarlyPresent) { if (jankMetadata & EarlyPresent) { jankType = JankType::SurfaceFlingerEarlyLatch; jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; } else if (jankMetadata & LatePresent) { } else if (jankMetadata & LatePresent) { if (jankMetadata & EarlyFinish) { if (jankMetadata & EarlyFinish) { // TODO(b/169890654): Classify this properly // TODO(b/169890654): Classify this properly jankType = JankType::Display; jankType = TimeStats::JankType::Display; } else { } else { jankType = JankType::AppDeadlineMissed; jankType = TimeStats::JankType::AppDeadlineMissed; } } } } totalJankReasons |= jankType; mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(), surfaceFrame->getName(), jankType | displayFrame->jankType); surfaceFrame->setJankInfo(jankType, jankMetadata); surfaceFrame->setJankInfo(jankType, jankMetadata); } } } } } } mTimeStats->incrementJankyFrames(totalJankReasons); } } mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); Loading Loading @@ -467,7 +493,7 @@ nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& display void FrameTimeline::dumpDisplayFrame(std::string& result, void FrameTimeline::dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>& displayFrame, const std::shared_ptr<DisplayFrame>& displayFrame, nsecs_t baseTime) { nsecs_t baseTime) { if (displayFrame->jankType != JankType::None) { if (displayFrame->jankType != TimeStats::JankType::None) { // Easily identify a janky Display Frame in the dump // Easily identify a janky Display Frame in the dump StringAppendF(&result, " [*] "); StringAppendF(&result, " [*] "); } } Loading Loading @@ -501,11 +527,11 @@ void FrameTimeline::dumpJank(std::string& result) { nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); for (size_t i = 0; i < mDisplayFrames.size(); i++) { for (size_t i = 0; i < mDisplayFrames.size(); i++) { const auto& displayFrame = mDisplayFrames[i]; const auto& displayFrame = mDisplayFrames[i]; if (displayFrame->jankType == JankType::None) { if (displayFrame->jankType == TimeStats::JankType::None) { // Check if any Surface Frame has been janky // Check if any Surface Frame has been janky bool isJanky = false; bool isJanky = false; for (const auto& surfaceFrame : displayFrame->surfaceFrames) { for (const auto& surfaceFrame : displayFrame->surfaceFrames) { if (surfaceFrame->getJankType() != JankType::None) { if (surfaceFrame->getJankType() != TimeStats::JankType::None) { isJanky = true; isJanky = true; break; break; } } Loading
services/surfaceflinger/FrameTimeline/FrameTimeline.h +21 −31 Original line number Original line Diff line number Diff line Loading @@ -16,9 +16,7 @@ #pragma once #pragma once #include <deque> #include <../TimeStats/TimeStats.h> #include <mutex> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposer.h> #include <ui/FenceTime.h> #include <ui/FenceTime.h> #include <utils/RefBase.h> #include <utils/RefBase.h> Loading @@ -26,26 +24,10 @@ #include <utils/Timers.h> #include <utils/Timers.h> #include <utils/Vector.h> #include <utils/Vector.h> namespace android::frametimeline { #include <deque> #include <mutex> /* namespace android::frametimeline { * The type of jank that is associated with a Display/Surface frame */ enum class JankType { // No Jank None, // Jank not related to SurfaceFlinger or the App Display, // SF took too long on the CPU SurfaceFlingerDeadlineMissed, // Either App or GPU took too long on the frame AppDeadlineMissed, // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame. PredictionExpired, // Latching a buffer early might cause an early present of the frame SurfaceFlingerEarlyLatch, }; enum JankMetadata { enum JankMetadata { // Frame was presented earlier than expected // Frame was presented earlier than expected Loading Loading @@ -147,8 +129,10 @@ public: // Create a new surface frame, set the predictions based on a token and return it to the caller. // Create a new surface frame, set the predictions based on a token and return it to the caller. // Sets the PredictionState of SurfaceFrame. // Sets the PredictionState of SurfaceFrame. // Debug name is the human-readable debugging string for dumpsys. virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) = 0; uid_t uid, std::string layerName, std::string debugName, std::optional<int64_t> token) = 0; // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // composited into one display frame. // composited into one display frame. Loading Loading @@ -206,8 +190,8 @@ private: class SurfaceFrame : public android::frametimeline::SurfaceFrame { class SurfaceFrame : public android::frametimeline::SurfaceFrame { public: public: SurfaceFrame(const std::string& layerName, PredictionState predictionState, SurfaceFrame(uid_t uid, std::string layerName, std::string debugName, TimelineItem&& predictions); PredictionState predictionState, TimelineItem&& predictions); ~SurfaceFrame() = default; ~SurfaceFrame() = default; TimelineItem getPredictions() const override { return mPredictions; }; TimelineItem getPredictions() const override { return mPredictions; }; Loading @@ -221,32 +205,37 @@ public: void setAcquireFenceTime(nsecs_t acquireFenceTime) override; void setAcquireFenceTime(nsecs_t acquireFenceTime) override; void setPresentState(PresentState state) override; void setPresentState(PresentState state) override; void setActualPresentTime(nsecs_t presentTime); void setActualPresentTime(nsecs_t presentTime); void setJankInfo(JankType jankType, int32_t jankMetadata); void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata); JankType getJankType() const; TimeStats::JankType getJankType() const; nsecs_t getBaseTime() const; nsecs_t getBaseTime() const; uid_t getOwnerUid() const; const std::string& getName() const; // All the timestamps are dumped relative to the baseTime // All the timestamps are dumped relative to the baseTime void dump(std::string& result, const std::string& indent, nsecs_t baseTime); void dump(std::string& result, const std::string& indent, nsecs_t baseTime); private: private: const uid_t mOwnerUid; const std::string mLayerName; const std::string mLayerName; const std::string mDebugName; PresentState mPresentState GUARDED_BY(mMutex); PresentState mPresentState GUARDED_BY(mMutex); const PredictionState mPredictionState; const PredictionState mPredictionState; const TimelineItem mPredictions; const TimelineItem mPredictions; TimelineItem mActuals GUARDED_BY(mMutex); TimelineItem mActuals GUARDED_BY(mMutex); nsecs_t mActualQueueTime GUARDED_BY(mMutex); nsecs_t mActualQueueTime GUARDED_BY(mMutex); mutable std::mutex mMutex; mutable std::mutex mMutex; JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank }; }; class FrameTimeline : public android::frametimeline::FrameTimeline { class FrameTimeline : public android::frametimeline::FrameTimeline { public: public: FrameTimeline(); FrameTimeline(std::shared_ptr<TimeStats> timeStats); ~FrameTimeline() = default; ~FrameTimeline() = default; frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) override; uid_t ownerUid, std::string layerName, std::string debugName, std::optional<int64_t> token) override; void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) override; SurfaceFrame::PresentState state) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; Loading Loading @@ -278,7 +267,7 @@ private: std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; PredictionState predictionState; PredictionState predictionState; JankType jankType = JankType::None; // Enum for the type of jank TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank int32_t jankMetadata = 0x0; // Additional details about the jank int32_t jankMetadata = 0x0; // Additional details about the jank }; }; Loading @@ -300,6 +289,7 @@ private: TokenManager mTokenManager; TokenManager mTokenManager; std::mutex mMutex; std::mutex mMutex; uint32_t mMaxDisplayFrames; uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // number doesn't represent any bounds on the number of surface frames that can go in a display // number doesn't represent any bounds on the number of surface frames that can go in a display Loading