SF TimeStats: add global present to present histogram

Bug: b/79872109
Test: dumpsys SurfaceFlinger --timestats <options>
Change-Id: Iab8c01d66c69a04a89f7e9313e53ff961ce0175c
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fa6ef25..d3f3460 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1909,6 +1909,8 @@
         mTimeStats.incrementClientCompositionFrames();
     }
 
+    mTimeStats.setPresentFenceGlobal(presentFenceTime);
+
     if (display && getHwComposer().isConnected(display->getId()) &&
         display->getPowerMode() == HWC_POWER_MODE_OFF) {
         return;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c14d93b..f754d47 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -479,6 +479,54 @@
     mPowerTime.powerMode = powerMode;
 }
 
+void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
+    ATRACE_CALL();
+
+    while (!mGlobalRecord.presentFences.empty()) {
+        const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime();
+        if (curPresentTime == Fence::SIGNAL_TIME_PENDING) break;
+
+        if (curPresentTime == Fence::SIGNAL_TIME_INVALID) {
+            ALOGE("GlobalPresentFence is invalid!");
+            mGlobalRecord.prevPresentTime = 0;
+            mGlobalRecord.presentFences.pop_front();
+            continue;
+        }
+
+        ALOGV("GlobalPresentFenceTime[%" PRId64 "]",
+              mGlobalRecord.presentFences.front()->getSignalTime());
+
+        const int32_t presentToPresentMs = msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
+        ALOGV("Global present2present[%d]", presentToPresentMs);
+
+        mTimeStats.presentToPresent.insert(presentToPresentMs);
+        mGlobalRecord.prevPresentTime = curPresentTime;
+        mGlobalRecord.presentFences.pop_front();
+    }
+}
+
+void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (presentFence == nullptr) {
+        mGlobalRecord.prevPresentTime = 0;
+        return;
+    }
+
+    if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) {
+        // The front presentFence must be trapped in pending status in this
+        // case. Try dequeuing the front one to recover.
+        ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.prevPresentTime = 0;
+        mGlobalRecord.presentFences.pop_front();
+    }
+
+    mGlobalRecord.presentFences.emplace_back(presentFence);
+    flushAvailableGlobalRecordsToStatsLocked();
+}
+
 void TimeStats::enable() {
     if (mEnabled.load()) return;
 
@@ -514,6 +562,7 @@
     mTimeStats.missedFrames = 0;
     mTimeStats.clientCompositionFrames = 0;
     mTimeStats.displayOnTime = 0;
+    mTimeStats.presentToPresent.hist.clear();
     mPowerTime.prevTime = systemTime();
 }
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 87e21a7..c78d84e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -73,6 +73,11 @@
         nsecs_t prevTime = 0;
     };
 
+    struct GlobalRecord {
+        nsecs_t prevPresentTime = 0;
+        std::deque<std::shared_ptr<FenceTime>> presentFences;
+    };
+
 public:
     static TimeStats& getInstance();
     void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
@@ -99,6 +104,7 @@
     void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
 
     void setPowerMode(int32_t powerMode);
+    void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
 
 private:
     TimeStats() = default;
@@ -106,6 +112,7 @@
     bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(const std::string& layerName);
     void flushPowerTimeLocked();
+    void flushAvailableGlobalRecordsToStatsLocked();
 
     void enable();
     void disable();
@@ -118,6 +125,7 @@
     TimeStatsHelper::TimeStatsGlobal mTimeStats;
     std::unordered_map<std::string, LayerRecord> mTimeStatsTracker;
     PowerTime mPowerTime;
+    GlobalRecord mGlobalRecord;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index ec41a62..c701224 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -93,6 +93,8 @@
     StringAppendF(&result, "missedFrames = %d\n", missedFrames);
     StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
     StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime));
+    StringAppendF(&result, "presentToPresent histogram is as below:\n");
+    result.append(presentToPresent.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         result.append(ele->toString());
@@ -128,6 +130,11 @@
     globalProto.set_missed_frames(missedFrames);
     globalProto.set_client_composition_frames(clientCompositionFrames);
     globalProto.set_display_on_time(displayOnTime);
+    for (const auto& histEle : presentToPresent.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 939e137..7d0fe79 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -58,6 +58,7 @@
         int32_t missedFrames = 0;
         int32_t clientCompositionFrames = 0;
         int64_t displayOnTime = 0;
+        Histogram presentToPresent;
         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 f584496..377612a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
 // changes to these messages, and keep google3 side proto messages in sync if
 // the end to end pipeline needs to be updated.
 
-// Next tag: 8
+// Next tag: 9
 message SFTimeStatsGlobalProto {
   // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
@@ -39,6 +39,8 @@
   optional int32 client_composition_frames = 5;
   // Primary display on time in milliseconds.
   optional int64 display_on_time = 7;
+  // Present to present histogram.
+  repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
   // Stats per layer. Apps could have multiple layers.
   repeated SFTimeStatsLayerProto stats = 6;
 }