Plumb refresh and render rates into shared timeline
* Make the overridden frame rate from the Scheduler public so that
SurfaceFlinger can access it for each uid.
* Add the display refresh rate on each display frame
* Add the application rendering rate on each SurfaceFrame created
* If there is no application rendering rate, then set it to the display
refresh rate.
* Plumb all those metrics into TimeStats.
* Change global metrics to increment for every SurfaceFrame instead of
every DisplayFrame, so that the rendering rate dimension can be
accurately captured.
Bug: 172937287
Test: builds, boots, timestats dump
Change-Id: Icfd4cecfdfa5d6c434661cab91c624eb08e8baea
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 5d387d6..c3f3671 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -105,53 +105,63 @@
AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mTimeStats.statsStart == 0) {
+ if (mTimeStats.statsStartLegacy == 0) {
return AStatsManager_PULL_SKIP;
}
flushPowerTimeLocked();
- AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
- mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime);
- mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime());
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCount);
- std::string frameDurationBytes =
- histogramToProtoByteString(mTimeStats.frameDuration.hist, mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
- frameDurationBytes.size());
- std::string renderEngineTimingBytes =
- histogramToProtoByteString(mTimeStats.renderEngineTiming.hist,
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
- renderEngineTimingBytes.size());
+ for (const auto& globalSlice : mTimeStats.stats) {
+ AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
+ mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFramesLegacy);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFramesLegacy);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFramesLegacy);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTimeLegacy);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresentLegacy.totalTime());
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCountLegacy);
+ std::string frameDurationBytes =
+ histogramToProtoByteString(mTimeStats.frameDurationLegacy.hist,
+ mMaxPulledHistogramBuckets);
+ mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
+ frameDurationBytes.size());
+ std::string renderEngineTimingBytes =
+ histogramToProtoByteString(mTimeStats.renderEngineTimingLegacy.hist,
+ mMaxPulledHistogramBuckets);
+ mStatsDelegate->statsEventWriteByteArray(event,
+ (const uint8_t*)renderEngineTimingBytes.c_str(),
+ renderEngineTimingBytes.size());
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames);
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames);
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu);
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu);
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed);
- mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed);
+ mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalFrames);
+ mStatsDelegate->statsEventWriteInt32(event,
+ globalSlice.second.jankPayload.totalJankyFrames);
+ mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongCpu);
+ mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongGpu);
+ mStatsDelegate->statsEventWriteInt32(event,
+ globalSlice.second.jankPayload.totalSFUnattributed);
+ mStatsDelegate->statsEventWriteInt32(event,
+ globalSlice.second.jankPayload.totalAppUnattributed);
- // TODO: populate these with real values
- mStatsDelegate->statsEventWriteInt32(event, 0); // total_janky_frames_sf_scheduling
- mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_sf_prediction_error
- mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing
- mStatsDelegate->statsEventWriteInt32(event, 0); // display_refresh_rate_bucket
- std::string sfDeadlineMissedBytes =
- histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)sfDeadlineMissedBytes.c_str(),
- sfDeadlineMissedBytes.size()); // sf_deadline_misses
- std::string sfPredictionErrorBytes =
- histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
- mMaxPulledHistogramBuckets);
- mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)sfPredictionErrorBytes.c_str(),
- sfPredictionErrorBytes.size()); // sf_prediction_errors
- mStatsDelegate->statsEventWriteInt32(event, 0); // render_rate_bucket
- mStatsDelegate->statsEventBuild(event);
+ // TODO: populate these with real values
+ mStatsDelegate->statsEventWriteInt32(event, 0); // total_janky_frames_sf_scheduling
+ mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_sf_prediction_error
+ mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing
+ mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.displayRefreshRateBucket);
+ std::string sfDeadlineMissedBytes =
+ histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+ mMaxPulledHistogramBuckets);
+ mStatsDelegate
+ ->statsEventWriteByteArray(event, (const uint8_t*)sfDeadlineMissedBytes.c_str(),
+ sfDeadlineMissedBytes.size()); // sf_deadline_misses
+ std::string sfPredictionErrorBytes =
+ histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+ mMaxPulledHistogramBuckets);
+ mStatsDelegate
+ ->statsEventWriteByteArray(event, (const uint8_t*)sfPredictionErrorBytes.c_str(),
+ sfPredictionErrorBytes.size()); // sf_prediction_errors
+ mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.renderRateBucket);
+ mStatsDelegate->statsEventBuild(event);
+ }
+
clearGlobalLocked();
return AStatsManager_PULL_SUCCESS;
@@ -161,8 +171,17 @@
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
- for (const auto& ele : mTimeStats.stats) {
- dumpStats.push_back(&ele.second);
+ uint32_t numLayers = 0;
+ for (const auto& globalSlice : mTimeStats.stats) {
+ numLayers += globalSlice.second.stats.size();
+ }
+
+ dumpStats.reserve(numLayers);
+
+ for (const auto& globalSlice : mTimeStats.stats) {
+ for (const auto& layerSlice : globalSlice.second.stats) {
+ dumpStats.push_back(&layerSlice.second);
+ }
}
std::sort(dumpStats.begin(), dumpStats.end(),
@@ -208,8 +227,9 @@
mStatsDelegate->statsEventWriteInt32(event, 0); // total_janky_frames_sf_scheduling
mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_sf_prediction_error
mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing
- mStatsDelegate->statsEventWriteInt32(event, 0); // display_refresh_rate_bucket
- mStatsDelegate->statsEventWriteInt32(event, 0); // render_rate_bucket
+ mStatsDelegate->statsEventWriteInt32(
+ event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
+ mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0);
mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
frameRateVoteBytes.size()); // set_frame_rate_vote
@@ -310,7 +330,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.totalFrames++;
+ mTimeStats.totalFramesLegacy++;
}
void TimeStats::incrementMissedFrames() {
@@ -319,7 +339,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.missedFrames++;
+ mTimeStats.missedFramesLegacy++;
}
void TimeStats::incrementClientCompositionFrames() {
@@ -328,7 +348,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.clientCompositionFrames++;
+ mTimeStats.clientCompositionFramesLegacy++;
}
void TimeStats::incrementClientCompositionReusedFrames() {
@@ -337,7 +357,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.clientCompositionReusedFrames++;
+ mTimeStats.clientCompositionReusedFramesLegacy++;
}
void TimeStats::incrementRefreshRateSwitches() {
@@ -346,7 +366,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.refreshRateSwitches++;
+ mTimeStats.refreshRateSwitchesLegacy++;
}
void TimeStats::incrementCompositionStrategyChanges() {
@@ -355,7 +375,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.compositionStrategyChanges++;
+ mTimeStats.compositionStrategyChangesLegacy++;
}
void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
@@ -364,8 +384,8 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStats.displayEventConnectionsCount =
- std::max(mTimeStats.displayEventConnectionsCount, count);
+ mTimeStats.displayEventConnectionsCountLegacy =
+ std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
}
static int32_t msBetween(nsecs_t start, nsecs_t end) {
@@ -381,7 +401,7 @@
std::lock_guard<std::mutex> lock(mMutex);
if (mPowerTime.powerMode == PowerMode::ON) {
- mTimeStats.frameDuration.insert(msBetween(startTime, endTime));
+ mTimeStats.frameDurationLegacy.insert(msBetween(startTime, endTime));
}
}
@@ -444,12 +464,22 @@
return true;
}
-void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
+static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) {
+ return (fps.getIntValue() / bucketWidth) * bucketWidth;
+}
+
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
+ std::optional<Fps> renderRate) {
ATRACE_CALL();
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
+ const int32_t refreshRateBucket =
+ clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ const int32_t renderRateBucket =
+ clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
while (!timeRecords.empty()) {
if (!recordReadyLocked(layerId, &timeRecords[0])) break;
ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
@@ -458,11 +488,21 @@
if (prevTimeRecord.ready) {
uid_t uid = layerRecord.uid;
const std::string& layerName = layerRecord.layerName;
- if (!mTimeStats.stats.count({uid, layerName})) {
- mTimeStats.stats[{uid, layerName}].uid = uid;
- mTimeStats.stats[{uid, layerName}].layerName = layerName;
+ TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
+ if (!mTimeStats.stats.count(timelineKey)) {
+ mTimeStats.stats[timelineKey].key = timelineKey;
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
+
+ TimeStatsHelper::TimelineStats& displayStats = mTimeStats.stats[timelineKey];
+
+ TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+ if (!displayStats.stats.count(layerKey)) {
+ displayStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
+ displayStats.stats[layerKey].renderRateBucket = renderRateBucket;
+ displayStats.stats[layerKey].uid = uid;
+ displayStats.stats[layerKey].layerName = layerName;
+ }
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
@@ -524,8 +564,16 @@
}
bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
- return mTimeStats.stats.count({uid, layerName}) > 0 ||
- mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+ uint32_t layerRecords = 0;
+ for (const auto& record : mTimeStats.stats) {
+ if (record.second.stats.count({uid, layerName}) > 0) {
+ return true;
+ }
+
+ layerRecords += record.second.stats.size();
+ }
+
+ return mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
}
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
@@ -676,7 +724,8 @@
}
}
-void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
+ Fps displayRefreshRate, std::optional<Fps> renderRate) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -695,11 +744,12 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
}
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence) {
+ const std::shared_ptr<FenceTime>& presentFence,
+ Fps displayRefreshRate, std::optional<Fps> renderRate) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -719,7 +769,7 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
}
template <class T>
@@ -746,16 +796,8 @@
}
}
-void TimeStats::incrementJankyFrames(int32_t reasons) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mMutex);
-
- updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons);
-}
-
-void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) {
+void TimeStats::incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
+ const std::string& layerName, int32_t reasons) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -772,16 +814,31 @@
// TimeStats will flush the first present fence for a layer *before* FrameTimeline does so that
// the first jank record is not dropped.
- bool useDefaultLayerKey = false;
static const std::string kDefaultLayerName = "none";
- if (!mTimeStats.stats.count({uid, layerName})) {
- mTimeStats.stats[{uid, kDefaultLayerName}].uid = uid;
- mTimeStats.stats[{uid, kDefaultLayerName}].layerName = kDefaultLayerName;
- useDefaultLayerKey = true;
+
+ const int32_t refreshRateBucket = clampToSmallestBucket(refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ const int32_t renderRateBucket =
+ clampToSmallestBucket(renderRate ? *renderRate : refreshRate, RENDER_RATE_BUCKET_WIDTH);
+ const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
+
+ if (!mTimeStats.stats.count(timelineKey)) {
+ mTimeStats.stats[timelineKey].key = timelineKey;
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer =
- mTimeStats.stats[{uid, useDefaultLayerKey ? kDefaultLayerName : layerName}];
+ TimeStatsHelper::TimelineStats& timelineStats = mTimeStats.stats[timelineKey];
+
+ updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, reasons);
+
+ TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+ if (!timelineStats.stats.count(layerKey)) {
+ layerKey = {uid, kDefaultLayerName};
+ timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
+ timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
+ timelineStats.stats[layerKey].uid = uid;
+ timelineStats.stats[layerKey].layerName = kDefaultLayerName;
+ }
+
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
}
@@ -823,7 +880,7 @@
switch (mPowerTime.powerMode) {
case PowerMode::ON:
- mTimeStats.displayOnTime += elapsedTime;
+ mTimeStats.displayOnTimeLegacy += elapsedTime;
break;
case PowerMode::OFF:
case PowerMode::DOZE:
@@ -852,10 +909,10 @@
void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mTimeStats.refreshRateStats.count(fps)) {
- mTimeStats.refreshRateStats[fps] += duration;
+ if (mTimeStats.refreshRateStatsLegacy.count(fps)) {
+ mTimeStats.refreshRateStatsLegacy[fps] += duration;
} else {
- mTimeStats.refreshRateStats.insert({fps, duration});
+ mTimeStats.refreshRateStatsLegacy.insert({fps, duration});
}
}
@@ -881,7 +938,7 @@
msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
ALOGV("Global present2present[%d] prev[%" PRId64 "] curr[%" PRId64 "]",
presentToPresentMs, mGlobalRecord.prevPresentTime, curPresentTime);
- mTimeStats.presentToPresent.insert(presentToPresentMs);
+ mTimeStats.presentToPresentLegacy.insert(presentToPresentMs);
}
mGlobalRecord.prevPresentTime = curPresentTime;
@@ -908,7 +965,7 @@
}
const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
- mTimeStats.renderEngineTiming.insert(renderEngineMs);
+ mTimeStats.renderEngineTimingLegacy.insert(renderEngineMs);
mGlobalRecord.renderEngineDurations.pop_front();
}
@@ -951,7 +1008,7 @@
std::lock_guard<std::mutex> lock(mMutex);
mEnabled.store(true);
- mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsStartLegacy = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
ALOGD("Enabled");
}
@@ -964,7 +1021,7 @@
std::lock_guard<std::mutex> lock(mMutex);
flushPowerTimeLocked();
mEnabled.store(false);
- mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEndLegacy = static_cast<int64_t>(std::time(0));
ALOGD("Disabled");
}
@@ -977,21 +1034,20 @@
void TimeStats::clearGlobalLocked() {
ATRACE_CALL();
- mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
- mTimeStats.statsEnd = 0;
- mTimeStats.totalFrames = 0;
- mTimeStats.missedFrames = 0;
- mTimeStats.clientCompositionFrames = 0;
- mTimeStats.clientCompositionReusedFrames = 0;
- mTimeStats.refreshRateSwitches = 0;
- mTimeStats.compositionStrategyChanges = 0;
- mTimeStats.displayEventConnectionsCount = 0;
- mTimeStats.displayOnTime = 0;
- mTimeStats.presentToPresent.hist.clear();
- mTimeStats.frameDuration.hist.clear();
- mTimeStats.renderEngineTiming.hist.clear();
- mTimeStats.jankPayload = TimeStatsHelper::JankPayload();
- mTimeStats.refreshRateStats.clear();
+ mTimeStats.statsStartLegacy = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+ mTimeStats.statsEndLegacy = 0;
+ mTimeStats.totalFramesLegacy = 0;
+ mTimeStats.missedFramesLegacy = 0;
+ mTimeStats.clientCompositionFramesLegacy = 0;
+ mTimeStats.clientCompositionReusedFramesLegacy = 0;
+ mTimeStats.refreshRateSwitchesLegacy = 0;
+ mTimeStats.compositionStrategyChangesLegacy = 0;
+ mTimeStats.displayEventConnectionsCountLegacy = 0;
+ mTimeStats.displayOnTimeLegacy = 0;
+ mTimeStats.presentToPresentLegacy.hist.clear();
+ mTimeStats.frameDurationLegacy.hist.clear();
+ mTimeStats.renderEngineTimingLegacy.hist.clear();
+ mTimeStats.refreshRateStatsLegacy.clear();
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
mGlobalRecord.presentFences.clear();
@@ -1014,11 +1070,11 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- if (mTimeStats.statsStart == 0) {
+ if (mTimeStats.statsStartLegacy == 0) {
return;
}
- mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEndLegacy = static_cast<int64_t>(std::time(0));
flushPowerTimeLocked();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index df40ef6..e76849f 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -27,12 +27,13 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <../Fps.h>
+#include <gui/JankInfo.h>
#include <stats_event.h>
#include <stats_pull_atom_callback.h>
#include <statslog.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
-#include <gui/JankInfo.h>
#include <ui/FenceTime.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -108,23 +109,22 @@
const std::shared_ptr<FenceTime>& acquireFence) = 0;
// SetPresent{Time, Fence} are not expected to be called in the critical
// rendering path, as they flush prior fences if those fences have fired.
- virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
+ virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
+ Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence) = 0;
+ const std::shared_ptr<FenceTime>& presentFence,
+ Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
- // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure
- // responsible for computing jank in the system, this is expected to be called from
- // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no
- // jank reasons, then total frames are incremented but jank is not, for accurate accounting of
- // janky frames.
- virtual void incrementJankyFrames(int32_t reasons) = 0;
- // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as
- // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible
- // for computing jank in the system, this is expected to be called from FrameTimeline, rather
- // than directly from SurfaceFlinger or individual layers.
- // If there are no jank reasons, then total frames are incremented but jank is not, for accurate
+ // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
+ // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
+ // infrastructure responsible for computing jank in the system, this is expected to be called
+ // from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there
+ // are no jank reasons, then total frames are incremented but jank is not, for accurate
// accounting of janky frames.
- virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0;
+ // When these frame counts are incremented, these are also aggregated into a global reporting
+ // packet to help with data validation and assessing of overall device health.
+ virtual void incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
+ const std::string& layerName, int32_t reasons) = 0;
// Clean up the layer record
virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -268,11 +268,13 @@
void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
- void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
+ void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
+ Fps displayRefreshRate, std::optional<Fps> renderRate) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence) override;
- void incrementJankyFrames(int32_t reasons) override;
- void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override;
+ const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
+ std::optional<Fps> renderRate) override;
+ void incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
+ const std::string& layerName, int32_t reasons) override;
// Clean up the layer record
void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -293,7 +295,8 @@
AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
- void flushAvailableRecordsToStatsLocked(int32_t layerId);
+ void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
+ std::optional<Fps> renderRate);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
@@ -314,6 +317,9 @@
GlobalRecord mGlobalRecord;
static const size_t MAX_NUM_LAYER_RECORDS = 200;
+
+ static const size_t REFRESH_RATE_BUCKET_WIDTH = 30;
+ static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH;
static const size_t MAX_NUM_LAYER_STATS = 200;
static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS;
std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 0fb748f..df7be1f 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -90,6 +90,8 @@
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
+ StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
+ StringAppendF(&result, "renderRate = %d fps\n", renderRateBucket);
StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
@@ -115,35 +117,45 @@
std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const {
std::string result = "SurfaceFlinger TimeStats:\n";
- StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart);
- StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd);
- StringAppendF(&result, "totalFrames = %d\n", totalFrames);
- StringAppendF(&result, "missedFrames = %d\n", missedFrames);
- StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
- StringAppendF(&result, "clientCompositionReusedFrames = %d\n", clientCompositionReusedFrames);
- StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
- StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
- StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
- result.append("Global aggregated jank payload:\n");
- result.append(jankPayload.toString());
+ result.append("Legacy stats are as follows:\n");
+ StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStartLegacy);
+ StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEndLegacy);
+ StringAppendF(&result, "totalFrames = %d\n", totalFramesLegacy);
+ StringAppendF(&result, "missedFrames = %d\n", missedFramesLegacy);
+ StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFramesLegacy);
+ StringAppendF(&result, "clientCompositionReusedFrames = %d\n",
+ clientCompositionReusedFramesLegacy);
+ StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitchesLegacy);
+ StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChangesLegacy);
+ StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTimeLegacy);
StringAppendF(&result, "displayConfigStats is as below:\n");
- for (const auto& [fps, duration] : refreshRateStats) {
+ for (const auto& [fps, duration] : refreshRateStatsLegacy) {
StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
}
result.back() = '\n';
- StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
+ StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresentLegacy.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
- result.append(presentToPresent.toString());
- const float averageFrameDuration = frameDuration.averageTime();
+ result.append(presentToPresentLegacy.toString());
+ const float averageFrameDuration = frameDurationLegacy.averageTime();
StringAppendF(&result, "averageFrameDuration = %.3f ms\n",
std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration);
StringAppendF(&result, "frameDuration histogram is as below:\n");
- result.append(frameDuration.toString());
- const float averageRenderEngineTiming = renderEngineTiming.averageTime();
+ result.append(frameDurationLegacy.toString());
+ const float averageRenderEngineTiming = renderEngineTimingLegacy.averageTime();
StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n",
std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming);
StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
- result.append(renderEngineTiming.toString());
+ result.append(renderEngineTimingLegacy.toString());
+
+ result.append("\nGlobal aggregated jank payload (Timeline stats):");
+ for (const auto& ele : stats) {
+ result.append("\n");
+ StringAppendF(&result, "displayRefreshRate = %d fps\n",
+ ele.second.key.displayRefreshRateBucket);
+ StringAppendF(&result, "renderRate = %d fps\n", ele.second.key.renderRateBucket);
+ result.append(ele.second.jankPayload.toString());
+ }
+
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
result.append(ele->toString());
@@ -173,30 +185,30 @@
SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto(
std::optional<uint32_t> maxLayers) const {
SFTimeStatsGlobalProto globalProto;
- globalProto.set_stats_start(statsStart);
- globalProto.set_stats_end(statsEnd);
- globalProto.set_total_frames(totalFrames);
- globalProto.set_missed_frames(missedFrames);
- globalProto.set_client_composition_frames(clientCompositionFrames);
- globalProto.set_display_on_time(displayOnTime);
- for (const auto& ele : refreshRateStats) {
+ globalProto.set_stats_start(statsStartLegacy);
+ globalProto.set_stats_end(statsEndLegacy);
+ globalProto.set_total_frames(totalFramesLegacy);
+ globalProto.set_missed_frames(missedFramesLegacy);
+ globalProto.set_client_composition_frames(clientCompositionFramesLegacy);
+ globalProto.set_display_on_time(displayOnTimeLegacy);
+ for (const auto& ele : refreshRateStatsLegacy) {
SFTimeStatsDisplayConfigBucketProto* configBucketProto =
globalProto.add_display_config_stats();
SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config();
configProto->set_fps(ele.first);
configBucketProto->set_duration_millis(ns2ms(ele.second));
}
- for (const auto& histEle : presentToPresent.hist) {
+ for (const auto& histEle : presentToPresentLegacy.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
- for (const auto& histEle : frameDuration.hist) {
+ for (const auto& histEle : frameDurationLegacy.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
- for (const auto& histEle : renderEngineTiming.hist) {
+ for (const auto& histEle : renderEngineTimingLegacy.hist) {
SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
@@ -212,8 +224,18 @@
std::vector<TimeStatsHelper::TimeStatsLayer const*>
TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
std::vector<TimeStatsLayer const*> dumpStats;
+
+ int numLayers = 0;
for (const auto& ele : stats) {
- dumpStats.push_back(&ele.second);
+ numLayers += ele.second.stats.size();
+ }
+
+ dumpStats.reserve(numLayers);
+
+ for (const auto& ele : stats) {
+ for (const auto& layerEle : ele.second.stats) {
+ dumpStats.push_back(&layerEle.second);
+ }
}
std::sort(dumpStats.begin(), dumpStats.end(),
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 033eb5d..0144abc0 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -57,6 +57,8 @@
uid_t uid;
std::string layerName;
std::string packageName;
+ int32_t displayRefreshRateBucket = 0;
+ int32_t renderRateBucket = 0;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
@@ -68,32 +70,82 @@
SFTimeStatsLayerProto toProto() const;
};
- class TimeStatsGlobal {
- public:
- int64_t statsStart = 0;
- int64_t statsEnd = 0;
- int32_t totalFrames = 0;
- int32_t missedFrames = 0;
- int32_t clientCompositionFrames = 0;
- int32_t clientCompositionReusedFrames = 0;
- int32_t refreshRateSwitches = 0;
- int32_t compositionStrategyChanges = 0;
- int32_t displayEventConnectionsCount = 0;
- int64_t displayOnTime = 0;
- Histogram presentToPresent;
- Histogram frameDuration;
- Histogram renderEngineTiming;
+ // Lifted from SkiaGLRenderEngine's LinearEffect class.
+ // Which in turn was inspired by art/runtime/class_linker.cc
+ // Also this is what boost:hash_combine does so this is a pretty good hash.
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
- struct StatsHasher {
- size_t operator()(const std::pair<uid_t, std::string>& p) const {
- // Normally this isn't a very good hash function due to symmetry reasons,
- // but these are distinct types so this should be good enough
- return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+ struct TimelineStatsKey {
+ int32_t displayRefreshRateBucket = 0;
+ int32_t renderRateBucket = 0;
+
+ struct Hasher {
+ size_t operator()(const TimelineStatsKey& key) const {
+ size_t result = std::hash<int32_t>{}(key.displayRefreshRateBucket);
+ return HashCombine(result, std::hash<int32_t>{}(key.renderRateBucket));
}
};
- std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats;
- std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
+
+ bool operator==(const TimelineStatsKey& o) const {
+ return displayRefreshRateBucket == o.displayRefreshRateBucket &&
+ renderRateBucket == o.renderRateBucket;
+ }
+ };
+
+ struct LayerStatsKey {
+ uid_t uid = 0;
+ std::string layerName;
+
+ struct Hasher {
+ size_t operator()(const LayerStatsKey& key) const {
+ size_t result = std::hash<uid_t>{}(key.uid);
+ return HashCombine(result, std::hash<std::string>{}(key.layerName));
+ }
+ };
+
+ bool operator==(const LayerStatsKey& o) const {
+ return uid == o.uid && layerName == o.layerName;
+ }
+ };
+
+ struct LayerStatsHasher {
+ size_t operator()(const std::pair<uid_t, std::string>& p) const {
+ // Normally this isn't a very good hash function due to symmetry reasons,
+ // but these are distinct types so this should be good enough
+ return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+ }
+ };
+
+ struct TimelineStats {
+ TimelineStatsKey key;
JankPayload jankPayload;
+ std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
+ };
+
+ class TimeStatsGlobal {
+ public:
+ // Note: these are all legacy statistics, we're keeping these around because a variety of
+ // systems and form-factors find these useful when comparing with older releases. However,
+ // the current recommendation is that the new timeline-based metrics are used, and the old
+ // ones are deprecated.
+ int64_t statsStartLegacy = 0;
+ int64_t statsEndLegacy = 0;
+ int32_t totalFramesLegacy = 0;
+ int32_t missedFramesLegacy = 0;
+ int32_t clientCompositionFramesLegacy = 0;
+ int32_t clientCompositionReusedFramesLegacy = 0;
+ int32_t refreshRateSwitchesLegacy = 0;
+ int32_t compositionStrategyChangesLegacy = 0;
+ int32_t displayEventConnectionsCountLegacy = 0;
+ int64_t displayOnTimeLegacy = 0;
+ Histogram presentToPresentLegacy;
+ Histogram frameDurationLegacy;
+ Histogram renderEngineTimingLegacy;
+ std::unordered_map<uint32_t, nsecs_t> refreshRateStatsLegacy;
+
+ std::unordered_map<TimelineStatsKey, TimelineStats, TimelineStatsKey::Hasher> stats;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;