Add MissedDeadlineFrames to track frames that miss the vsync deadline

This change adds a MissedDeadlineFrames UMA that tracks frames that
miss the vsync deadline during scroll/animations.

Bug: 1092938
Change-Id: I044985583193d94c90485f1308be5645d553af7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2343642
Commit-Queue: Matthew Crabill <[email protected]>
Reviewed-by: Sadrul Chowdhury <[email protected]>
Reviewed-by: Xida Chen <[email protected]>
Reviewed-by: David Bokan <[email protected]>
Reviewed-by: Behdad Bakhshinategh <[email protected]>
Reviewed-by: Stephen Chenney <[email protected]>
Cr-Commit-Position: refs/heads/master@{#807596}
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index 56019f9..9e851e3d 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -361,6 +361,9 @@
     DCHECK_GT(impl_throughput().frames_expected,
               impl_throughput().frames_produced)
         << TRACKER_DCHECK_MSG;
+    DCHECK_GE(impl_throughput().frames_produced,
+              impl_throughput().frames_ontime)
+        << TRACKER_DCHECK_MSG;
     --impl_throughput().frames_expected;
 #if DCHECK_IS_ON()
     ++impl_throughput().frames_processed;
@@ -437,9 +440,28 @@
 
   uint32_t impl_frames_produced = 0;
   uint32_t main_frames_produced = 0;
+  uint32_t impl_frames_ontime = 0;
+  uint32_t main_frames_ontime = 0;
+
+  const auto& vsync_interval =
+      (feedback.interval.is_zero() ? viz::BeginFrameArgs::DefaultInterval()
+                                   : feedback.interval) *
+      1.5;
+  DCHECK(!vsync_interval.is_zero()) << TRACKER_DCHECK_MSG;
+  base::TimeTicks safe_deadline_for_frame =
+      last_frame_presentation_timestamp_ + vsync_interval;
 
   const bool was_presented = !feedback.failed();
   if (was_presented && submitted_frame_since_last_presentation) {
+    if (!last_frame_presentation_timestamp_.is_null() &&
+        (safe_deadline_for_frame < feedback.timestamp)) {
+      DCHECK_LE(impl_throughput().frames_ontime,
+                impl_throughput().frames_produced)
+          << TRACKER_DCHECK_MSG;
+      ++impl_throughput().frames_ontime;
+      ++impl_frames_ontime;
+    }
+
     DCHECK_LT(impl_throughput().frames_produced,
               impl_throughput().frames_expected)
         << TRACKER_DCHECK_MSG;
@@ -474,6 +496,16 @@
       metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kMain,
                              feedback.timestamp, feedback.interval);
     }
+    if (main_frames_.size() < size_before_erase) {
+      if (!last_frame_presentation_timestamp_.is_null() &&
+          (safe_deadline_for_frame < feedback.timestamp)) {
+        DCHECK_LE(main_throughput().frames_ontime,
+                  main_throughput().frames_produced)
+            << TRACKER_DCHECK_MSG;
+        ++main_throughput().frames_ontime;
+        ++main_frames_ontime;
+      }
+    }
 
     if (impl_frames_produced > 0) {
       // If there is no main frame presented, then we need to see whether or not
@@ -492,6 +524,7 @@
           // frames produced so that we can apply that to aggregated throughput
           // if the main frame reports no-damage later on.
           impl_frames_produced_while_expecting_main_ += impl_frames_produced;
+          impl_frames_ontime_while_expecting_main_ += impl_frames_ontime;
         } else {
           // TODO(https://crbug.com/1066455): Determine why this DCHECK is
           // causing PageLoadMetricsBrowserTests to flake, and re-enable.
@@ -499,10 +532,14 @@
           //    << TRACKER_DCHECK_MSG;
           aggregated_throughput().frames_produced += impl_frames_produced;
           impl_frames_produced_while_expecting_main_ = 0;
+          aggregated_throughput().frames_ontime += impl_frames_ontime;
+          impl_frames_ontime_while_expecting_main_ = 0;
         }
       } else {
         aggregated_throughput().frames_produced += main_frames_produced;
         impl_frames_produced_while_expecting_main_ = 0;
+        aggregated_throughput().frames_ontime += main_frames_ontime;
+        impl_frames_ontime_while_expecting_main_ = 0;
         while (!expecting_main_when_submit_impl_.empty() &&
                !viz::FrameTokenGT(expecting_main_when_submit_impl_.front(),
                                   frame_token)) {
@@ -511,6 +548,8 @@
       }
     }
 
+    last_frame_presentation_timestamp_ = feedback.timestamp;
+
     if (checkerboarding_.last_frame_had_checkerboarding) {
       DCHECK(!checkerboarding_.last_frame_timestamp.is_null())
           << TRACKER_DCHECK_MSG;
@@ -608,6 +647,8 @@
   DCHECK_GT(main_throughput().frames_expected,
             main_throughput().frames_produced)
       << TRACKER_DCHECK_MSG;
+  DCHECK_GE(main_throughput().frames_produced, main_throughput().frames_ontime)
+      << TRACKER_DCHECK_MSG;
   last_no_main_damage_sequence_ = args.frame_id.sequence_number;
   --main_throughput().frames_expected;
   // Compute the number of actually expected compositor frames during this main
@@ -634,6 +675,9 @@
   aggregated_throughput().frames_produced +=
       impl_frames_produced_while_expecting_main_;
   impl_frames_produced_while_expecting_main_ = 0;
+  aggregated_throughput().frames_ontime +=
+      impl_frames_ontime_while_expecting_main_;
+  impl_frames_ontime_while_expecting_main_ = 0;
   expecting_main_when_submit_impl_.clear();
 }