summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/FrameTimeline
diff options
context:
space:
mode:
Diffstat (limited to 'services/surfaceflinger/FrameTimeline')
-rw-r--r--services/surfaceflinger/FrameTimeline/Android.bp30
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.cpp1304
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.h493
3 files changed, 0 insertions, 1827 deletions
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
deleted file mode 100644
index 10a58333f9..0000000000
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_static {
- name: "libframetimeline",
- defaults: ["surfaceflinger_defaults"],
- srcs: [
- "FrameTimeline.cpp",
- ],
- shared_libs: [
- "android.hardware.graphics.composer@2.4",
- "libbase",
- "libcutils",
- "liblog",
- "libgui",
- "libtimestats",
- "libui",
- "libutils",
- ],
- static_libs: [
- "libperfetto_client_experimental",
- ],
- export_include_dirs: ["."],
-}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
deleted file mode 100644
index c294ff2695..0000000000
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ /dev/null
@@ -1,1304 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "FrameTimeline"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "FrameTimeline.h"
-
-#include <android-base/stringprintf.h>
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <cinttypes>
-#include <numeric>
-#include <unordered_set>
-
-namespace android::frametimeline {
-
-using base::StringAppendF;
-using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
-using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
-
-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) {
- // Animation leashes can send the endTime as -1
- 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:
- return "None";
- }
-}
-
-std::string jankTypeBitmaskToString(int32_t jankType) {
- if (jankType == JankType::None) {
- return "None";
- }
-
- std::vector<std::string> janks;
- if (jankType & JankType::DisplayHAL) {
- janks.emplace_back("Display HAL");
- jankType &= ~JankType::DisplayHAL;
- }
- if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
- janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
- jankType &= ~JankType::SurfaceFlingerCpuDeadlineMissed;
- }
- if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
- janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
- jankType &= ~JankType::SurfaceFlingerGpuDeadlineMissed;
- }
- if (jankType & JankType::AppDeadlineMissed) {
- janks.emplace_back("App Deadline Missed");
- jankType &= ~JankType::AppDeadlineMissed;
- }
- if (jankType & JankType::PredictionError) {
- janks.emplace_back("Prediction Error");
- jankType &= ~JankType::PredictionError;
- }
- if (jankType & JankType::SurfaceFlingerScheduling) {
- janks.emplace_back("SurfaceFlinger Scheduling");
- jankType &= ~JankType::SurfaceFlingerScheduling;
- }
- if (jankType & JankType::BufferStuffing) {
- janks.emplace_back("Buffer Stuffing");
- jankType &= ~JankType::BufferStuffing;
- }
- if (jankType & JankType::Unknown) {
- janks.emplace_back("Unknown jank");
- jankType &= ~JankType::Unknown;
- }
- if (jankType & JankType::SurfaceFlingerStuffing) {
- janks.emplace_back("SurfaceFlinger Stuffing");
- jankType &= ~JankType::SurfaceFlingerStuffing;
- }
-
- // jankType should be 0 if all types of jank were checked for.
- LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
- return std::accumulate(janks.begin(), janks.end(), std::string(),
- [](const std::string& l, const std::string& r) {
- return l.empty() ? r : l + ", " + r;
- });
-}
-
-std::string toString(FramePresentMetadata presentMetadata) {
- switch (presentMetadata) {
- case FramePresentMetadata::OnTimePresent:
- return "On Time Present";
- case FramePresentMetadata::LatePresent:
- return "Late Present";
- case FramePresentMetadata::EarlyPresent:
- return "Early Present";
- case FramePresentMetadata::UnknownPresent:
- return "Unknown Present";
- }
-}
-
-std::string toString(FrameReadyMetadata finishMetadata) {
- switch (finishMetadata) {
- case FrameReadyMetadata::OnTimeFinish:
- return "On Time Finish";
- case FrameReadyMetadata::LateFinish:
- return "Late Finish";
- case FrameReadyMetadata::UnknownFinish:
- return "Unknown Finish";
- }
-}
-
-std::string toString(FrameStartMetadata startMetadata) {
- switch (startMetadata) {
- case FrameStartMetadata::OnTimeStart:
- return "On Time Start";
- case FrameStartMetadata::LateStart:
- return "Late Start";
- case FrameStartMetadata::EarlyStart:
- return "Early Start";
- case FrameStartMetadata::UnknownStart:
- return "Unknown Start";
- }
-}
-
-std::string toString(SurfaceFrame::PresentState presentState) {
- using PresentState = SurfaceFrame::PresentState;
- switch (presentState) {
- case PresentState::Presented:
- return "Presented";
- case PresentState::Dropped:
- return "Dropped";
- case PresentState::Unknown:
- return "Unknown";
- }
-}
-
-FrameTimelineEvent::PresentType toProto(FramePresentMetadata presentMetadata) {
- switch (presentMetadata) {
- case FramePresentMetadata::EarlyPresent:
- return FrameTimelineEvent::PRESENT_EARLY;
- case FramePresentMetadata::LatePresent:
- return FrameTimelineEvent::PRESENT_LATE;
- case FramePresentMetadata::OnTimePresent:
- return FrameTimelineEvent::PRESENT_ON_TIME;
- case FramePresentMetadata::UnknownPresent:
- return FrameTimelineEvent::PRESENT_UNSPECIFIED;
- }
-}
-
-FrameTimelineEvent::PredictionType toProto(PredictionState predictionState) {
- switch (predictionState) {
- case PredictionState::Valid:
- return FrameTimelineEvent::PREDICTION_VALID;
- case PredictionState::Expired:
- return FrameTimelineEvent::PREDICTION_EXPIRED;
- case PredictionState::None:
- return FrameTimelineEvent::PREDICTION_UNKNOWN;
- }
-}
-
-int32_t jankTypeBitmaskToProto(int32_t jankType) {
- if (jankType == JankType::None) {
- return FrameTimelineEvent::JANK_NONE;
- }
-
- int32_t protoJank = 0;
- if (jankType & JankType::DisplayHAL) {
- protoJank |= FrameTimelineEvent::JANK_DISPLAY_HAL;
- jankType &= ~JankType::DisplayHAL;
- }
- if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
- protoJank |= FrameTimelineEvent::JANK_SF_CPU_DEADLINE_MISSED;
- jankType &= ~JankType::SurfaceFlingerCpuDeadlineMissed;
- }
- if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
- protoJank |= FrameTimelineEvent::JANK_SF_GPU_DEADLINE_MISSED;
- jankType &= ~JankType::SurfaceFlingerGpuDeadlineMissed;
- }
- if (jankType & JankType::AppDeadlineMissed) {
- protoJank |= FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
- jankType &= ~JankType::AppDeadlineMissed;
- }
- if (jankType & JankType::PredictionError) {
- protoJank |= FrameTimelineEvent::JANK_PREDICTION_ERROR;
- jankType &= ~JankType::PredictionError;
- }
- if (jankType & JankType::SurfaceFlingerScheduling) {
- protoJank |= FrameTimelineEvent::JANK_SF_SCHEDULING;
- jankType &= ~JankType::SurfaceFlingerScheduling;
- }
- if (jankType & JankType::BufferStuffing) {
- protoJank |= FrameTimelineEvent::JANK_BUFFER_STUFFING;
- jankType &= ~JankType::BufferStuffing;
- }
- if (jankType & JankType::Unknown) {
- protoJank |= FrameTimelineEvent::JANK_UNKNOWN;
- jankType &= ~JankType::Unknown;
- }
- if (jankType & JankType::SurfaceFlingerStuffing) {
- protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
- jankType &= ~JankType::SurfaceFlingerStuffing;
- }
-
- // jankType should be 0 if all types of jank were checked for.
- LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
- return protoJank;
-}
-
-// Returns the smallest timestamp from the set of predictions and actuals.
-nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions,
- TimelineItem actuals) {
- nsecs_t minTime = std::numeric_limits<nsecs_t>::max();
- if (predictionState == PredictionState::Valid) {
- // Checking start time for predictions is enough because start time is always lesser than
- // endTime and presentTime.
- minTime = std::min(minTime, predictions.startTime);
- }
-
- // Need to check startTime, endTime and presentTime for actuals because some frames might not
- // have them set.
- if (actuals.startTime != 0) {
- minTime = std::min(minTime, actuals.startTime);
- }
- if (actuals.endTime != 0) {
- minTime = std::min(minTime, actuals.endTime);
- }
- if (actuals.presentTime != 0) {
- minTime = std::min(minTime, actuals.endTime);
- }
- return minTime;
-}
-
-int64_t TraceCookieCounter::getCookieForTracing() {
- return ++mTraceCookie;
-}
-
-SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid,
- uid_t ownerUid, int32_t layerId, std::string layerName,
- std::string debugName, PredictionState predictionState,
- frametimeline::TimelineItem&& predictions,
- std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode)
- : mToken(frameTimelineInfo.vsyncId),
- mInputEventId(frameTimelineInfo.inputEventId),
- mOwnerPid(ownerPid),
- mOwnerUid(ownerUid),
- mLayerName(std::move(layerName)),
- mDebugName(std::move(debugName)),
- mLayerId(layerId),
- mPresentState(PresentState::Unknown),
- mPredictionState(predictionState),
- mPredictions(predictions),
- mActuals({0, 0, 0}),
- mTimeStats(timeStats),
- mJankClassificationThresholds(thresholds),
- mTraceCookieCounter(*traceCookieCounter),
- mIsBuffer(isBuffer),
- mGameMode(gameMode) {}
-
-void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
- std::scoped_lock lock(mMutex);
- mActuals.startTime = actualStartTime;
-}
-
-void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) {
- std::scoped_lock lock(mMutex);
- mActualQueueTime = actualQueueTime;
-}
-
-void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) {
- std::scoped_lock lock(mMutex);
- mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
-}
-
-void SurfaceFrame::setDropTime(nsecs_t dropTime) {
- std::scoped_lock lock(mMutex);
- mDropTime = dropTime;
-}
-
-void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) {
- std::scoped_lock lock(mMutex);
- LOG_ALWAYS_FATAL_IF(mPresentState != PresentState::Unknown,
- "setPresentState called on a SurfaceFrame from Layer - %s, that has a "
- "PresentState - %s set already.",
- mDebugName.c_str(), toString(mPresentState).c_str());
- mPresentState = presentState;
- mLastLatchTime = lastLatchTime;
-}
-
-void SurfaceFrame::setRenderRate(Fps renderRate) {
- std::lock_guard<std::mutex> lock(mMutex);
- mRenderRate = renderRate;
-}
-
-void SurfaceFrame::setGpuComposition() {
- std::scoped_lock lock(mMutex);
- mGpuComposition = true;
-}
-
-std::optional<int32_t> SurfaceFrame::getJankType() const {
- std::scoped_lock lock(mMutex);
- if (mPresentState == PresentState::Dropped) {
- // Return no jank if it's a dropped frame since we cannot attribute a jank to a it.
- return JankType::None;
- }
- if (mActuals.presentTime == 0) {
- // Frame hasn't been presented yet.
- return std::nullopt;
- }
- return mJankType;
-}
-
-nsecs_t SurfaceFrame::getBaseTime() const {
- std::scoped_lock lock(mMutex);
- return getMinTime(mPredictionState, mPredictions, mActuals);
-}
-
-TimelineItem SurfaceFrame::getActuals() const {
- std::scoped_lock lock(mMutex);
- return mActuals;
-}
-
-PredictionState SurfaceFrame::getPredictionState() const {
- std::scoped_lock lock(mMutex);
- return mPredictionState;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
- std::scoped_lock lock(mMutex);
- return mPresentState;
-}
-
-FramePresentMetadata SurfaceFrame::getFramePresentMetadata() const {
- std::scoped_lock lock(mMutex);
- return mFramePresentMetadata;
-}
-
-FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const {
- std::scoped_lock lock(mMutex);
- return mFrameReadyMetadata;
-}
-
-nsecs_t SurfaceFrame::getDropTime() const {
- std::scoped_lock lock(mMutex);
- return mDropTime;
-}
-
-void SurfaceFrame::promoteToBuffer() {
- std::scoped_lock lock(mMutex);
- LOG_ALWAYS_FATAL_IF(mIsBuffer == true,
- "Trying to promote an already promoted BufferSurfaceFrame from layer %s "
- "with token %" PRId64 "",
- mDebugName.c_str(), mToken);
- mIsBuffer = true;
-}
-
-bool SurfaceFrame::getIsBuffer() const {
- std::scoped_lock lock(mMutex);
- return mIsBuffer;
-}
-
-void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
- std::scoped_lock lock(mMutex);
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Layer - %s", mDebugName.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, "Token: %" PRId64 "\n", mToken);
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
- mRenderRate ? mRenderRate->getIntValue() : 0);
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Layer ID : %d\n", mLayerId);
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
- StringAppendF(&result, "%s", indent.c_str());
- if (mPresentState == PresentState::Dropped) {
- std::chrono::nanoseconds dropTime(mDropTime - baseTime);
- StringAppendF(&result, "Drop time : %10f\n",
- std::chrono::duration<double, std::milli>(dropTime).count());
- 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", jankTypeBitmaskToString(mJankType).c_str());
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
- std::chrono::nanoseconds latchTime(
- std::max(static_cast<int64_t>(0), mLastLatchTime - baseTime));
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Last latch time: %10f\n",
- std::chrono::duration<double, std::milli>(latchTime).count());
- if (mPredictionState == PredictionState::Valid) {
- nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
- std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
- StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Present delta: %10f\n",
- std::chrono::duration<double, std::milli>(presentDeltaNs).count());
- }
- dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
-}
-
-std::string SurfaceFrame::miniDump() const {
- std::scoped_lock lock(mMutex);
- std::string result;
- StringAppendF(&result, "Layer - %s\n", mDebugName.c_str());
- StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
- StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
- StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
- StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
- StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
- StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
- StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
- StringAppendF(&result, "Present time: %" PRId64 "", mActuals.presentTime);
- return result;
-}
-
-void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
- nsecs_t& deadlineDelta) {
- if (mPredictionState == PredictionState::Expired ||
- mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
- // Cannot do any classification for invalid present time.
- // For prediction expired case, we do not know what happened here to classify this
- // correctly. This could potentially be AppDeadlineMissed but that's assuming no app will
- // request frames 120ms apart.
- mJankType = JankType::Unknown;
- deadlineDelta = -1;
- return;
- }
-
- if (mPredictionState == PredictionState::None) {
- // Cannot do jank classification on frames that don't have a token.
- return;
- }
-
- deadlineDelta = mActuals.endTime - mPredictions.endTime;
- const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
- const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
- ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
- : 0;
-
- if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
- mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
- } else {
- mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
- }
-
- if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
- mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
- : FramePresentMetadata::EarlyPresent;
- } else {
- mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
- }
-
- if (mFramePresentMetadata == FramePresentMetadata::OnTimePresent) {
- // Frames presented on time are not janky.
- mJankType = JankType::None;
- } else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
- if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
- // Finish on time, Present early
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= refreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold) {
- // Delta factor of vsync
- mJankType = JankType::SurfaceFlingerScheduling;
- } else {
- // Delta not a factor of vsync
- mJankType = JankType::PredictionError;
- }
- } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- // Finish late, Present early
- mJankType = JankType::Unknown;
- }
- } else {
- if (mLastLatchTime != 0 && mPredictions.endTime <= mLastLatchTime) {
- // Buffer Stuffing.
- mJankType |= JankType::BufferStuffing;
- // In a stuffed state, the frame could be stuck on a dequeue wait for quite some time.
- // Because of this dequeue wait, it can be hard to tell if a frame was genuinely late.
- // We try to do this by moving the deadline. Since the queue could be stuffed by more
- // than one buffer, we take the last latch time as reference and give one vsync
- // worth of time for the frame to be ready.
- nsecs_t adjustedDeadline = mLastLatchTime + refreshRate.getPeriodNsecs();
- if (adjustedDeadline > mActuals.endTime) {
- mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
- } else {
- mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
- }
- }
- if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
- // Finish on time, Present late
- if (displayFrameJankType != JankType::None) {
- // Propagate displayFrame's jank if it exists
- mJankType |= displayFrameJankType;
- } else {
- if (!(mJankType & JankType::BufferStuffing)) {
- // In a stuffed state, if the app finishes on time and there is no display frame
- // jank, only buffer stuffing is the root cause of the jank.
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= refreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold) {
- // Delta factor of vsync
- mJankType |= JankType::SurfaceFlingerScheduling;
- } else {
- // Delta not a factor of vsync
- mJankType |= JankType::PredictionError;
- }
- }
- }
- } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- // Finish late, Present late
- mJankType |= JankType::AppDeadlineMissed;
- // Propagate DisplayFrame's jankType if it is janky
- mJankType |= displayFrameJankType;
- }
- }
-}
-
-void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
- nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
- std::scoped_lock lock(mMutex);
-
- if (mPresentState != PresentState::Presented) {
- // No need to update dropped buffers
- return;
- }
-
- mActuals.presentTime = presentTime;
- nsecs_t deadlineDelta = 0;
-
- classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta);
-
- if (mPredictionState != PredictionState::None) {
- // Only update janky frames if the app used vsync predictions
- mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
- mGameMode, mJankType, displayDeadlineDelta,
- displayPresentDelta, deadlineDelta});
- }
-}
-
-void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const {
- int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
-
- // Expected timeline start
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
-
- auto* event = packet->set_frame_timeline_event();
- auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start();
-
- expectedSurfaceFrameStartEvent->set_cookie(expectedTimelineCookie);
-
- expectedSurfaceFrameStartEvent->set_token(mToken);
- expectedSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
-
- expectedSurfaceFrameStartEvent->set_pid(mOwnerPid);
- expectedSurfaceFrameStartEvent->set_layer_name(mDebugName);
- });
-
- // Expected timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime));
-
- auto* event = packet->set_frame_timeline_event();
- auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
-
- expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
- });
-}
-
-void SurfaceFrame::traceActuals(int64_t displayFrameToken) const {
- int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
-
- // Actual timeline start
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- // Actual start time is not yet available, so use expected start instead
- if (mPredictionState == PredictionState::Expired) {
- // If prediction is expired, we can't use the predicted start time. Instead, just use a
- // start time a little earlier than the end time so that we have some info about this
- // frame in the trace.
- nsecs_t endTime =
- (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
- packet->set_timestamp(
- static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta));
- } else {
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
- }
-
- auto* event = packet->set_frame_timeline_event();
- auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start();
-
- actualSurfaceFrameStartEvent->set_cookie(actualTimelineCookie);
-
- actualSurfaceFrameStartEvent->set_token(mToken);
- actualSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
-
- actualSurfaceFrameStartEvent->set_pid(mOwnerPid);
- actualSurfaceFrameStartEvent->set_layer_name(mDebugName);
-
- if (mPresentState == PresentState::Dropped) {
- actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
- } else if (mPresentState == PresentState::Unknown) {
- actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
- } else {
- actualSurfaceFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
- }
- actualSurfaceFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
- FrameReadyMetadata::OnTimeFinish);
- actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
- actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
- actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
- actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer);
- });
-
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- if (mPresentState == PresentState::Dropped) {
- packet->set_timestamp(static_cast<uint64_t>(mDropTime));
- } else {
- packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime));
- }
-
- auto* event = packet->set_frame_timeline_event();
- auto* actualSurfaceFrameEndEvent = event->set_frame_end();
-
- actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
- });
-}
-
-/**
- * TODO(b/178637512): add inputEventId to the perfetto trace.
- */
-void SurfaceFrame::trace(int64_t displayFrameToken) const {
- if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID ||
- displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
- // No packets can be traced with a missing token.
- return;
- }
- if (getPredictionState() != PredictionState::Expired) {
- // Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in
- // a trace.
- tracePredictions(displayFrameToken);
- }
- traceActuals(displayFrameToken);
-}
-
-namespace impl {
-
-int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
- ATRACE_CALL();
- std::scoped_lock lock(mMutex);
- while (mPredictions.size() >= kMaxTokens) {
- mPredictions.erase(mPredictions.begin());
- }
- const int64_t assignedToken = mCurrentToken++;
- mPredictions[assignedToken] = predictions;
- return assignedToken;
-}
-
-std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) const {
- std::scoped_lock lock(mMutex);
- auto predictionsIterator = mPredictions.find(token);
- if (predictionsIterator != mPredictions.end()) {
- return predictionsIterator->second;
- }
- return {};
-}
-
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds)
- : mMaxDisplayFrames(kDefaultMaxDisplayFrames),
- mTimeStats(std::move(timeStats)),
- mSurfaceFlingerPid(surfaceFlingerPid),
- mJankClassificationThresholds(thresholds) {
- mCurrentDisplayFrame =
- std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
-}
-
-void FrameTimeline::onBootFinished() {
- perfetto::TracingInitArgs args;
- args.backends = perfetto::kSystemBackend;
- perfetto::Tracing::Initialize(args);
- registerDataSource();
-}
-
-void FrameTimeline::registerDataSource() {
- perfetto::DataSourceDescriptor dsd;
- dsd.set_name(kFrameTimelineDataSource);
- FrameTimelineDataSource::Register(dsd);
-}
-
-std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName, bool isBuffer, int32_t gameMode) {
- ATRACE_CALL();
- if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
- std::move(layerName), std::move(debugName),
- PredictionState::None, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter,
- isBuffer, gameMode);
- }
- std::optional<TimelineItem> predictions =
- mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
- if (predictions) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
- std::move(layerName), std::move(debugName),
- PredictionState::Valid, std::move(*predictions),
- mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter, isBuffer, gameMode);
- }
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
- std::move(layerName), std::move(debugName),
- PredictionState::Expired, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter,
- isBuffer, gameMode);
-}
-
-FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter)
- : mSurfaceFlingerPredictions(TimelineItem()),
- mSurfaceFlingerActuals(TimelineItem()),
- mTimeStats(timeStats),
- mJankClassificationThresholds(thresholds),
- mTraceCookieCounter(*traceCookieCounter) {
- mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
-}
-
-void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
- ATRACE_CALL();
- std::scoped_lock lock(mMutex);
- mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
-}
-
-void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate) {
- ATRACE_CALL();
- std::scoped_lock lock(mMutex);
- mCurrentDisplayFrame->onSfWakeUp(token, refreshRate,
- mTokenManager.getPredictionsForToken(token), wakeUpTime);
-}
-
-void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
- const std::shared_ptr<FenceTime>& presentFence,
- const std::shared_ptr<FenceTime>& gpuFence) {
- ATRACE_CALL();
- std::scoped_lock lock(mMutex);
- mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
- mCurrentDisplayFrame->setGpuFence(gpuFence);
- mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
- flushPendingPresentFences();
- finalizeCurrentDisplayFrame();
-}
-
-void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
- mSurfaceFrames.push_back(surfaceFrame);
-}
-
-void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, Fps refreshRate,
- std::optional<TimelineItem> predictions,
- nsecs_t wakeUpTime) {
- mToken = token;
- mRefreshRate = refreshRate;
- if (!predictions) {
- mPredictionState = PredictionState::Expired;
- } else {
- mPredictionState = PredictionState::Valid;
- mSurfaceFlingerPredictions = *predictions;
- }
- mSurfaceFlingerActuals.startTime = wakeUpTime;
-}
-
-void FrameTimeline::DisplayFrame::setPredictions(PredictionState predictionState,
- TimelineItem predictions) {
- mPredictionState = predictionState;
- mSurfaceFlingerPredictions = predictions;
-}
-
-void FrameTimeline::DisplayFrame::setActualStartTime(nsecs_t actualStartTime) {
- mSurfaceFlingerActuals.startTime = actualStartTime;
-}
-
-void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) {
- mSurfaceFlingerActuals.endTime = actualEndTime;
-}
-
-void FrameTimeline::DisplayFrame::setGpuFence(const std::shared_ptr<FenceTime>& gpuFence) {
- mGpuFence = gpuFence;
-}
-
-void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
- nsecs_t previousPresentTime) {
- if (mPredictionState == PredictionState::Expired ||
- mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
- // 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;
- deadlineDelta = 0;
- deltaToVsync = 0;
- return;
- }
-
- // Delta between the expected present and the actual present
- const nsecs_t presentDelta =
- mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
- // 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.
- deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
- ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
- : 0;
-
- if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
- mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
- : FramePresentMetadata::EarlyPresent;
- } else {
- mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
- }
-
- if (combinedEndTime > mSurfaceFlingerPredictions.endTime) {
- mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
- } else {
- mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
- }
-
- if (std::abs(mSurfaceFlingerActuals.startTime - mSurfaceFlingerPredictions.startTime) >
- mJankClassificationThresholds.startThreshold) {
- mFrameStartMetadata =
- mSurfaceFlingerActuals.startTime > mSurfaceFlingerPredictions.startTime
- ? FrameStartMetadata::LateStart
- : FrameStartMetadata::EarlyStart;
- }
-
- if (mFramePresentMetadata != FramePresentMetadata::OnTimePresent) {
- // Do jank classification only if present is not on time
- if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
- if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
- // Finish on time, Present early
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold)) {
- // Delta is a factor of vsync if its within the presentTheshold on either side
- // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
- // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
- mJankType = JankType::SurfaceFlingerScheduling;
- } else {
- // Delta is not a factor of vsync,
- mJankType = JankType::PredictionError;
- }
- } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- // Finish late, Present early
- mJankType = JankType::SurfaceFlingerScheduling;
- } else {
- // Finish time unknown
- mJankType = JankType::Unknown;
- }
- } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
- if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
- mJankClassificationThresholds.presentThreshold ||
- previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
- // The previous frame was either presented in the current frame's expected vsync or
- // it was presented even later than the current frame's expected vsync.
- mJankType = JankType::SurfaceFlingerStuffing;
- }
- if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
- !(mJankType & JankType::SurfaceFlingerStuffing)) {
- // Finish on time, Present late
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold)) {
- // Delta is a factor of vsync if its within the presentTheshold on either side
- // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
- // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
- mJankType = JankType::DisplayHAL;
- } else {
- // Delta is not a factor of vsync
- mJankType = JankType::PredictionError;
- }
- } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
- mSurfaceFlingerActuals.presentTime - previousPresentTime >
- mRefreshRate.getPeriodNsecs() +
- mJankClassificationThresholds.presentThreshold) {
- // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
- // was presented more than a vsync 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;
- }
- } else {
- // Present unknown
- mJankType = JankType::Unknown;
- }
- }
-}
-
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) {
- mSurfaceFlingerActuals.presentTime = signalTime;
- nsecs_t deadlineDelta = 0;
- nsecs_t deltaToVsync = 0;
- classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
-
- for (auto& surfaceFrame : mSurfaceFrames) {
- surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
- }
-}
-
-void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid) const {
- int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
-
- // Expected timeline start
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime));
-
- auto* event = packet->set_frame_timeline_event();
- auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start();
-
- expectedDisplayFrameStartEvent->set_cookie(expectedTimelineCookie);
-
- expectedDisplayFrameStartEvent->set_token(mToken);
- expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
- });
-
- // Expected timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime));
-
- auto* event = packet->set_frame_timeline_event();
- auto* expectedDisplayFrameEndEvent = event->set_frame_end();
-
- expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
- });
-}
-
-void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const {
- int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
-
- // Actual timeline start
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.startTime));
-
- auto* event = packet->set_frame_timeline_event();
- auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
-
- actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie);
-
- actualDisplayFrameStartEvent->set_token(mToken);
- actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
-
- actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
- actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
- FrameReadyMetadata::OnTimeFinish);
- actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE);
- actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
- actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState));
- });
-
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime));
-
- auto* event = packet->set_frame_timeline_event();
- auto* actualDisplayFrameEndEvent = event->set_frame_end();
-
- actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
- });
-}
-
-void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
- if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
- // DisplayFrame should not have an invalid token.
- ALOGE("Cannot trace DisplayFrame with invalid token");
- return;
- }
-
- if (mPredictionState == PredictionState::Valid) {
- // Expired and unknown predictions have zeroed timestamps. This cannot be used in any
- // meaningful way in a trace.
- tracePredictions(surfaceFlingerPid);
- }
- traceActuals(surfaceFlingerPid);
-
- for (auto& surfaceFrame : mSurfaceFrames) {
- surfaceFrame->trace(mToken);
- }
-}
-
-float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
- if (layerIds.empty()) {
- return 0.0f;
- }
-
- std::vector<nsecs_t> presentTimes;
- {
- std::scoped_lock lock(mMutex);
- presentTimes.reserve(mDisplayFrames.size());
- for (size_t i = 0; i < mDisplayFrames.size(); i++) {
- const auto& displayFrame = mDisplayFrames[i];
- if (displayFrame->getActuals().presentTime <= 0) {
- continue;
- }
- for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
- if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
- layerIds.count(surfaceFrame->getLayerId()) > 0) {
- // We're looking for DisplayFrames that presents at least one layer from
- // layerIds, so push the present time and skip looking through the rest of the
- // SurfaceFrames.
- presentTimes.push_back(displayFrame->getActuals().presentTime);
- break;
- }
- }
- }
- }
-
- // FPS can't be computed when there's fewer than 2 presented frames.
- if (presentTimes.size() <= 1) {
- return 0.0f;
- }
-
- nsecs_t priorPresentTime = -1;
- nsecs_t totalPresentToPresentWalls = 0;
-
- for (const nsecs_t presentTime : presentTimes) {
- if (priorPresentTime == -1) {
- priorPresentTime = presentTime;
- continue;
- }
-
- totalPresentToPresentWalls += (presentTime - priorPresentTime);
- priorPresentTime = presentTime;
- }
-
- if (CC_UNLIKELY(totalPresentToPresentWalls <= 0)) {
- ALOGW("Invalid total present-to-present duration when computing fps: %" PRId64,
- totalPresentToPresentWalls);
- return 0.0f;
- }
-
- const constexpr nsecs_t kOneSecond =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
- // (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
- // M frames / second
- return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
- static_cast<float>(totalPresentToPresentWalls);
-}
-
-void FrameTimeline::flushPendingPresentFences() {
- for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
- const auto& pendingPresentFence = mPendingPresentFences[i];
- nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
- if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
- signalTime = pendingPresentFence.first->getSignalTime();
- if (signalTime == Fence::SIGNAL_TIME_PENDING) {
- continue;
- }
- }
- auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid);
- mPreviousPresentTime = signalTime;
-
- mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
- --i;
- }
-}
-
-void FrameTimeline::finalizeCurrentDisplayFrame() {
- while (mDisplayFrames.size() >= mMaxDisplayFrames) {
- // We maintain only a fixed number of frames' data. Pop older frames
- mDisplayFrames.pop_front();
- }
- mDisplayFrames.push_back(mCurrentDisplayFrame);
- mCurrentDisplayFrame.reset();
- mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter);
-}
-
-nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
- nsecs_t baseTime =
- getMinTime(mPredictionState, mSurfaceFlingerPredictions, mSurfaceFlingerActuals);
- for (const auto& surfaceFrame : mSurfaceFrames) {
- nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
- if (surfaceFrameBaseTime != 0) {
- baseTime = std::min(baseTime, surfaceFrameBaseTime);
- }
- }
- return baseTime;
-}
-
-void FrameTimeline::DisplayFrame::dumpJank(std::string& result, nsecs_t baseTime,
- int displayFrameCount) const {
- if (mJankType == JankType::None) {
- // Check if any Surface Frame has been janky
- bool isJanky = false;
- for (const auto& surfaceFrame : mSurfaceFrames) {
- if (surfaceFrame->getJankType() != JankType::None) {
- isJanky = true;
- break;
- }
- }
- if (!isJanky) {
- return;
- }
- }
- StringAppendF(&result, "Display Frame %d", displayFrameCount);
- dump(result, baseTime);
-}
-
-void FrameTimeline::DisplayFrame::dumpAll(std::string& result, nsecs_t baseTime) const {
- dump(result, baseTime);
-}
-
-void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) const {
- if (mJankType != JankType::None) {
- // Easily identify a janky Display Frame in the dump
- StringAppendF(&result, " [*] ");
- }
- StringAppendF(&result, "\n");
- StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
- StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
- StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
- StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
- StringAppendF(&result, "Start Metadata: %s\n", toString(mFrameStartMetadata).c_str());
- std::chrono::nanoseconds vsyncPeriod(mRefreshRate.getPeriodNsecs());
- StringAppendF(&result, "Vsync Period: %10f\n",
- std::chrono::duration<double, std::milli>(vsyncPeriod).count());
- nsecs_t presentDelta =
- mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
- std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
- StringAppendF(&result, "Present delta: %10f\n",
- std::chrono::duration<double, std::milli>(presentDeltaNs).count());
- std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mRefreshRate.getPeriodNsecs());
- StringAppendF(&result, "Present delta %% refreshrate: %10f\n",
- std::chrono::duration<double, std::milli>(deltaToVsync).count());
- dumpTable(result, mSurfaceFlingerPredictions, mSurfaceFlingerActuals, "", mPredictionState,
- baseTime);
- StringAppendF(&result, "\n");
- std::string indent = " "; // 4 spaces
- for (const auto& surfaceFrame : mSurfaceFrames) {
- surfaceFrame->dump(result, indent, baseTime);
- }
- StringAppendF(&result, "\n");
-}
-
-void FrameTimeline::dumpAll(std::string& result) {
- std::scoped_lock lock(mMutex);
- StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
- nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
- for (size_t i = 0; i < mDisplayFrames.size(); i++) {
- StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
- mDisplayFrames[i]->dumpAll(result, baseTime);
- }
-}
-
-void FrameTimeline::dumpJank(std::string& result) {
- std::scoped_lock lock(mMutex);
- nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
- for (size_t i = 0; i < mDisplayFrames.size(); i++) {
- mDisplayFrames[i]->dumpJank(result, baseTime, static_cast<int>(i));
- }
-}
-
-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::scoped_lock 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 impl
-} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
deleted file mode 100644
index 139f91f3bc..0000000000
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <../Fps.h>
-#include <../TimeStats/TimeStats.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/JankInfo.h>
-#include <perfetto/trace/android/frame_timeline_event.pbzero.h>
-#include <perfetto/tracing.h>
-#include <ui/FenceTime.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <deque>
-#include <mutex>
-
-namespace android::frametimeline {
-
-class FrameTimelineTest;
-
-using namespace std::chrono_literals;
-
-// Metadata indicating how the frame was presented w.r.t expected present time.
-enum class FramePresentMetadata : int8_t {
- // Frame was presented on time
- OnTimePresent,
- // Frame was presented late
- LatePresent,
- // Frame was presented early
- EarlyPresent,
- // Unknown/initial state
- UnknownPresent,
-};
-
-// Metadata comparing the frame's actual finish time to the expected deadline.
-enum class FrameReadyMetadata : int8_t {
- // App/SF finished on time. Early finish is treated as on time since the goal of any component
- // is to finish before the deadline.
- OnTimeFinish,
- // App/SF finished work later than expected
- LateFinish,
- // Unknown/initial state
- UnknownFinish,
-};
-
-// Metadata comparing the frame's actual start time to the expected start time.
-enum class FrameStartMetadata : int8_t {
- // App/SF started on time
- OnTimeStart,
- // App/SF started later than expected
- LateStart,
- // App/SF started earlier than expected
- EarlyStart,
- // Unknown/initial state
- UnknownStart,
-};
-
-/*
- * Collection of timestamps that can be used for both predictions and actual times.
- */
-struct TimelineItem {
- TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
- const nsecs_t presentTime = 0)
- : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
-
- nsecs_t startTime;
- nsecs_t endTime;
- nsecs_t presentTime;
-
- bool operator==(const TimelineItem& other) const {
- return startTime == other.startTime && endTime == other.endTime &&
- presentTime == other.presentTime;
- }
-
- bool operator!=(const TimelineItem& other) const { return !(*this == other); }
-};
-
-struct JankClassificationThresholds {
- // The various thresholds for App and SF. If the actual timestamp falls within the threshold
- // compared to prediction, we treat it as on time.
- nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
- nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(0ms).count();
- nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-};
-
-/*
- * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
- * saves these predictions for a short period of time and returns the predictions for a given token,
- * if it hasn't expired.
- */
-class TokenManager {
-public:
- virtual ~TokenManager() = default;
-
- // Generates a token for the given set of predictions. Stores the predictions for 120ms and
- // destroys it later.
- virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
-
- // Returns the stored predictions for a given token, if the predictions haven't expired.
- virtual std::optional<TimelineItem> getPredictionsForToken(int64_t token) const = 0;
-};
-
-enum class PredictionState {
- Valid, // Predictions obtained successfully from the TokenManager
- Expired, // TokenManager no longer has the predictions
- None, // Predictions are either not present or didn't come from TokenManager
-};
-
-/*
- * Trace cookie is used to send start and end timestamps of <Surface/Display>Frames separately
- * without needing to resend all the other information. We send all info to perfetto, along with a
- * new cookie, in the start of a frame. For the corresponding end, we just send the same cookie.
- * This helps in reducing the amount of data emitted by the producer.
- */
-class TraceCookieCounter {
-public:
- int64_t getCookieForTracing();
-
-private:
- // Friend class for testing
- friend class android::frametimeline::FrameTimelineTest;
-
- std::atomic<int64_t> mTraceCookie = 0;
-};
-
-class SurfaceFrame {
-public:
- enum class PresentState {
- Presented, // Buffer was latched and presented by SurfaceFlinger
- Dropped, // Buffer was dropped by SurfaceFlinger
- Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet
- };
-
- // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
- // TokenManager), Thresholds and TimeStats pointer.
- SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName,
- PredictionState predictionState, TimelineItem&& predictions,
- std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode);
- ~SurfaceFrame() = default;
-
- // Returns std::nullopt if the frame hasn't been classified yet.
- // Used by both SF and FrameTimeline.
- std::optional<int32_t> getJankType() const;
-
- // Functions called by SF
- int64_t getToken() const { return mToken; };
- int32_t getInputEventId() const { return mInputEventId; };
- TimelineItem getPredictions() const { return mPredictions; };
- // Actual timestamps of the app are set individually at different functions.
- // Start time (if the app provides) and Queue time are accessible after queueing the frame,
- // whereas Acquire Fence time is available only during latch. Drop time is available at the time
- // the buffer was dropped.
- void setActualStartTime(nsecs_t actualStartTime);
- void setActualQueueTime(nsecs_t actualQueueTime);
- void setAcquireFenceTime(nsecs_t acquireFenceTime);
- void setDropTime(nsecs_t dropTime);
- void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
- void setRenderRate(Fps renderRate);
- void setGpuComposition();
-
- // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
- // isBuffer.
- void promoteToBuffer();
-
- // Functions called by FrameTimeline
- // BaseTime is the smallest timestamp in this SurfaceFrame.
- // Used for dumping all timestamps relative to the oldest, making it easy to read.
- nsecs_t getBaseTime() const;
- // Sets the actual present time, appropriate metadata and classifies the jank.
- // displayRefreshRate, displayDeadlineDelta, and displayPresentDelta are propagated from the
- // display frame.
- void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
- nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
- // All the timestamps are dumped relative to the baseTime
- void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
- // Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
- std::string miniDump() const;
- // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
- // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
- // DisplayFrame at the trace processor side.
- void trace(int64_t displayFrameToken) const;
-
- // Getter functions used only by FrameTimelineTests and SurfaceFrame internally
- TimelineItem getActuals() const;
- pid_t getOwnerPid() const { return mOwnerPid; };
- int32_t getLayerId() const { return mLayerId; };
- PredictionState getPredictionState() const;
- PresentState getPresentState() const;
- FrameReadyMetadata getFrameReadyMetadata() const;
- FramePresentMetadata getFramePresentMetadata() const;
- nsecs_t getDropTime() const;
- bool getIsBuffer() const;
-
- // For prediction expired frames, this delta is subtracted from the actual end time to get a
- // start time decent enough to see in traces.
- // TODO(b/172587309): Remove this when we have actual start times.
- static constexpr nsecs_t kPredictionExpiredStartTimeDelta =
- std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-
-private:
- void tracePredictions(int64_t displayFrameToken) const;
- void traceActuals(int64_t displayFrameToken) const;
- void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
- nsecs_t& deadlineDelta) REQUIRES(mMutex);
-
- const int64_t mToken;
- const int32_t mInputEventId;
- const pid_t mOwnerPid;
- const uid_t mOwnerUid;
- const std::string mLayerName;
- const std::string mDebugName;
- const int32_t mLayerId;
- PresentState mPresentState GUARDED_BY(mMutex);
- const PredictionState mPredictionState;
- const TimelineItem mPredictions;
- TimelineItem mActuals GUARDED_BY(mMutex);
- std::shared_ptr<TimeStats> mTimeStats;
- const JankClassificationThresholds mJankClassificationThresholds;
- nsecs_t mActualQueueTime GUARDED_BY(mMutex) = 0;
- nsecs_t mDropTime GUARDED_BY(mMutex) = 0;
- mutable std::mutex mMutex;
- // Bitmask for the type of jank
- int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
- // Indicates if this frame was composited by the GPU or not
- bool mGpuComposition GUARDED_BY(mMutex) = false;
- // Rendering rate for this frame.
- std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
- // Enum for the type of present
- FramePresentMetadata mFramePresentMetadata GUARDED_BY(mMutex) =
- FramePresentMetadata::UnknownPresent;
- // Enum for the type of finish
- FrameReadyMetadata mFrameReadyMetadata GUARDED_BY(mMutex) = FrameReadyMetadata::UnknownFinish;
- // Time when the previous buffer from the same layer was latched by SF. This is used in checking
- // for BufferStuffing where the current buffer is expected to be ready but the previous buffer
- // was latched instead.
- nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
- // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
- // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
- TraceCookieCounter& mTraceCookieCounter;
- // Tells if the SurfaceFrame is representing a buffer or a transaction without a
- // buffer(animations)
- bool mIsBuffer;
- // GameMode from the layer. Used in metrics.
- int32_t mGameMode = 0;
-};
-
-/*
- * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were
- * presented
- */
-class FrameTimeline {
-public:
- virtual ~FrameTimeline() = default;
- virtual TokenManager* getTokenManager() = 0;
-
- // Initializes the Perfetto DataSource that emits DisplayFrame and SurfaceFrame events. Test
- // classes can avoid double registration by mocking this function.
- virtual void onBootFinished() = 0;
-
- // Create a new surface frame, set the predictions based on a token and return it to the caller.
- // Debug name is the human-readable debugging string for dumpsys.
- virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
- const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
- int32_t gameMode) = 0;
-
- // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
- // composited into one display frame.
- virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) = 0;
-
- // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
- // 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 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. If a gpuFence was also provided, its tracked in the
- // corresponding DisplayFrame.
- virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
- const std::shared_ptr<FenceTime>& gpuFence) = 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);
-
- // Computes the historical fps for the provided set of layer IDs
- // The fps is compted from the linear timeline of present timestamps for DisplayFrames
- // containing at least one layer ID.
- virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
-
- // Restores the max number of display frames to default. Called by SF backdoor.
- virtual void reset() = 0;
-};
-
-namespace impl {
-
-class TokenManager : public android::frametimeline::TokenManager {
-public:
- TokenManager() : mCurrentToken(FrameTimelineInfo::INVALID_VSYNC_ID + 1) {}
- ~TokenManager() = default;
-
- int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
- std::optional<TimelineItem> getPredictionsForToken(int64_t token) const override;
-
-private:
- // Friend class for testing
- friend class android::frametimeline::FrameTimelineTest;
-
- void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
-
- std::map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
- int64_t mCurrentToken GUARDED_BY(mMutex);
- mutable std::mutex mMutex;
- static constexpr size_t kMaxTokens = 500;
-};
-
-class FrameTimeline : public android::frametimeline::FrameTimeline {
-public:
- class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
- void OnSetup(const SetupArgs&) override{};
- void OnStart(const StartArgs&) override{};
- void OnStop(const StopArgs&) override{};
- };
-
- /*
- * DisplayFrame should be used only internally within FrameTimeline. All members and methods are
- * guarded by FrameTimeline's mMutex.
- */
- class DisplayFrame {
- public:
- DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter);
- virtual ~DisplayFrame() = default;
- // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
- // SurfaceFrame is janky.
- void dumpJank(std::string& result, nsecs_t baseTime, int displayFrameCount) const;
- // Dumpsys interface - dumps all data irrespective of jank
- void dumpAll(std::string& result, nsecs_t baseTime) const;
- // Emits a packet for perfetto tracing. The function body will be executed only if tracing
- // is enabled.
- void trace(pid_t surfaceFlingerPid) const;
- // Sets the token, vsyncPeriod, predictions and SF start time.
- void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
- nsecs_t wakeUpTime);
- // Sets the appropriate metadata and classifies the jank.
- void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
- // Adds the provided SurfaceFrame to the current display frame.
- void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
-
- void setPredictions(PredictionState predictionState, TimelineItem predictions);
- void setActualStartTime(nsecs_t actualStartTime);
- void setActualEndTime(nsecs_t actualEndTime);
- 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.
- nsecs_t getBaseTime() const;
-
- // Functions to be used only in testing.
- TimelineItem getActuals() const { return mSurfaceFlingerActuals; };
- TimelineItem getPredictions() const { return mSurfaceFlingerPredictions; };
- FrameStartMetadata getFrameStartMetadata() const { return mFrameStartMetadata; };
- FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
- FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
- int32_t getJankType() const { return mJankType; }
- const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const {
- return mSurfaceFrames;
- }
-
- private:
- void dump(std::string& result, nsecs_t baseTime) const;
- void tracePredictions(pid_t surfaceFlingerPid) const;
- void traceActuals(pid_t surfaceFlingerPid) const;
- void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
- nsecs_t previousPresentTime);
-
- int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
-
- /* Usage of TimelineItem w.r.t SurfaceFlinger
- * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates
- * endTime Time when SurfaceFlinger sends a composited frame to Display
- * presentTime Time when the composited frame was presented on screen
- */
- TimelineItem mSurfaceFlingerPredictions;
- TimelineItem mSurfaceFlingerActuals;
- std::shared_ptr<TimeStats> mTimeStats;
- const JankClassificationThresholds mJankClassificationThresholds;
-
- // Collection of predictions and actual values sent over by Layers
- std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
-
- PredictionState mPredictionState = PredictionState::None;
- // Bitmask for the type of jank
- int32_t mJankType = JankType::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
- FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
- // Enum for the type of finish
- FrameReadyMetadata mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
- // Enum for the type of start
- FrameStartMetadata mFrameStartMetadata = FrameStartMetadata::UnknownStart;
- // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
- // timeline
- Fps mRefreshRate;
- // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto.
- // Using a reference here because the counter is owned by FrameTimeline, which outlives
- // DisplayFrame.
- TraceCookieCounter& mTraceCookieCounter;
- };
-
- FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds = {});
- ~FrameTimeline() = default;
-
- frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
- std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
- const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
- int32_t gameMode) override;
- 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,
- 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;
- void reset() override;
-
- // Sets up the perfetto tracing backend and data source.
- void onBootFinished() override;
- // Registers the data source with the perfetto backend. Called as part of onBootFinished()
- // and should not be called manually outside of tests.
- void registerDataSource();
-
- static constexpr char kFrameTimelineDataSource[] = "android.surfaceflinger.frametimeline";
-
-private:
- // Friend class for testing
- friend class android::frametimeline::FrameTimelineTest;
-
- void flushPendingPresentFences() REQUIRES(mMutex);
- void finalizeCurrentDisplayFrame() 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);
- std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
- mPendingPresentFences GUARDED_BY(mMutex);
- std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
- TokenManager mTokenManager;
- TraceCookieCounter mTraceCookieCounter;
- mutable std::mutex mMutex;
- uint32_t mMaxDisplayFrames;
- std::shared_ptr<TimeStats> mTimeStats;
- const pid_t mSurfaceFlingerPid;
- nsecs_t mPreviousPresentTime = 0;
- const JankClassificationThresholds mJankClassificationThresholds;
- 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;
-};
-
-} // namespace impl
-} // namespace android::frametimeline