diff options
Diffstat (limited to 'services/surfaceflinger/FrameTimeline')
-rw-r--r-- | services/surfaceflinger/FrameTimeline/Android.bp | 30 | ||||
-rw-r--r-- | services/surfaceflinger/FrameTimeline/FrameTimeline.cpp | 1304 | ||||
-rw-r--r-- | services/surfaceflinger/FrameTimeline/FrameTimeline.h | 493 |
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 |