SF - plumbing game mode for metrics (part 2)
Update TimeStats to take in the game mode from layer for all the frames.
Bug: 186025682
Test: statsd_testdrive 10063
Test: atest libsurfaceflinger_unittest
Change-Id: If95a8c91940228a8925ae9e4e21656d1b492a2ba
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index d6a0787..f1b153f 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -58,6 +58,21 @@
return histogramProto;
}
+SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(int32_t gameMode) {
+ switch (gameMode) {
+ case TimeStatsHelper::GameModeUnsupported:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSUPPORTED;
+ case TimeStatsHelper::GameModeStandard:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD;
+ case TimeStatsHelper::GameModePerformance:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
+ case TimeStatsHelper::GameModeBattery:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
+ default:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
+ }
+}
+
SurfaceflingerStatsLayerInfo_SetFrameRateVote frameRateVoteToProto(
const TimeStats::SetFrameRateVote& setFrameRateVote) {
using FrameRateCompatibilityEnum =
@@ -206,6 +221,7 @@
*atom->mutable_app_deadline_misses() =
histogramToProto(layer->deltas["appDeadlineDeltas"].hist,
mMaxPulledHistogramBuckets);
+ atom->set_game_mode(gameModeToProto(layer->gameMode));
}
// Always clear data.
@@ -437,7 +453,8 @@
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) {
+ SetFrameRateVote frameRateVote,
+ int32_t gameMode) {
ATRACE_CALL();
ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
@@ -464,12 +481,13 @@
TimeStatsHelper::TimelineStats& displayStats = mTimeStats.stats[timelineKey];
- TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+ TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName, gameMode};
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;
+ displayStats.stats[layerKey].gameMode = gameMode;
}
if (frameRateVote.frameRate > 0.0f) {
displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
@@ -535,10 +553,11 @@
layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
}
-bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName,
+ int32_t gameMode) {
uint32_t layerRecords = 0;
for (const auto& record : mTimeStats.stats) {
- if (record.second.stats.count({uid, layerName}) > 0) {
+ if (record.second.stats.count({uid, layerName, gameMode}) > 0) {
return true;
}
@@ -549,7 +568,7 @@
}
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime) {
+ uid_t uid, nsecs_t postTime, int32_t gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -557,13 +576,14 @@
postTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!canAddNewAggregatedStats(uid, layerName)) {
+ if (!canAddNewAggregatedStats(uid, layerName, gameMode)) {
return;
}
if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
layerNameIsValid(layerName)) {
mTimeStatsTracker[layerId].uid = uid;
mTimeStatsTracker[layerId].layerName = layerName;
+ mTimeStatsTracker[layerId].gameMode = gameMode;
}
if (!mTimeStatsTracker.count(layerId)) return;
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
@@ -698,7 +718,7 @@
void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) {
+ SetFrameRateVote frameRateVote, int32_t gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -717,13 +737,14 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote,
+ gameMode);
}
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) {
+ SetFrameRateVote frameRateVote, int32_t gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -743,7 +764,8 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote,
+ gameMode);
}
static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
@@ -801,6 +823,7 @@
// the first jank record is not dropped.
static const std::string kDefaultLayerName = "none";
+ static constexpr int32_t kDefaultGameMode = TimeStatsHelper::GameModeUnsupported;
const int32_t refreshRateBucket =
clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -817,13 +840,14 @@
updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, info.reasons);
- TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName};
+ TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName, info.gameMode};
if (!timelineStats.stats.count(layerKey)) {
- layerKey = {info.uid, kDefaultLayerName};
+ layerKey = {info.uid, kDefaultLayerName, kDefaultGameMode};
timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
timelineStats.stats[layerKey].uid = info.uid;
- timelineStats.stats[layerKey].layerName = kDefaultLayerName;
+ timelineStats.stats[layerKey].layerName = kDefaultGameMode;
+ timelineStats.stats[layerKey].gameMode = info.gameMode;
}
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5b0f5bd..dd48950 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -87,7 +87,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime) = 0;
+ uid_t uid, nsecs_t postTime, int32_t gameMode) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -109,11 +109,11 @@
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) = 0;
+ SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) = 0;
+ SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -131,6 +131,7 @@
std::optional<Fps> renderRate;
uid_t uid = 0;
std::string layerName;
+ int32_t gameMode = 0;
int32_t reasons = 0;
nsecs_t displayDeadlineDelta = 0;
nsecs_t displayPresentJitter = 0;
@@ -141,8 +142,8 @@
((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
(renderRate != std::nullopt && o.renderRate != std::nullopt &&
Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
- uid == o.uid && layerName == o.layerName && reasons == o.reasons &&
- displayDeadlineDelta == o.displayDeadlineDelta &&
+ uid == o.uid && layerName == o.layerName && gameMode == o.gameMode &&
+ reasons == o.reasons && displayDeadlineDelta == o.displayDeadlineDelta &&
displayPresentJitter == o.displayPresentJitter &&
appDeadlineDelta == o.appDeadlineDelta;
}
@@ -199,6 +200,7 @@
struct LayerRecord {
uid_t uid;
std::string layerName;
+ int32_t gameMode = 0;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
@@ -251,7 +253,7 @@
const std::shared_ptr<FenceTime>& readyFence) override;
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
- nsecs_t postTime) override;
+ nsecs_t postTime, int32_t gameMode) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
void incrementBadDesiredPresent(int32_t layerId) override;
@@ -261,10 +263,11 @@
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote) override;
+ SetFrameRateVote frameRateVote, int32_t gameMode) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
- std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
+ std::optional<Fps> renderRate, SetFrameRateVote frameRateVote,
+ int32_t gameMode) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
@@ -286,10 +289,10 @@
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote);
+ SetFrameRateVote frameRateVote, int32_t gameMode);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
- bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode);
void enable();
void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index 133a541..e45757d 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -166,6 +166,23 @@
// This is intended to be used as a dimension in collecting per-render rate
// jank statistics.
optional int32 render_rate_bucket = 23;
+
+ enum GameMode {
+ GAME_MODE_UNSPECIFIED = 0;
+ GAME_MODE_UNSUPPORTED = 1;
+ GAME_MODE_STANDARD = 2;
+ GAME_MODE_PERFORMANCE = 3;
+ GAME_MODE_BATTERY = 4;
+ }
+
+ // Game mode that the layer was running at. Used to track user engagement
+ // in different modes. The modes are defined in GameManager.java
+ // Game modes are used only for integrating with GameManager. All non-game
+ // layers will have this field set to UNSUPPORTED.
+ // Introduced in Android 12
+ // This is intended to be used as a dimension in collecting per-game mode
+ // fps and frame related metrics.
+ optional GameMode game_mode = 26;
// The layer for this set of metrics
// In many scenarios the package name is included in the layer name, e.g.,
// layers created by Window Manager. But this is not a guarantee - in the
@@ -271,7 +288,7 @@
// Introduced in Android 12.
optional FrameTimingHistogram app_deadline_misses = 25;
- // Next ID: 26
+ // Next ID: 27
}
/**
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index a7e7db2..ffb2f09 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -122,6 +122,20 @@
return result;
}
+std::string TimeStatsHelper::TimeStatsLayer::toString(int32_t gameMode) const {
+ switch (gameMode) {
+ case TimeStatsHelper::GameModeUnsupported:
+ return "GameModeUnsupported";
+ case TimeStatsHelper::GameModeStandard:
+ return "GameModeStandard";
+ case TimeStatsHelper::GameModePerformance:
+ return "GameModePerformance";
+ case TimeStatsHelper::GameModeBattery:
+ return "GameModeBattery";
+ default:
+ return "GameModeUnspecified";
+ }
+}
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -129,6 +143,7 @@
StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
+ StringAppendF(&result, "gameMode = %s\n", toString(gameMode).c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 2b37ffe..2afff8d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -77,6 +77,18 @@
std::string toString() const;
};
+ /**
+ * GameMode of the layer. GameModes are set by SysUI through WMShell.
+ * Actual game mode definitions are managed by GameManager.java
+ * The values defined here should always be in sync with the ones in GameManager.
+ */
+ enum GameMode {
+ GameModeUnsupported = 0,
+ GameModeStandard = 1,
+ GameModePerformance = 2,
+ GameModeBattery = 3,
+ };
+
class TimeStatsLayer {
public:
uid_t uid;
@@ -84,6 +96,7 @@
std::string packageName;
int32_t displayRefreshRateBucket = 0;
int32_t renderRateBucket = 0;
+ int32_t gameMode = 0;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
@@ -93,6 +106,7 @@
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
+ std::string toString(int32_t gameMode) const;
SFTimeStatsLayerProto toProto() const;
};
@@ -123,24 +137,19 @@
struct LayerStatsKey {
uid_t uid = 0;
std::string layerName;
+ int32_t gameMode = 0;
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));
+ size_t uidHash = std::hash<uid_t>{}(key.uid);
+ size_t layerNameHash = std::hash<std::string>{}(key.layerName);
+ size_t gameModeHash = std::hash<int32_t>{}(key.gameMode);
+ return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
}
};
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);
+ return uid == o.uid && layerName == o.layerName && gameMode == o.gameMode;
}
};