diff options
| author | 2020-10-06 00:11:45 +0000 | |
|---|---|---|
| committer | 2020-10-06 00:11:45 +0000 | |
| commit | 4e9769a7ccef89ecf4dce68ca90f4319d688c608 (patch) | |
| tree | 83c5be5bf38865b13ca905f53fc5297b6cb32072 /services/surfaceflinger/FrameTimeline | |
| parent | dd3282660a2c6c8730e05486643e6ec991932a8c (diff) | |
| parent | 2d73632786885af1cfb2a92fe6b2f00f6671fd5a (diff) | |
Merge changes I872e2405,Iac8c8377
* changes:
Add a backdoor to change FrameTimeline's maxDisplayFrames
Dumpsys for FrameTimeline
Diffstat (limited to 'services/surfaceflinger/FrameTimeline')
| -rw-r--r-- | services/surfaceflinger/FrameTimeline/FrameTimeline.cpp | 380 | ||||
| -rw-r--r-- | services/surfaceflinger/FrameTimeline/FrameTimeline.h | 108 |
2 files changed, 429 insertions, 59 deletions
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 9985253d2b..43176a3f10 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -22,12 +22,127 @@ #include <android-base/stringprintf.h> #include <utils/Log.h> #include <utils/Trace.h> +#include <chrono> #include <cinttypes> +#include <numeric> namespace android::frametimeline::impl { using base::StringAppendF; +void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals, + const std::string& indent, PredictionState predictionState, nsecs_t baseTime) { + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "\t\t"); + StringAppendF(&result, " Start time\t\t|"); + StringAppendF(&result, " End time\t\t|"); + StringAppendF(&result, " Present time\n"); + if (predictionState == PredictionState::Valid) { + // Dump the Predictions only if they are valid + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Expected\t|"); + std::chrono::nanoseconds startTime(predictions.startTime - baseTime); + std::chrono::nanoseconds endTime(predictions.endTime - baseTime); + std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime); + StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n", + std::chrono::duration<double, std::milli>(startTime).count(), + std::chrono::duration<double, std::milli>(endTime).count(), + std::chrono::duration<double, std::milli>(presentTime).count()); + } + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Actual \t|"); + + if (actuals.startTime == 0) { + StringAppendF(&result, "\t\tN/A\t|"); + } else { + std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime)); + StringAppendF(&result, "\t%10.2f\t|", + std::chrono::duration<double, std::milli>(startTime).count()); + } + if (actuals.endTime == 0) { + StringAppendF(&result, "\t\tN/A\t|"); + } else { + std::chrono::nanoseconds endTime(actuals.endTime - baseTime); + StringAppendF(&result, "\t%10.2f\t|", + std::chrono::duration<double, std::milli>(endTime).count()); + } + if (actuals.presentTime == 0) { + StringAppendF(&result, "\t\tN/A\n"); + } else { + std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime)); + StringAppendF(&result, "\t%10.2f\n", + std::chrono::duration<double, std::milli>(presentTime).count()); + } + + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "----------------------"); + StringAppendF(&result, "----------------------"); + StringAppendF(&result, "----------------------"); + StringAppendF(&result, "----------------------\n"); +} + +std::string toString(PredictionState predictionState) { + switch (predictionState) { + case PredictionState::Valid: + return "Valid"; + case PredictionState::Expired: + return "Expired"; + case PredictionState::None: + default: + return "None"; + } +} + +std::string toString(JankType jankType) { + switch (jankType) { + case JankType::None: + return "None"; + case JankType::Display: + return "Composer/Display - outside SF and App"; + case JankType::SurfaceFlingerDeadlineMissed: + return "SurfaceFlinger Deadline Missed"; + case JankType::AppDeadlineMissed: + return "App Deadline Missed"; + case JankType::PredictionExpired: + return "Prediction Expired"; + case JankType::SurfaceFlingerEarlyLatch: + return "SurfaceFlinger Early Latch"; + default: + return "Unclassified"; + } +} + +std::string jankMetadataBitmaskToString(int32_t jankMetadata) { + std::vector<std::string> jankInfo; + + if (jankMetadata & EarlyStart) { + jankInfo.emplace_back("Early Start"); + } else if (jankMetadata & LateStart) { + jankInfo.emplace_back("Late Start"); + } + + if (jankMetadata & EarlyFinish) { + jankInfo.emplace_back("Early Finish"); + } else if (jankMetadata & LateFinish) { + jankInfo.emplace_back("Late Finish"); + } + + if (jankMetadata & EarlyPresent) { + jankInfo.emplace_back("Early Present"); + } else if (jankMetadata & LatePresent) { + jankInfo.emplace_back("Late Present"); + } + // TODO(b/169876734): add GPU composition metadata here + + if (jankInfo.empty()) { + return "None"; + } + return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(), + [](const std::string& l, const std::string& r) { + return l.empty() ? r : l + ", " + r; + }); +} + int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); @@ -69,29 +184,26 @@ SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predict mPredictionState(predictionState), mPredictions(predictions), mActuals({0, 0, 0}), - mActualQueueTime(0) {} + mActualQueueTime(0), + mJankType(JankType::None), + mJankMetadata(0) {} void SurfaceFrame::setPresentState(PresentState state) { std::lock_guard<std::mutex> lock(mMutex); mPresentState = state; } -PredictionState SurfaceFrame::getPredictionState() { - std::lock_guard<std::mutex> lock(mMutex); - return mPredictionState; -} - -SurfaceFrame::PresentState SurfaceFrame::getPresentState() { +SurfaceFrame::PresentState SurfaceFrame::getPresentState() const { std::lock_guard<std::mutex> lock(mMutex); return mPresentState; } -TimelineItem SurfaceFrame::getActuals() { +TimelineItem SurfaceFrame::getActuals() const { std::lock_guard<std::mutex> lock(mMutex); return mActuals; } -nsecs_t SurfaceFrame::getActualQueueTime() { +nsecs_t SurfaceFrame::getActualQueueTime() const { std::lock_guard<std::mutex> lock(mMutex); return mActualQueueTime; } @@ -115,25 +227,74 @@ void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) { mActuals.presentTime = presentTime; } -void SurfaceFrame::dump(std::string& result) { +void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) { + std::lock_guard<std::mutex> lock(mMutex); + mJankType = jankType; + mJankMetadata = jankMetadata; +} + +JankType SurfaceFrame::getJankType() const { + std::lock_guard<std::mutex> lock(mMutex); + return mJankType; +} + +nsecs_t SurfaceFrame::getBaseTime() const { std::lock_guard<std::mutex> lock(mMutex); - StringAppendF(&result, "Present State : %d\n", static_cast<int>(mPresentState)); - StringAppendF(&result, "Prediction State : %d\n", static_cast<int>(mPredictionState)); - StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime); - StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime); - StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime); - StringAppendF(&result, "Predicted Render Complete Time : %" PRId64 "\n", mPredictions.endTime); - StringAppendF(&result, "Actual Render Complete Time : %" PRId64 "\n", mActuals.endTime); - StringAppendF(&result, "Predicted Present Time : %" PRId64 "\n", mPredictions.presentTime); - StringAppendF(&result, "Actual Present Time : %" PRId64 "\n", mActuals.presentTime); + nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); + if (mPredictionState == PredictionState::Valid) { + baseTime = std::min(baseTime, mPredictions.startTime); + } + if (mActuals.startTime != 0) { + baseTime = std::min(baseTime, mActuals.startTime); + } + baseTime = std::min(baseTime, mActuals.endTime); + return baseTime; } -FrameTimeline::FrameTimeline() : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()) {} +std::string presentStateToString(SurfaceFrame::PresentState presentState) { + using PresentState = SurfaceFrame::PresentState; + switch (presentState) { + case PresentState::Presented: + return "Presented"; + case PresentState::Dropped: + return "Dropped"; + case PresentState::Unknown: + default: + return "Unknown"; + } +} + +void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { + std::lock_guard<std::mutex> lock(mMutex); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Layer - %s", mLayerName.c_str()); + if (mJankType != JankType::None) { + // Easily identify a janky Surface Frame in the dump + StringAppendF(&result, " [*] "); + } + StringAppendF(&result, "\n"); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str()); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str()); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str()); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Jank Metadata: %s\n", + jankMetadataBitmaskToString(mJankMetadata).c_str()); + dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); +} + +FrameTimeline::FrameTimeline() + : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), + mMaxDisplayFrames(kDefaultMaxDisplayFrames) {} FrameTimeline::DisplayFrame::DisplayFrame() : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()), - predictionState(PredictionState::None) { + predictionState(PredictionState::None), + jankType(JankType::None), + jankMetadata(0) { this->surfaceFrames.reserve(kNumSurfaceFramesInitial); } @@ -200,10 +361,75 @@ void FrameTimeline::flushPendingPresentFences() { if (signalTime != Fence::SIGNAL_TIME_INVALID) { auto& displayFrame = pendingPresentFence.second; displayFrame->surfaceFlingerActuals.presentTime = signalTime; + + // Jank Analysis for DisplayFrame + const auto& sfActuals = displayFrame->surfaceFlingerActuals; + const auto& sfPredictions = displayFrame->surfaceFlingerPredictions; + if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) { + displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime + ? LatePresent + : EarlyPresent; + } + if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) { + if (sfActuals.endTime > sfPredictions.endTime) { + displayFrame->jankMetadata |= LateFinish; + } else { + displayFrame->jankMetadata |= EarlyFinish; + } + + if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) { + displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch; + } else if (displayFrame->jankMetadata & LateFinish & LatePresent) { + displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed; + } else if (displayFrame->jankMetadata & EarlyPresent || + displayFrame->jankMetadata & LatePresent) { + // Cases where SF finished early but frame was presented late and vice versa + displayFrame->jankType = JankType::Display; + } + } + if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { + displayFrame->jankMetadata |= + sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; + } + for (auto& surfaceFrame : displayFrame->surfaceFrames) { if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { // Only presented SurfaceFrames need to be updated surfaceFrame->setActualPresentTime(signalTime); + + // Jank Analysis for SurfaceFrame + const auto& predictionState = surfaceFrame->getPredictionState(); + if (predictionState == PredictionState::Expired) { + // Jank analysis cannot be done on apps that don't use predictions + surfaceFrame->setJankInfo(JankType::PredictionExpired, 0); + continue; + } else if (predictionState == PredictionState::Valid) { + const auto& actuals = surfaceFrame->getActuals(); + const auto& predictions = surfaceFrame->getPredictions(); + int32_t jankMetadata = 0; + JankType jankType = JankType::None; + if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { + jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish + : EarlyFinish; + } + if (std::abs(actuals.presentTime - predictions.presentTime) > + kPresentThreshold) { + jankMetadata |= actuals.presentTime > predictions.presentTime + ? LatePresent + : EarlyPresent; + } + if (jankMetadata & EarlyPresent) { + jankType = JankType::SurfaceFlingerEarlyLatch; + } else if (jankMetadata & LatePresent) { + if (jankMetadata & EarlyFinish) { + // TODO(b/169890654): Classify this properly + jankType = JankType::Display; + } else { + jankType = JankType::AppDeadlineMissed; + } + } + surfaceFrame->setJankInfo(jankType, jankMetadata); + } } } } @@ -214,7 +440,7 @@ void FrameTimeline::flushPendingPresentFences() { } void FrameTimeline::finalizeCurrentDisplayFrame() { - while (mDisplayFrames.size() >= kMaxDisplayFrames) { + while (mDisplayFrames.size() >= mMaxDisplayFrames) { // We maintain only a fixed number of frames' data. Pop older frames mDisplayFrames.pop_front(); } @@ -223,30 +449,100 @@ void FrameTimeline::finalizeCurrentDisplayFrame() { mCurrentDisplayFrame = std::make_shared<DisplayFrame>(); } -void FrameTimeline::dump(std::string& result) { +nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) { + nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); + if (displayFrame->predictionState == PredictionState::Valid) { + baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime); + } + baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime); + for (const auto& surfaceFrame : displayFrame->surfaceFrames) { + nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime(); + if (surfaceFrameBaseTime != 0) { + baseTime = std::min(baseTime, surfaceFrameBaseTime); + } + } + return baseTime; +} + +void FrameTimeline::dumpDisplayFrame(std::string& result, + const std::shared_ptr<DisplayFrame>& displayFrame, + nsecs_t baseTime) { + if (displayFrame->jankType != JankType::None) { + // Easily identify a janky Display Frame in the dump + StringAppendF(&result, " [*] "); + } + StringAppendF(&result, "\n"); + StringAppendF(&result, "Prediction State : %s\n", + toString(displayFrame->predictionState).c_str()); + StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str()); + StringAppendF(&result, "Jank Metadata: %s\n", + jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str()); + dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals, + "", displayFrame->predictionState, baseTime); + StringAppendF(&result, "\n"); + std::string indent = " "; // 4 spaces + for (const auto& surfaceFrame : displayFrame->surfaceFrames) { + surfaceFrame->dump(result, indent, baseTime); + } + StringAppendF(&result, "\n"); +} +void FrameTimeline::dumpAll(std::string& result) { std::lock_guard<std::mutex> lock(mMutex); StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size()); - for (const auto& displayFrame : mDisplayFrames) { - StringAppendF(&result, "---Display Frame---\n"); - StringAppendF(&result, "Prediction State : %d\n", - static_cast<int>(displayFrame->predictionState)); - StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n", - displayFrame->surfaceFlingerPredictions.startTime); - StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n", - displayFrame->surfaceFlingerActuals.startTime); - StringAppendF(&result, "Predicted SF Complete time : %" PRId64 "\n", - displayFrame->surfaceFlingerPredictions.endTime); - StringAppendF(&result, "Actual SF Complete time : %" PRId64 "\n", - displayFrame->surfaceFlingerActuals.endTime); - StringAppendF(&result, "Predicted Present time : %" PRId64 "\n", - displayFrame->surfaceFlingerPredictions.presentTime); - StringAppendF(&result, "Actual Present time : %" PRId64 "\n", - displayFrame->surfaceFlingerActuals.presentTime); - for (size_t i = 0; i < displayFrame->surfaceFrames.size(); i++) { - StringAppendF(&result, "Surface frame - %" PRId32 "\n", (int)i); - displayFrame->surfaceFrames[i]->dump(result); + nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); + for (size_t i = 0; i < mDisplayFrames.size(); i++) { + StringAppendF(&result, "Display Frame %d", static_cast<int>(i)); + dumpDisplayFrame(result, mDisplayFrames[i], baseTime); + } +} + +void FrameTimeline::dumpJank(std::string& result) { + std::lock_guard<std::mutex> lock(mMutex); + nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); + for (size_t i = 0; i < mDisplayFrames.size(); i++) { + const auto& displayFrame = mDisplayFrames[i]; + if (displayFrame->jankType == JankType::None) { + // Check if any Surface Frame has been janky + bool isJanky = false; + for (const auto& surfaceFrame : displayFrame->surfaceFrames) { + if (surfaceFrame->getJankType() != JankType::None) { + isJanky = true; + break; + } + } + if (!isJanky) { + continue; + } } + StringAppendF(&result, "Display Frame %d", static_cast<int>(i)); + dumpDisplayFrame(result, displayFrame, baseTime); + } +} +void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) { + ATRACE_CALL(); + std::unordered_map<std::string, bool> argsMap; + for (size_t i = 0; i < args.size(); i++) { + argsMap[std::string(String8(args[i]).c_str())] = true; + } + if (argsMap.count("-jank")) { + dumpJank(result); } + if (argsMap.count("-all")) { + dumpAll(result); + } +} + +void FrameTimeline::setMaxDisplayFrames(uint32_t size) { + std::lock_guard<std::mutex> lock(mMutex); + + // The size can either increase or decrease, clear everything, to be consistent + mDisplayFrames.clear(); + mPendingPresentFences.clear(); + mMaxDisplayFrames = size; +} + +void FrameTimeline::reset() { + setMaxDisplayFrames(kDefaultMaxDisplayFrames); } } // namespace android::frametimeline::impl diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 6d1bb73ee3..bd637df8bc 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -22,10 +22,48 @@ #include <gui/ISurfaceComposer.h> #include <ui/FenceTime.h> #include <utils/RefBase.h> +#include <utils/String16.h> #include <utils/Timers.h> +#include <utils/Vector.h> 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 { + // Frame was presented earlier than expected + EarlyPresent = 0x1, + // Frame was presented later than expected + LatePresent = 0x2, + // App/SF started earlier than expected + EarlyStart = 0x4, + // App/SF started later than expected + LateStart = 0x8, + // App/SF finished work earlier than the deadline + EarlyFinish = 0x10, + // App/SF finished work later than the deadline + LateFinish = 0x20, + // SF was in GPU composition + GpuComposition = 0x40, +}; + class FrameTimelineTest; /* @@ -82,11 +120,11 @@ public: virtual ~SurfaceFrame() = default; - virtual TimelineItem getPredictions() = 0; - virtual TimelineItem getActuals() = 0; - virtual nsecs_t getActualQueueTime() = 0; - virtual PresentState getPresentState() = 0; - virtual PredictionState getPredictionState() = 0; + virtual TimelineItem getPredictions() const = 0; + virtual TimelineItem getActuals() const = 0; + virtual nsecs_t getActualQueueTime() const = 0; + virtual PresentState getPresentState() const = 0; + virtual PredictionState getPredictionState() const = 0; virtual void setPresentState(PresentState state) = 0; @@ -127,7 +165,17 @@ public: virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) = 0; - virtual void dump(std::string& result) = 0; + // Args: + // -jank : Dumps only the Display Frames that are either janky themselves + // or contain janky Surface Frames. + // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within + virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0; + + // Sets the max number of display frames that can be stored. Called by SF backdoor. + virtual void setMaxDisplayFrames(uint32_t size); + + // Restores the max number of display frames to default. Called by SF backdoor. + virtual void reset() = 0; }; namespace impl { @@ -162,27 +210,33 @@ public: TimelineItem&& predictions); ~SurfaceFrame() = default; - TimelineItem getPredictions() override { return mPredictions; }; - TimelineItem getActuals() override; - nsecs_t getActualQueueTime() override; - PresentState getPresentState() override; - PredictionState getPredictionState() override; + TimelineItem getPredictions() const override { return mPredictions; }; + TimelineItem getActuals() const override; + nsecs_t getActualQueueTime() const override; + PresentState getPresentState() const override; + PredictionState getPredictionState() const override { return mPredictionState; }; void setActualStartTime(nsecs_t actualStartTime) override; void setActualQueueTime(nsecs_t actualQueueTime) override; void setAcquireFenceTime(nsecs_t acquireFenceTime) override; void setPresentState(PresentState state) override; void setActualPresentTime(nsecs_t presentTime); - void dump(std::string& result); + void setJankInfo(JankType jankType, int32_t jankMetadata); + JankType getJankType() const; + nsecs_t getBaseTime() const; + // All the timestamps are dumped relative to the baseTime + void dump(std::string& result, const std::string& indent, nsecs_t baseTime); private: const std::string mLayerName; PresentState mPresentState GUARDED_BY(mMutex); - PredictionState mPredictionState GUARDED_BY(mMutex); + const PredictionState mPredictionState; const TimelineItem mPredictions; TimelineItem mActuals GUARDED_BY(mMutex); - nsecs_t mActualQueueTime; - std::mutex mMutex; + nsecs_t mActualQueueTime GUARDED_BY(mMutex); + mutable std::mutex mMutex; + JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank + int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank }; class FrameTimeline : public android::frametimeline::FrameTimeline { @@ -198,7 +252,9 @@ public: void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) override; - void dump(std::string& result) override; + void parseArgs(const Vector<String16>& args, std::string& result) override; + void setMaxDisplayFrames(uint32_t size) override; + void reset() override; private: // Friend class for testing @@ -222,10 +278,19 @@ private: std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; PredictionState predictionState; + JankType jankType = JankType::None; // Enum for the type of jank + int32_t jankMetadata = 0x0; // Additional details about the jank }; void flushPendingPresentFences() REQUIRES(mMutex); void finalizeCurrentDisplayFrame() REQUIRES(mMutex); + // BaseTime is the smallest timestamp in a DisplayFrame. + // Used for dumping all timestamps relative to the oldest, making it easy to read. + nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex); + void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&, + nsecs_t baseTime) REQUIRES(mMutex); + void dumpAll(std::string& result); + void dumpJank(std::string& result); // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex); @@ -234,12 +299,21 @@ private: std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex); TokenManager mTokenManager; std::mutex mMutex; - static constexpr uint32_t kMaxDisplayFrames = 64; + uint32_t mMaxDisplayFrames; + static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // 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 // 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; + // The various thresholds for App and SF. If the actual timestamp falls within the threshold + // compared to prediction, we don't treat it as a jank. + static constexpr nsecs_t kPresentThreshold = + std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); + static constexpr nsecs_t kDeadlineThreshold = + std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); + static constexpr nsecs_t kSFStartThreshold = + std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(); }; } // namespace impl |