Merge "Expand jank classification"
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2dacae1..1808571 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -99,15 +99,13 @@
status_t JankData::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeInt64, frameVsyncId);
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankType));
+ SAFE_PARCEL(output->writeInt32, jankType);
return NO_ERROR;
}
status_t JankData::readFromParcel(const Parcel* input) {
SAFE_PARCEL(input->readInt64, &frameVsyncId);
- int32_t jankTypeInt;
- SAFE_PARCEL(input->readInt32, &jankTypeInt);
- jankType = static_cast<JankType>(jankTypeInt);
+ SAFE_PARCEL(input->readInt32, &jankType);
return NO_ERROR;
}
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 26b3840..cb17cee 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -69,15 +69,14 @@
status_t readFromParcel(const Parcel* input) override;
JankData();
- JankData(int64_t frameVsyncId, JankType jankType)
- : frameVsyncId(frameVsyncId),
- jankType(jankType) {}
+ JankData(int64_t frameVsyncId, int32_t jankType)
+ : frameVsyncId(frameVsyncId), jankType(jankType) {}
// Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
int64_t frameVsyncId;
- // The type of jank occurred
- JankType jankType;
+ // Bitmask of janks that occurred
+ int32_t jankType;
};
class SurfaceStats : public Parcelable {
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 47daf95..fc91714 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -18,24 +18,31 @@
namespace android {
-// Jank information tracked by SurfaceFlinger for the purpose of funneling to telemetry.
+// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry.
+// TODO(b/175843808): Change JankType from enum to enum class
enum JankType {
// No Jank
None = 0x0,
- // Jank not related to SurfaceFlinger or the App
- Display = 0x1,
+ // Jank that occurs in the layers below SurfaceFlinger
+ DisplayHAL = 0x1,
// SF took too long on the CPU
- SurfaceFlingerDeadlineMissed = 0x2,
+ SurfaceFlingerCpuDeadlineMissed = 0x2,
// SF took too long on the GPU
SurfaceFlingerGpuDeadlineMissed = 0x4,
// Either App or GPU took too long on the frame
AppDeadlineMissed = 0x8,
- // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
- // jank
- // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
- PredictionExpired = 0x10,
- // Latching a buffer early might cause an early present of the frame
- SurfaceFlingerEarlyLatch = 0x20,
+ // Vsync predictions have drifted beyond the threshold from the actual HWVsync
+ PredictionError = 0x10,
+ // Janks caused due to the time SF was scheduled to work on the frame
+ // Example: SF woke up too early and latched a buffer resulting in an early present
+ SurfaceFlingerScheduling = 0x20,
+ // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
+ // presented later because the previous buffer was presented in its expected vsync. This
+ // usually happens if there is an unexpectedly long frame causing the rest of the buffers
+ // to enter a stuffed state.
+ BufferStuffing = 0x40,
+ // Jank due to unknown reasons.
+ Unknown = 0x80,
};
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 325ecfe..56d8742 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -274,8 +274,8 @@
mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
if (mQueueItems[0].surfaceFrame) {
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
- PresentState::Dropped);
+ mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
}
mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
@@ -290,8 +290,8 @@
Mutex::Autolock lock(mQueueItemLock);
for (auto& [item, surfaceFrame] : mQueueItems) {
if (surfaceFrame) {
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
- PresentState::Dropped);
+ surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
}
}
mQueueItems.clear();
@@ -321,8 +321,8 @@
mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
if (mQueueItems[0].surfaceFrame) {
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
- PresentState::Dropped);
+ mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
}
mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
@@ -336,8 +336,9 @@
if (mQueueItems[0].surfaceFrame) {
mQueueItems[0].surfaceFrame->setAcquireFenceTime(
mQueueItems[0].item.mFenceTime->getSignalTime());
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
- PresentState::Presented);
+ mQueueItems[0].surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
+ mLastLatchTime = latchTime;
}
mQueueItems.erase(mQueueItems.begin());
}
@@ -436,8 +437,9 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
- mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+ mOwnerPid, mOwnerUid, mName,
+ mName);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems.push_back({item, surfaceFrame});
@@ -475,8 +477,9 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
- mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+ mOwnerPid, mOwnerUid, mName,
+ mName);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems[mQueueItems.size() - 1].item = item;
mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5a6b9bc..3551026 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -146,6 +146,11 @@
// a buffer to correlate the buffer with the vsync id. Can only be accessed
// with the SF state lock held.
std::optional<int64_t> mFrameTimelineVsyncId;
+
+ // Keeps track of the time SF latched the last buffer from this layer.
+ // Used in buffer stuffing analysis in FrameTimeline.
+ // TODO(b/176106798): Find a way to do this for BLASTBufferQueue as well.
+ nsecs_t mLastLatchTime = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b09f07a..a8fef04 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -26,7 +26,7 @@
#include <cinttypes>
#include <numeric>
-namespace android::frametimeline::impl {
+namespace android::frametimeline {
using base::StringAppendF;
using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
@@ -60,7 +60,8 @@
StringAppendF(&result, "\t%10.2f\t|",
std::chrono::duration<double, std::milli>(startTime).count());
}
- if (actuals.endTime == 0) {
+ 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);
@@ -89,124 +90,160 @@
case PredictionState::Expired:
return "Expired";
case PredictionState::None:
- default:
return "None";
}
}
-std::string toString(JankType jankType) {
- switch (jankType) {
- case JankType::None:
- return "None";
- case JankType::Display:
- return "Composer/Display - outside SF and App";
- case JankType::SurfaceFlingerDeadlineMissed:
- return "SurfaceFlinger Deadline Missed";
- case JankType::AppDeadlineMissed:
- return "App Deadline Missed";
- case JankType::PredictionExpired:
- return "Prediction Expired";
- case JankType::SurfaceFlingerEarlyLatch:
- return "SurfaceFlinger Early Latch";
- default:
- return "Unclassified";
- }
-}
-
-std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
- std::vector<std::string> jankInfo;
-
- if (jankMetadata & EarlyStart) {
- jankInfo.emplace_back("Early Start");
- } else if (jankMetadata & LateStart) {
- jankInfo.emplace_back("Late Start");
- }
-
- if (jankMetadata & EarlyFinish) {
- jankInfo.emplace_back("Early Finish");
- } else if (jankMetadata & LateFinish) {
- jankInfo.emplace_back("Late Finish");
- }
-
- if (jankMetadata & EarlyPresent) {
- jankInfo.emplace_back("Early Present");
- } else if (jankMetadata & LatePresent) {
- jankInfo.emplace_back("Late Present");
- }
- // TODO(b/169876734): add GPU composition metadata here
-
- if (jankInfo.empty()) {
+std::string jankTypeBitmaskToString(int32_t jankType) {
+ // TODO(b/175843808): Make this a switch case if jankType becomes an enum class
+ std::vector<std::string> janks;
+ if (jankType == JankType::None) {
return "None";
}
- return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+ if (jankType & JankType::DisplayHAL) {
+ janks.emplace_back("Display HAL");
+ }
+ if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
+ janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
+ }
+ if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
+ janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
+ }
+ if (jankType & JankType::AppDeadlineMissed) {
+ janks.emplace_back("App Deadline Missed");
+ }
+ if (jankType & JankType::PredictionError) {
+ janks.emplace_back("Prediction Error");
+ }
+ if (jankType & JankType::SurfaceFlingerScheduling) {
+ janks.emplace_back("SurfaceFlinger Scheduling");
+ }
+ if (jankType & JankType::BufferStuffing) {
+ janks.emplace_back("Buffer Stuffing");
+ }
+ if (jankType & JankType::Unknown) {
+ janks.emplace_back("Unknown jank");
+ }
+ return std::accumulate(janks.begin(), janks.end(), std::string(),
[](const std::string& l, const std::string& r) {
return l.empty() ? r : l + ", " + r;
});
}
-FrameTimelineEvent::PresentType presentTypeToProto(int32_t jankMetadata) {
- if (jankMetadata & EarlyPresent) {
- return FrameTimelineEvent::PRESENT_EARLY;
+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";
}
- if (jankMetadata & LatePresent) {
- return FrameTimelineEvent::PRESENT_LATE;
- }
- return FrameTimelineEvent::PRESENT_ON_TIME;
}
-FrameTimelineEvent::JankType JankTypeToProto(JankType jankType) {
+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::JankType jankTypeBitmaskToProto(int32_t jankType) {
+ // TODO(b/175843808): Either make the proto a bitmask or jankType an enum class
switch (jankType) {
case JankType::None:
return FrameTimelineEvent::JANK_NONE;
- case JankType::Display:
+ case JankType::DisplayHAL:
return FrameTimelineEvent::JANK_DISPLAY_HAL;
- case JankType::SurfaceFlingerDeadlineMissed:
+ case JankType::SurfaceFlingerCpuDeadlineMissed:
+ case JankType::SurfaceFlingerGpuDeadlineMissed:
return FrameTimelineEvent::JANK_SF_DEADLINE_MISSED;
case JankType::AppDeadlineMissed:
- case JankType::PredictionExpired:
+ case JankType::PredictionError:
return FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
+ case JankType::SurfaceFlingerScheduling:
+ return FrameTimelineEvent::JANK_SF_SCHEDULING;
+ case JankType::BufferStuffing:
+ return FrameTimelineEvent::JANK_BUFFER_STUFFING;
default:
+ // TODO(b/175843808): Remove default if JankType becomes an enum class
return FrameTimelineEvent::JANK_UNKNOWN;
}
}
-int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
- ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mMutex);
- const int64_t assignedToken = mCurrentToken++;
- mPredictions[assignedToken] = predictions;
- mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
- flushTokens(systemTime());
- return assignedToken;
-}
-
-std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
- std::lock_guard<std::mutex> lock(mMutex);
- flushTokens(systemTime());
- auto predictionsIterator = mPredictions.find(token);
- if (predictionsIterator != mPredictions.end()) {
- return predictionsIterator->second;
+// 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);
}
- return {};
-}
-void TokenManager::flushTokens(nsecs_t flushTime) {
- for (size_t i = 0; i < mTokens.size(); i++) {
- if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
- mPredictions.erase(mTokens[i].first);
- mTokens.erase(mTokens.begin() + static_cast<int>(i));
- --i;
- } else {
- // Tokens are ordered by time. If i'th token is within the retention time, then the
- // i+1'th token will also be within retention time.
- break;
- }
+ // 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;
}
SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
std::string debugName, PredictionState predictionState,
- frametimeline::TimelineItem&& predictions)
+ frametimeline::TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats,
+ JankClassificationThresholds thresholds)
: mToken(token),
mOwnerPid(ownerPid),
mOwnerUid(ownerUid),
@@ -216,29 +253,8 @@
mPredictionState(predictionState),
mPredictions(predictions),
mActuals({0, 0, 0}),
- mActualQueueTime(0),
- mJankType(JankType::None),
- mJankMetadata(0) {}
-
-void SurfaceFrame::setPresentState(PresentState state) {
- std::lock_guard<std::mutex> lock(mMutex);
- mPresentState = state;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mPresentState;
-}
-
-TimelineItem SurfaceFrame::getActuals() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mActuals;
-}
-
-nsecs_t SurfaceFrame::getActualQueueTime() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mActualQueueTime;
-}
+ mTimeStats(timeStats),
+ mJankClassificationThresholds(thresholds) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -254,18 +270,13 @@
mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
}
-void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
+void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) {
std::lock_guard<std::mutex> lock(mMutex);
- mActuals.presentTime = presentTime;
+ mPresentState = presentState;
+ mLastLatchTime = lastLatchTime;
}
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
- std::lock_guard<std::mutex> lock(mMutex);
- mJankType = jankType;
- mJankMetadata = jankMetadata;
-}
-
-std::optional<JankType> SurfaceFrame::getJankType() const {
+std::optional<int32_t> SurfaceFrame::getJankType() const {
std::lock_guard<std::mutex> lock(mMutex);
if (mActuals.presentTime == 0) {
return std::nullopt;
@@ -275,31 +286,30 @@
nsecs_t SurfaceFrame::getBaseTime() const {
std::lock_guard<std::mutex> lock(mMutex);
- nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
- if (mPredictionState == PredictionState::Valid) {
- baseTime = std::min(baseTime, mPredictions.startTime);
- }
- if (mActuals.startTime != 0) {
- baseTime = std::min(baseTime, mActuals.startTime);
- }
- baseTime = std::min(baseTime, mActuals.endTime);
- return baseTime;
+ return getMinTime(mPredictionState, mPredictions, mActuals);
}
-std::string presentStateToString(SurfaceFrame::PresentState presentState) {
- using PresentState = SurfaceFrame::PresentState;
- switch (presentState) {
- case PresentState::Presented:
- return "Presented";
- case PresentState::Dropped:
- return "Dropped";
- case PresentState::Unknown:
- default:
- return "Unknown";
- }
+TimelineItem SurfaceFrame::getActuals() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mActuals;
}
-void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPresentState;
+}
+
+FramePresentMetadata SurfaceFrame::getFramePresentMetadata() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mFramePresentMetadata;
+}
+
+FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mFrameReadyMetadata;
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
std::lock_guard<std::mutex> lock(mMutex);
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Layer - %s", mDebugName.c_str());
@@ -309,21 +319,130 @@
}
StringAppendF(&result, "\n");
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+ StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Jank Metadata: %s\n",
- jankMetadataBitmaskToString(mJankMetadata).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);
}
-void SurfaceFrame::traceSurfaceFrame(int64_t displayFrameToken) {
- using FrameTimelineDataSource = FrameTimeline::FrameTimelineDataSource;
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType,
+ nsecs_t vsyncPeriod) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mPresentState != PresentState::Presented) {
+ // No need to update dropped buffers
+ return;
+ }
+
+ mActuals.presentTime = presentTime;
+ // Jank Analysis for SurfaceFrame
+ if (mPredictionState == PredictionState::None) {
+ // Cannot do jank classification on frames that don't have a token.
+ return;
+ }
+ if (mPredictionState == PredictionState::Expired) {
+ // 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;
+ mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+ mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+ mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+ return;
+ }
+
+ const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+ const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
+ const nsecs_t deltaToVsync = std::abs(presentDelta) % vsyncPeriod;
+
+ 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 >= vsyncPeriod - 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;
+ }
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present late
+ if (displayFrameJankType != JankType::None) {
+ // Propagate displayFrame's jank if it exists
+ mJankType |= displayFrameJankType;
+ } else {
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= vsyncPeriod - 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
+ if (displayFrameJankType == JankType::None) {
+ // Display frame is not janky, so purely app's fault
+ mJankType |= JankType::AppDeadlineMissed;
+ } else {
+ // Propagate DisplayFrame's jankType if it is janky
+ mJankType |= displayFrameJankType;
+ }
+ }
+ }
+ mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+}
+
+void SurfaceFrame::trace(int64_t displayFrameToken) {
+ using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
std::lock_guard<std::mutex> lock(mMutex);
if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
@@ -349,11 +468,12 @@
} else if (mPresentState == PresentState::Unknown) {
surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
} else {
- surfaceFrameEvent->set_present_type(presentTypeToProto(mJankMetadata));
+ surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata));
}
- surfaceFrameEvent->set_on_time_finish(!(mJankMetadata & LateFinish));
- surfaceFrameEvent->set_gpu_composition(mJankMetadata & GpuComposition);
- surfaceFrameEvent->set_jank_type(JankTypeToProto(mJankType));
+ surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ surfaceFrameEvent->set_gpu_composition(mGpuComposition);
+ surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
@@ -366,10 +486,45 @@
});
}
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
- : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+namespace impl {
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ const int64_t assignedToken = mCurrentToken++;
+ mPredictions[assignedToken] = {systemTime(), predictions};
+ flushTokens(systemTime());
+ return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto predictionsIterator = mPredictions.find(token);
+ if (predictionsIterator != mPredictions.end()) {
+ return predictionsIterator->second.predictions;
+ }
+ return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+ for (auto it = mPredictions.begin(); it != mPredictions.end();) {
+ if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
+ it = mPredictions.erase(it);
+ } else {
+ // Tokens are ordered by time. If i'th token is within the retention time, then the
+ // i+1'th token will also be within retention time.
+ break;
+ }
+ }
+}
+
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+ JankClassificationThresholds thresholds)
+ : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)),
mMaxDisplayFrames(kDefaultMaxDisplayFrames),
- mTimeStats(std::move(timeStats)) {}
+ mTimeStats(std::move(timeStats)),
+ mSurfaceFlingerPid(surfaceFlingerPid),
+ mJankClassificationThresholds(thresholds) {}
void FrameTimeline::onBootFinished() {
perfetto::TracingInitArgs args;
@@ -384,67 +539,225 @@
FrameTimelineDataSource::Register(dsd);
}
-FrameTimeline::DisplayFrame::DisplayFrame()
- : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()) {
- this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
-}
-
-std::shared_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
- std::optional<int64_t> token) {
+std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+ std::optional<int64_t> token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+ std::string debugName) {
ATRACE_CALL();
if (!token) {
- return std::make_shared<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID,ownerPid,
- ownerUid, std::move(layerName),
- std::move(debugName), PredictionState::None,
- TimelineItem());
+ return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
+ ownerUid, std::move(layerName), std::move(debugName),
+ PredictionState::None, TimelineItem(), mTimeStats,
+ mJankClassificationThresholds);
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
- return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
- std::move(layerName), std::move(debugName),
- PredictionState::Valid,
- std::move(*predictions));
+ return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Valid,
+ std::move(*predictions), mTimeStats,
+ mJankClassificationThresholds);
}
- return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
- std::move(debugName), PredictionState::Expired,
- TimelineItem());
+ return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Expired,
+ TimelineItem(), mTimeStats,
+ mJankClassificationThresholds);
}
-void FrameTimeline::addSurfaceFrame(
- std::shared_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
- SurfaceFrame::PresentState state) {
- ATRACE_CALL();
- surfaceFrame->setPresentState(state);
- std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->surfaceFrames.push_back(
- std::static_pointer_cast<impl::SurfaceFrame>(surfaceFrame));
+FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
+ JankClassificationThresholds thresholds)
+ : mSurfaceFlingerPredictions(TimelineItem()),
+ mSurfaceFlingerActuals(TimelineItem()),
+ mTimeStats(timeStats),
+ mJankClassificationThresholds(thresholds) {
+ mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
}
-void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
ATRACE_CALL();
- const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->token = token;
- if (!prediction) {
- mCurrentDisplayFrame->predictionState = PredictionState::Expired;
- } else {
- mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
- mCurrentDisplayFrame->predictionState = PredictionState::Valid;
- }
- mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+ mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, nsecs_t vsyncPeriod) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->onSfWakeUp(token, vsyncPeriod,
+ mTokenManager.getPredictionsForToken(token), wakeUpTime);
}
void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+ mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
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, nsecs_t vsyncPeriod,
+ std::optional<TimelineItem> predictions,
+ nsecs_t wakeUpTime) {
+ mToken = token;
+ mVsyncPeriod = vsyncPeriod;
+ if (!predictions) {
+ mPredictionState = PredictionState::Expired;
+ } else {
+ mPredictionState = PredictionState::Valid;
+ mSurfaceFlingerPredictions = *predictions;
+ }
+ mSurfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::DisplayFrame::setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod) {
+ mToken = token;
+ mVsyncPeriod = vsyncPeriod;
+}
+
+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::onPresent(nsecs_t signalTime) {
+ mSurfaceFlingerActuals.presentTime = signalTime;
+ int32_t totalJankReasons = JankType::None;
+
+ // Delta between the expected present and the actual present
+ const nsecs_t presentDelta =
+ mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+ // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
+ // was a prediction error or not.
+ nsecs_t deltaToVsync = std::abs(presentDelta) % mVsyncPeriod;
+ if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+ : FramePresentMetadata::EarlyPresent;
+ } else {
+ mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+ }
+
+ if (mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime >
+ mJankClassificationThresholds.deadlineThreshold) {
+ 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 >=
+ (mVsyncPeriod - 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 (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present late
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >=
+ (mVsyncPeriod - 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) {
+ // Finish late, Present late
+ mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ } else {
+ // Finish time unknown
+ mJankType = JankType::Unknown;
+ }
+ } else {
+ // Present unknown
+ mJankType = JankType::Unknown;
+ }
+ }
+ totalJankReasons |= mJankType;
+
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->onPresent(signalTime, mJankType, mVsyncPeriod);
+ auto surfaceFrameJankType = surfaceFrame->getJankType();
+ if (surfaceFrameJankType != std::nullopt) {
+ totalJankReasons |= *surfaceFrameJankType;
+ }
+ }
+ mTimeStats->incrementJankyFrames(totalJankReasons);
+}
+
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+ ALOGD("Cannot trace DisplayFrame with invalid token");
+ return;
+ }
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* displayFrameEvent = event->set_display_frame();
+
+ displayFrameEvent->set_token(mToken);
+ displayFrameEvent->set_present_type(toProto(mFramePresentMetadata));
+ displayFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ displayFrameEvent->set_gpu_composition(mGpuComposition);
+ displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+
+ displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime);
+ displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime);
+
+ displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime);
+ displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime);
+
+ displayFrameEvent->set_pid(surfaceFlingerPid);
+ });
+
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->trace(mToken);
+ }
+}
+
void FrameTimeline::flushPendingPresentFences() {
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
@@ -456,93 +769,9 @@
}
}
if (signalTime != Fence::SIGNAL_TIME_INVALID) {
- int32_t totalJankReasons = JankType::None;
auto& displayFrame = pendingPresentFence.second;
- displayFrame->surfaceFlingerActuals.presentTime = signalTime;
-
- // Jank Analysis for DisplayFrame
- const auto& sfActuals = displayFrame->surfaceFlingerActuals;
- const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
- if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
- displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
- ? LatePresent
- : EarlyPresent;
- }
- if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
- if (sfActuals.endTime > sfPredictions.endTime) {
- displayFrame->jankMetadata |= LateFinish;
- } else {
- displayFrame->jankMetadata |= EarlyFinish;
- }
-
- if ((displayFrame->jankMetadata & EarlyFinish) &&
- (displayFrame->jankMetadata & EarlyPresent)) {
- displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
- } else if ((displayFrame->jankMetadata & LateFinish) &&
- (displayFrame->jankMetadata & LatePresent)) {
- displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
- } else if (displayFrame->jankMetadata & EarlyPresent ||
- displayFrame->jankMetadata & LatePresent) {
- // Cases where SF finished early but frame was presented late and vice versa
- displayFrame->jankType = JankType::Display;
- }
- }
-
- if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
- displayFrame->jankMetadata |=
- sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
- }
-
- totalJankReasons |= displayFrame->jankType;
- traceDisplayFrame(*displayFrame);
-
- for (auto& surfaceFrame : displayFrame->surfaceFrames) {
- if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
- // Only presented SurfaceFrames need to be updated
- surfaceFrame->setActualPresentTime(signalTime);
-
- // Jank Analysis for SurfaceFrame
- const auto& predictionState = surfaceFrame->getPredictionState();
- if (predictionState == PredictionState::Expired) {
- // Jank analysis cannot be done on apps that don't use predictions
- surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
- } else if (predictionState == PredictionState::Valid) {
- const auto& actuals = surfaceFrame->getActuals();
- const auto& predictions = surfaceFrame->getPredictions();
- int32_t jankMetadata = 0;
- JankType jankType = JankType::None;
- if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
- jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
- : EarlyFinish;
- }
- if (std::abs(actuals.presentTime - predictions.presentTime) >
- kPresentThreshold) {
- jankMetadata |= actuals.presentTime > predictions.presentTime
- ? LatePresent
- : EarlyPresent;
- }
- if (jankMetadata & EarlyPresent) {
- jankType = JankType::SurfaceFlingerEarlyLatch;
- } else if (jankMetadata & LatePresent) {
- if (jankMetadata & EarlyFinish) {
- // TODO(b/169890654): Classify this properly
- jankType = JankType::Display;
- } else {
- jankType = JankType::AppDeadlineMissed;
- }
- }
-
- totalJankReasons |= jankType;
- mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
- surfaceFrame->getName(),
- jankType | displayFrame->jankType);
- surfaceFrame->setJankInfo(jankType, jankMetadata);
- }
- }
- surfaceFrame->traceSurfaceFrame(displayFrame->token);
- }
-
- mTimeStats->incrementJankyFrames(totalJankReasons);
+ displayFrame->onPresent(signalTime);
+ displayFrame->trace(mSurfaceFlingerPid);
}
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -557,16 +786,14 @@
}
mDisplayFrames.push_back(mCurrentDisplayFrame);
mCurrentDisplayFrame.reset();
- mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+ mCurrentDisplayFrame =
+ std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds);
}
-nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
- nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
- if (displayFrame->predictionState == PredictionState::Valid) {
- baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
- }
- baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
- for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+nsecs_t 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);
@@ -575,60 +802,79 @@
return baseTime;
}
-void FrameTimeline::dumpDisplayFrame(std::string& result,
- const std::shared_ptr<DisplayFrame>& displayFrame,
- nsecs_t baseTime) {
- if (displayFrame->jankType != JankType::None) {
+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(displayFrame->predictionState).c_str());
- StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
- StringAppendF(&result, "Jank Metadata: %s\n",
- jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
- dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
- "", displayFrame->predictionState, baseTime);
+ StringAppendF(&result, "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(mVsyncPeriod);
+ 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) % mVsyncPeriod);
+ 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 : displayFrame->surfaceFrames) {
+ for (const auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->dump(result, indent, baseTime);
}
StringAppendF(&result, "\n");
}
+
void FrameTimeline::dumpAll(std::string& result) {
std::lock_guard<std::mutex> lock(mMutex);
StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
- nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ 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));
- dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+ mDisplayFrames[i]->dumpAll(result, baseTime);
}
}
void FrameTimeline::dumpJank(std::string& result) {
std::lock_guard<std::mutex> lock(mMutex);
- nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
for (size_t i = 0; i < mDisplayFrames.size(); i++) {
- const auto& displayFrame = mDisplayFrames[i];
- if (displayFrame->jankType == JankType::None) {
- // Check if any Surface Frame has been janky
- bool isJanky = false;
- for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
- if (surfaceFrame->getJankType() != JankType::None) {
- isJanky = true;
- break;
- }
- }
- if (!isJanky) {
- continue;
- }
- }
- StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
- dumpDisplayFrame(result, displayFrame, baseTime);
+ 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;
@@ -656,31 +902,5 @@
setMaxDisplayFrames(kDefaultMaxDisplayFrames);
}
-void FrameTimeline::traceDisplayFrame(const DisplayFrame& displayFrame) {
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- if (displayFrame.token == ISurfaceComposer::INVALID_VSYNC_ID) {
- ALOGD("Cannot trace DisplayFrame with invalid token");
- return;
- }
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(systemTime()));
-
- auto* event = packet->set_frame_timeline_event();
- auto* displayFrameEvent = event->set_display_frame();
-
- displayFrameEvent->set_token(displayFrame.token);
- displayFrameEvent->set_present_type(presentTypeToProto(displayFrame.jankMetadata));
- displayFrameEvent->set_on_time_finish(!(displayFrame.jankMetadata & LateFinish));
- displayFrameEvent->set_gpu_composition(displayFrame.jankMetadata & GpuComposition);
- displayFrameEvent->set_jank_type(JankTypeToProto(displayFrame.jankType));
-
- displayFrameEvent->set_expected_start_ns(displayFrame.surfaceFlingerPredictions.startTime);
- displayFrameEvent->set_expected_end_ns(displayFrame.surfaceFlingerPredictions.endTime);
-
- displayFrameEvent->set_actual_start_ns(displayFrame.surfaceFlingerActuals.startTime);
- displayFrameEvent->set_actual_end_ns(displayFrame.surfaceFlingerActuals.endTime);
- });
-}
-
-} // namespace android::frametimeline::impl
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 084935b..f5239aa 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -32,24 +32,44 @@
namespace android::frametimeline {
-enum JankMetadata {
- // Frame was presented earlier than expected
- EarlyPresent = 0x1,
- // Frame was presented later than expected
- LatePresent = 0x2,
- // App/SF started earlier than expected
- EarlyStart = 0x4,
- // App/SF started later than expected
- LateStart = 0x8,
- // App/SF finished work earlier than the deadline
- EarlyFinish = 0x10,
- // App/SF finished work later than the deadline
- LateFinish = 0x20,
- // SF was in GPU composition
- GpuComposition = 0x40,
+class FrameTimelineTest;
+
+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,
};
-class FrameTimelineTest;
+// 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.
@@ -71,6 +91,19 @@
bool operator!=(const TimelineItem& other) const { return !(*this == other); }
};
+struct TokenManagerPrediction {
+ nsecs_t timestamp = 0;
+ TimelineItem predictions;
+};
+
+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>(2ms).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,
@@ -83,6 +116,9 @@
// 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 {
@@ -91,10 +127,6 @@
None, // Predictions are either not present or didn't come from TokenManager
};
-/*
- * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame
- * from the app
- */
class SurfaceFrame {
public:
enum class PresentState {
@@ -103,29 +135,76 @@
Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet
};
- virtual ~SurfaceFrame() = default;
+ // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
+ // TokenManager), Thresholds and TimeStats pointer.
+ SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+ std::string debugName, PredictionState predictionState, TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+ ~SurfaceFrame() = default;
- virtual TimelineItem getPredictions() const = 0;
- virtual TimelineItem getActuals() const = 0;
- virtual nsecs_t getActualQueueTime() const = 0;
- virtual PresentState getPresentState() const = 0;
- virtual PredictionState getPredictionState() const = 0;
- virtual pid_t getOwnerPid() const = 0;
+ // Returns std::nullopt if the frame hasn't been classified yet.
+ // Used by both SF and FrameTimeline.
+ std::optional<int32_t> getJankType() const;
- virtual void setPresentState(PresentState state) = 0;
-
+ // Functions called by SF
+ int64_t getToken() const { return mToken; };
+ 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.
- virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
- virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
- virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+ void setActualStartTime(nsecs_t actualStartTime);
+ void setActualQueueTime(nsecs_t actualQueueTime);
+ void setAcquireFenceTime(nsecs_t acquireFenceTime);
+ void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
- // Retrieves jank classification, if it's already been classified.
- virtual std::optional<JankType> getJankType() const = 0;
+ // 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.
+ void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, nsecs_t vsyncPeriod);
+ // All the timestamps are dumped relative to the baseTime
+ void dump(std::string& result, const std::string& indent, nsecs_t baseTime) 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);
- // Token identifying the frame.
- virtual int64_t getToken() const = 0;
+ // Getter functions used only by FrameTimelineTests
+ TimelineItem getActuals() const;
+ pid_t getOwnerPid() const { return mOwnerPid; };
+ PredictionState getPredictionState() const { return mPredictionState; };
+ PresentState getPresentState() const;
+ FrameReadyMetadata getFrameReadyMetadata() const;
+ FramePresentMetadata getFramePresentMetadata() const;
+
+private:
+ const int64_t mToken;
+ const pid_t mOwnerPid;
+ const uid_t mOwnerUid;
+ const std::string mLayerName;
+ const std::string mDebugName;
+ 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;
+ 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;
+ // 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;
};
/*
@@ -142,20 +221,19 @@
virtual void onBootFinished() = 0;
// Create a new surface frame, set the predictions based on a token and return it to the caller.
- // Sets the PredictionState of SurfaceFrame.
// Debug name is the human-readable debugging string for dumpsys.
- virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
- pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
- std::optional<int64_t> token) = 0;
+ virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+ pid_t ownerPid, uid_t ownerUid,
+ std::string layerName,
+ std::string debugName) = 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,
- SurfaceFrame::PresentState state) = 0;
+ 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) = 0;
+ virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) = 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
@@ -178,15 +256,13 @@
namespace impl {
-using namespace std::chrono_literals;
-
class TokenManager : public android::frametimeline::TokenManager {
public:
TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
~TokenManager() = default;
int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
- std::optional<TimelineItem> getPredictionsForToken(int64_t token);
+ std::optional<TimelineItem> getPredictionsForToken(int64_t token) const override;
private:
// Friend class for testing
@@ -194,64 +270,13 @@
void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
- std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
- std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex);
+ std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
int64_t mCurrentToken GUARDED_BY(mMutex);
- std::mutex mMutex;
+ mutable std::mutex mMutex;
static constexpr nsecs_t kMaxRetentionTime =
std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
};
-class SurfaceFrame : public android::frametimeline::SurfaceFrame {
-public:
- SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
- std::string debugName, PredictionState predictionState,
- TimelineItem&& predictions);
- ~SurfaceFrame() = default;
-
- TimelineItem getPredictions() const override { return mPredictions; };
- TimelineItem getActuals() const override;
- nsecs_t getActualQueueTime() const override;
- PresentState getPresentState() const override;
- PredictionState getPredictionState() const override { return mPredictionState; };
- pid_t getOwnerPid() const override { return mOwnerPid; };
- std::optional<JankType> getJankType() const override;
- int64_t getToken() const override { return mToken; };
- nsecs_t getBaseTime() const;
- uid_t getOwnerUid() const { return mOwnerUid; };
- const std::string& getName() const { return mLayerName; };
-
- void setActualStartTime(nsecs_t actualStartTime) override;
- void setActualQueueTime(nsecs_t actualQueueTime) override;
- void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
- void setPresentState(PresentState state) override;
- void setActualPresentTime(nsecs_t presentTime);
- void setJankInfo(JankType jankType, int32_t jankMetadata);
-
- // All the timestamps are dumped relative to the baseTime
- void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
-
- // 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 traceSurfaceFrame(int64_t displayFrameToken);
-
-private:
- const int64_t mToken;
- const pid_t mOwnerPid;
- const uid_t mOwnerUid;
- const std::string mLayerName;
- const std::string mDebugName;
- PresentState mPresentState GUARDED_BY(mMutex);
- const PredictionState mPredictionState;
- const TimelineItem mPredictions;
- TimelineItem mActuals GUARDED_BY(mMutex);
- nsecs_t mActualQueueTime GUARDED_BY(mMutex);
- mutable std::mutex mMutex;
- JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
- int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
-};
-
class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
@@ -260,16 +285,94 @@
void OnStop(const StopArgs&) override{};
};
- FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+ /*
+ * 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);
+ 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, nsecs_t vsyncPeriod, std::optional<TimelineItem> predictions,
+ nsecs_t wakeUpTime);
+ // Sets the appropriate metadata, classifies the jank and returns the classified jankType.
+ void onPresent(nsecs_t signalTime);
+ // Adds the provided SurfaceFrame to the current display frame.
+ void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
+
+ void setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod);
+ void setPredictions(PredictionState predictionState, TimelineItem predictions);
+ void setActualStartTime(nsecs_t actualStartTime);
+ void setActualEndTime(nsecs_t actualEndTime);
+
+ // 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; };
+ 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;
+
+ int64_t mToken = ISurfaceComposer::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;
+ // Indicates if this frame was composited by the GPU or not
+ bool mGpuComposition = false;
+ // 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
+ nsecs_t mVsyncPeriod = 0;
+ };
+
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+ JankClassificationThresholds thresholds = {});
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
- std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
- pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
- std::optional<int64_t> token) override;
- void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame,
- SurfaceFrame::PresentState state) override;
- void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
+ std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+ pid_t ownerPid, uid_t ownerUid,
+ std::string layerName,
+ std::string debugName) override;
+ void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
+ void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) override;
void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
@@ -288,67 +391,28 @@
// Friend class for testing
friend class android::frametimeline::FrameTimelineTest;
- /*
- * DisplayFrame should be used only internally within FrameTimeline.
- */
- struct DisplayFrame {
- DisplayFrame();
-
- int64_t token = ISurfaceComposer::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 surfaceFlingerPredictions;
- TimelineItem surfaceFlingerActuals;
-
- // Collection of predictions and actual values sent over by Layers
- std::vector<std::shared_ptr<SurfaceFrame>> surfaceFrames;
-
- PredictionState predictionState = PredictionState::None;
- JankType jankType = JankType::None; // Enum for the type of jank
- int32_t jankMetadata = 0x0; // Additional details about the jank
- };
-
void flushPendingPresentFences() REQUIRES(mMutex);
void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
- // BaseTime is the smallest timestamp in a DisplayFrame.
- // Used for dumping all timestamps relative to the oldest, making it easy to read.
- nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
- void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
- nsecs_t baseTime) REQUIRES(mMutex);
void dumpAll(std::string& result);
void dumpJank(std::string& result);
- // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
- // enabled.
- void traceDisplayFrame(const DisplayFrame& displayFrame) REQUIRES(mMutex);
-
// 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;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
+ const pid_t mSurfaceFlingerPid;
+ 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;
- // The various thresholds for App and SF. If the actual timestamp falls within the threshold
- // compared to prediction, we don't treat it as a jank.
- static constexpr nsecs_t kPresentThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
- static constexpr nsecs_t kDeadlineThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
- static constexpr nsecs_t kSFStartThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
};
} // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f53c4cc..4731f50 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -911,9 +911,8 @@
: std::make_optional(stateToCommit->frameTimelineVsyncId);
mSurfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
- mName, mTransactionName,
- vsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(vsyncId, mOwnerPid, mOwnerUid,
+ mName, mTransactionName);
mSurfaceFrame->setActualQueueTime(stateToCommit->postTime);
// For transactions we set the acquire fence time to the post time as we
// don't have a buffer. For BufferStateLayer it is overridden in
@@ -1066,7 +1065,8 @@
void Layer::commitTransaction(const State& stateToCommit) {
mDrawingState = stateToCommit;
- mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame, PresentState::Presented);
+ mSurfaceFrame->setPresentState(PresentState::Presented);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame);
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 17ef7ef..7ecfe53 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -339,7 +339,7 @@
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(mFactory.createFrameTracer()),
- mFrameTimeline(mFactory.createFrameTimeline(mTimeStats)),
+ mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -1872,7 +1872,7 @@
const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
- mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
+ mFrameTimeline->setSfWakeUp(vsyncId, frameStart, stats.vsyncPeriod);
refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
@@ -5366,8 +5366,8 @@
const auto refreshRate = data.readFloat();
mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
- }
return NO_ERROR;
+ }
}
}
return err;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 10846f1..4a75180 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -136,8 +136,8 @@
}
std::unique_ptr<frametimeline::FrameTimeline> DefaultFactory::createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) {
- return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats);
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) {
+ return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats, surfaceFlingerPid);
}
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index ea66676..24148dd 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -56,7 +56,7 @@
sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
std::unique_ptr<FrameTracer> createFrameTracer() override;
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) override;
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 5a0ea57..885297f 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -110,7 +110,7 @@
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) = 0;
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
protected:
~Factory() = default;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 2405884..f4a0319 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -680,19 +680,18 @@
static void updateJankPayload(T& t, int32_t reasons) {
t.jankPayload.totalFrames++;
- static const constexpr int32_t kValidJankyReason =
- JankType::SurfaceFlingerDeadlineMissed |
- JankType::SurfaceFlingerGpuDeadlineMissed |
- JankType::AppDeadlineMissed | JankType::Display;
+ static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+ JankType::DisplayHAL;
if (reasons & kValidJankyReason) {
t.jankPayload.totalJankyFrames++;
- if ((reasons & JankType::SurfaceFlingerDeadlineMissed) != 0) {
+ if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
t.jankPayload.totalSFLongCpu++;
}
if ((reasons & JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
t.jankPayload.totalSFLongGpu++;
}
- if ((reasons & JankType::Display) != 0) {
+ if ((reasons & JankType::DisplayHAL) != 0) {
t.jankPayload.totalSFUnattributed++;
}
if ((reasons & JankType::AppDeadlineMissed) != 0) {
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 43b5afe..4b897fa 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -26,6 +26,7 @@
#include <cinttypes>
using namespace std::chrono_literals;
+using testing::AtLeast;
using testing::Contains;
using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame;
@@ -62,7 +63,8 @@
void SetUp() override {
mTimeStats = std::make_shared<mock::TimeStats>();
- mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid,
+ kTestThresholds);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
@@ -110,7 +112,8 @@
SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
- return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]);
+ return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]
+ ->getSurfaceFrames()[surfaceFrameIdx]);
}
std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
@@ -123,7 +126,7 @@
a.presentTime == b.presentTime;
}
- const std::unordered_map<int64_t, TimelineItem>& getPredictions() {
+ const std::map<int64_t, TokenManagerPrediction>& getPredictions() {
return mTokenManager->mPredictions;
}
@@ -138,6 +141,16 @@
FenceToFenceTimeMap fenceFactory;
uint32_t* maxDisplayFrames;
nsecs_t maxTokenRetentionTime;
+ pid_t mSurfaceFlingerPid = 666;
+ static constexpr nsecs_t kPresentThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+ static constexpr nsecs_t kDeadlineThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+ static constexpr nsecs_t kStartThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+ static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
+ kDeadlineThreshold,
+ kStartThreshold};
};
static const std::string sLayerNameOne = "layer1";
@@ -162,55 +175,58 @@
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidTwo, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
flushTokens(systemTime() + maxTokenRetentionTime);
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(surfaceFrame1, SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfWakeUp(token1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flush by calling setSfPresent for the next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
@@ -219,48 +235,50 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
auto surfaceFrame2 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
- sLayerNameTwo, surfaceFrameToken2);
- mFrameTimeline->setSfWakeUp(sfToken1, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
- mFrameTimeline->addSurfaceFrame(surfaceFrame2,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameTwo, sLayerNameTwo);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
- SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
- SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
presentFence1->signalForTest(42);
// Fences haven't been flushed yet, so it should be 0
- EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
- EXPECT_EQ(surfaceFrame1->getToken(), surfaceFrameToken1);
- EXPECT_EQ(surfaceFrame2->getToken(), surfaceFrameToken2);
-
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame3 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken2);
- mFrameTimeline->setSfWakeUp(sfToken2, 52);
- mFrameTimeline->addSurfaceFrame(surfaceFrame3, SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3);
mFrameTimeline->setSfPresent(56, presentFence2);
displayFrame = getDisplayFrame(0);
// Fences have flushed, so the present timestamps should be updated
- EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
@@ -270,6 +288,12 @@
TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
// Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
int frameTimeFactor = 0;
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames));
for (size_t i = 0; i < *maxDisplayFrames; i++) {
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
@@ -277,11 +301,11 @@
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken);
- mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
presentFence->signalForTest(32 + frameTimeFactor);
frameTimeFactor += 30;
@@ -289,8 +313,7 @@
auto displayFrame0 = getDisplayFrame(0);
// The 0th Display Frame should have actuals 22, 27, 32
- EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)),
- true);
+ EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(22, 27, 32)), true);
// Add one more display frame
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -299,51 +322,54 @@
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken);
- mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
- mFrameTimeline->addSurfaceFrame(surfaceFrame, SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
presentFence->signalForTest(32 + frameTimeFactor);
displayFrame0 = getDisplayFrame(0);
// The window should have slided by 1 now and the previous 0th display frame
// should have been removed from the deque
- EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)),
- true);
+ EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue", std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue", std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
presentFence->signalForTest(2);
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -351,15 +377,18 @@
// Increase the size to 256
mFrameTimeline->setMaxDisplayFrames(256);
EXPECT_EQ(*maxDisplayFrames, 256);
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -367,26 +396,30 @@
// Shrink the size to 128
mFrameTimeline->setMaxDisplayFrames(128);
EXPECT_EQ(*maxDisplayFrames, 128);
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
}
+// Tests related to TimeStats
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(sUidOne, sLayerNameOne,
- HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+ HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+ incrementJankyFrames(HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
{std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -397,12 +430,13 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1,
- std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(70ms).count());
@@ -412,8 +446,9 @@
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::Display)));
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::Display)));
+ incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::DisplayHAL)));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::DisplayHAL)));
+
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
{std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -424,18 +459,18 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1,
- std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ 30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
presentFence1);
- EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
- EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::Display) != 0);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -449,26 +484,26 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(82ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(
std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
mFrameTimeline->setSfWakeUp(sfToken1,
- std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ 11);
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
presentFence1);
- EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
- EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::AppDeadlineMissed) != 0);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
}
/*
@@ -481,23 +516,26 @@
*/
TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfWakeUp(token1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
@@ -506,25 +544,29 @@
TEST_F(FrameTimelineTest, tracing_sanityTest) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token2, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(token2, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
@@ -543,6 +585,8 @@
TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -550,13 +594,13 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
// Set up the display frame
- mFrameTimeline->setSfWakeUp(-1, 20);
+ mFrameTimeline->setSfWakeUp(-1, 20, 11);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token1, 50);
+ mFrameTimeline->setSfWakeUp(token1, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(60);
@@ -572,24 +616,27 @@
TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfWakeUp(token1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(60);
@@ -662,6 +709,8 @@
TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -670,7 +719,7 @@
int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
mFrameTimeline->setSfPresent(26, presentFence1);
presentFence1->signalForTest(31);
@@ -687,7 +736,7 @@
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+ mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
@@ -713,6 +762,10 @@
TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -722,8 +775,8 @@
int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
surfaceFrame1->setActualStartTime(0);
surfaceFrame1->setActualQueueTime(15);
surfaceFrame1->setAcquireFenceTime(20);
@@ -743,15 +796,15 @@
protoSurfaceFrame.set_pid(sPidOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(26, presentFence1);
- presentFence1->signalForTest(31);
+ presentFence1->signalForTest(40);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+ mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
@@ -775,4 +828,508 @@
validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame);
}
+// Tests for Jank classification
+TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ auto& presentedSurfaceFrame = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(29);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 29);
+ EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 29);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(30);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 30);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ auto displayFrame2 = getDisplayFrame(1);
+ presentFence2->signalForTest(65);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame2 = getDisplayFrame(1);
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ auto displayFrame2 = getDisplayFrame(1);
+ presentFence2->signalForTest(75);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame2 = getDisplayFrame(1);
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 75);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+
+ mFrameTimeline->setSfPresent(22, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(28);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 28);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(52);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(30);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 30);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 30);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::SurfaceFlingerScheduling);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(65);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 65);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 50);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::DisplayHAL);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(86);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 86);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 86);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 42, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(46, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 50);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::Unknown);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
+ // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+ // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
+ // jank to the SurfaceFrame.
+
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(26);
+ mFrameTimeline->setSfWakeUp(sfToken1, 32, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(40);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken2, 43, 11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 40);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 40);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(60);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 60);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 60);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 56, 60});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, 30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(60);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(84);
+ mFrameTimeline->setSfWakeUp(sfToken2, 112, 30);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(116, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+ presentFence2->signalForTest(120);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.endTime, 50);
+ EXPECT_EQ(actuals1.presentTime, 60);
+
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 120);
+
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing);
+}
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 53dfe3f..8208b3f 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -69,6 +69,7 @@
~MockTokenManager() override = default;
MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+ MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
};
class MessageQueueTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9d16782..c98004a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -156,8 +156,8 @@
}
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) override {
- return std::make_unique<mock::FrameTimeline>(timeStats);
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+ return std::make_unique<mock::FrameTimeline>(timeStats, surfaceFlingerPid);
}
using CreateBufferQueueFunction =
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index bbcc0c9..ace370f 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -356,9 +356,9 @@
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
- mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(JankType::Display);
+ mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::None);
@@ -383,10 +383,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerDeadlineMissed);
+ JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -848,10 +848,10 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerDeadlineMissed);
+ JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -987,9 +987,9 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(JankType::Display);
+ mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::None);
@@ -1062,10 +1062,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerDeadlineMissed);
+ JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
index f784df3..ff005a0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
@@ -19,8 +19,8 @@
namespace android::mock {
// Explicit default instantiation is recommended.
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
- : android::frametimeline::impl::FrameTimeline(timeStats) {}
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid)
+ : android::frametimeline::impl::FrameTimeline(timeStats, surfaceFlingerPid) {}
FrameTimeline::~FrameTimeline() = default;
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 6b12536..0a6a9f4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -26,14 +26,12 @@
// No need to create mocks for SurfaceFrame and TokenManager yet. They are very small components
// and do not have external dependencies like perfetto.
public:
- FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid);
~FrameTimeline();
MOCK_METHOD0(onBootFinished, void());
- MOCK_METHOD2(addSurfaceFrame,
- void(std::shared_ptr<frametimeline::SurfaceFrame>,
- frametimeline::SurfaceFrame::PresentState));
- MOCK_METHOD2(setSfWakeUp, void(int64_t, nsecs_t));
+ MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
+ MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, nsecs_t));
MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
};