SF TimeStats: add primary display on time metrics
Bug: b/79872109
Test: dumpsys SurfaceFlinger --timestats <options>
Change-Id: Iae949ce4648f2f6f4d38164d8e620d62970c0af1
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f0723e8..fa6ef25 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4135,6 +4135,10 @@
getHwComposer().setPowerMode(type, mode);
}
+ if (display->isPrimary()) {
+ mTimeStats.setPowerMode(mode);
+ }
+
ALOGD("Finished setting power mode %d on display %d", mode, displayId);
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 6fb70f8..c14d93b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -24,6 +24,7 @@
#include <log/log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -445,6 +446,39 @@
layerRecord.droppedFrames++;
}
+void TimeStats::flushPowerTimeLocked() {
+ nsecs_t curTime = systemTime();
+ // elapsedTime is in milliseconds.
+ int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000;
+
+ switch (mPowerTime.powerMode) {
+ case HWC_POWER_MODE_NORMAL:
+ mTimeStats.displayOnTime += elapsedTime;
+ break;
+ case HWC_POWER_MODE_OFF:
+ case HWC_POWER_MODE_DOZE:
+ case HWC_POWER_MODE_DOZE_SUSPEND:
+ default:
+ break;
+ }
+
+ mPowerTime.prevTime = curTime;
+}
+
+void TimeStats::setPowerMode(int32_t powerMode) {
+ if (!mEnabled.load()) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPowerTime.powerMode = powerMode;
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (powerMode == mPowerTime.powerMode) return;
+
+ flushPowerTimeLocked();
+ mPowerTime.powerMode = powerMode;
+}
+
void TimeStats::enable() {
if (mEnabled.load()) return;
@@ -454,6 +488,7 @@
ALOGD("Enabled");
mEnabled.store(true);
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mPowerTime.prevTime = systemTime();
}
void TimeStats::disable() {
@@ -478,6 +513,8 @@
mTimeStats.totalFrames = 0;
mTimeStats.missedFrames = 0;
mTimeStats.clientCompositionFrames = 0;
+ mTimeStats.displayOnTime = 0;
+ mPowerTime.prevTime = systemTime();
}
bool TimeStats::isEnabled() {
@@ -494,6 +531,8 @@
mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ flushPowerTimeLocked();
+
if (asProto) {
ALOGD("Dumping TimeStats as proto");
SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 6ae6b15..87e21a7 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -19,6 +19,8 @@
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
+#include <hardware/hwcomposer_defs.h>
+
#include <ui/FenceTime.h>
#include <utils/String16.h>
@@ -66,6 +68,11 @@
std::deque<TimeRecord> timeRecords;
};
+ struct PowerTime {
+ int32_t powerMode = HWC_POWER_MODE_OFF;
+ nsecs_t prevTime = 0;
+ };
+
public:
static TimeStats& getInstance();
void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
@@ -91,11 +98,14 @@
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
+ void setPowerMode(int32_t powerMode);
+
private:
TimeStats() = default;
bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(const std::string& layerName);
+ void flushPowerTimeLocked();
void enable();
void disable();
@@ -107,6 +117,7 @@
std::mutex mMutex;
TimeStatsHelper::TimeStatsGlobal mTimeStats;
std::unordered_map<std::string, LayerRecord> mTimeStatsTracker;
+ PowerTime mPowerTime;
};
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index b7b2778..ec41a62 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -49,7 +49,7 @@
float TimeStatsHelper::Histogram::averageTime() const {
int64_t ret = 0;
int64_t count = 0;
- for (auto& ele : hist) {
+ for (const auto& ele : hist) {
count += ele.second;
ret += ele.first * ele.second;
}
@@ -73,13 +73,13 @@
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
- auto iter = deltas.find("present2present");
+ const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
}
- for (auto& ele : deltas) {
+ for (const auto& ele : deltas) {
StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
- StringAppendF(&result, "%s", ele.second.toString().c_str());
+ result.append(ele.second.toString());
}
return result;
@@ -92,9 +92,10 @@
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "missedFrames = %d\n", missedFrames);
StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+ StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime));
const auto dumpStats = generateDumpStats(maxLayers);
- for (auto& ele : dumpStats) {
- StringAppendF(&result, "%s", ele->toString().c_str());
+ for (const auto& ele : dumpStats) {
+ result.append(ele->toString());
}
return result;
@@ -106,10 +107,10 @@
layerProto.set_package_name(packageName);
layerProto.set_total_frames(totalFrames);
layerProto.set_dropped_frames(droppedFrames);
- for (auto& ele : deltas) {
+ for (const auto& ele : deltas) {
SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas();
deltaProto->set_delta_name(ele.first);
- for (auto& histEle : ele.second.hist) {
+ for (const auto& histEle : ele.second.hist) {
SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
@@ -126,8 +127,9 @@
globalProto.set_total_frames(totalFrames);
globalProto.set_missed_frames(missedFrames);
globalProto.set_client_composition_frames(clientCompositionFrames);
+ globalProto.set_display_on_time(displayOnTime);
const auto dumpStats = generateDumpStats(maxLayers);
- for (auto& ele : dumpStats) {
+ for (const auto& ele : dumpStats) {
SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
layerProto->CopyFrom(ele->toProto());
}
@@ -137,7 +139,7 @@
std::vector<TimeStatsHelper::TimeStatsLayer const*>
TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
std::vector<TimeStatsLayer const*> dumpStats;
- for (auto& ele : stats) {
+ for (const auto& ele : stats) {
dumpStats.push_back(&ele.second);
}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 99c891b..939e137 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -57,6 +57,7 @@
int32_t totalFrames = 0;
int32_t missedFrames = 0;
int32_t clientCompositionFrames = 0;
+ int64_t displayOnTime = 0;
std::unordered_map<std::string, TimeStatsLayer> stats;
std::string toString(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index b8f38c2..f584496 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -20,13 +20,12 @@
option optimize_for = LITE_RUNTIME;
-// //depot/google3/java/com/google/android/apps/graphics/stats/proto/
+// //depot/google3/wireless/android/graphics/surfaceflingerstats/proto/
// timestats.proto is based on this proto. Please only make valid protobuf
-// changes to these messages, and keep the other file in sync per Android
-// release. Please also do not include "option optimize_for = LITE_RUNTIME;" at
-// google3 side.
+// changes to these messages, and keep google3 side proto messages in sync if
+// the end to end pipeline needs to be updated.
-// Next tag: 7
+// Next tag: 8
message SFTimeStatsGlobalProto {
// The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
@@ -38,6 +37,8 @@
optional int32 missed_frames = 4;
// Total frames fallback to client composition.
optional int32 client_composition_frames = 5;
+ // Primary display on time in milliseconds.
+ optional int64 display_on_time = 7;
// Stats per layer. Apps could have multiple layers.
repeated SFTimeStatsLayerProto stats = 6;
}