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/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;