[go: nahoru, domu]

Decouple AverageLagTracker and LatencyInfo.

Moving the event collection to LayerTreeHostImpl is an initial step
towards a new version of the AverageLag metrics using
PresentationFeedback times instead of GpuSwap times, which are available
through LayerTreeHostImpl.
Moving them from LatencyTracker is also interesting as it aggregates
events across all viz clients at the end of the GPU pipeline, which can
potentially mix up events from multiple sources.
Tracking events by viz client (through LayerTreeHostImpl) avoids it.

As AverageLagTracker should be dedicated to Telemetry, it is interesting
to remove all LatencyInfo preprocessing from it, as LatencyInfo stores
a lot of information that is irrelevant to AverageLagTracker.

This CL proposes to move the scroll events collection from LatencyTracker
to LayerTreeHostImpl. It also proposes an AverageLagTrackingManager,
an intermediary class for using AverageLagTracker that preprocesses
LatencyInfo objects so AverageLagTracker can be independent from it.

As the event collection is moved to LayerTreeHostImpl, the
AverageLagTrackingManager also serves to encapsulate any preprocessing
from LayerTreeHostImpl and AverageLagTrakcer.


Bug: 1079024, 989207
Change-Id: I2e73e244eee9565239a638f00dfbf19d5658eadb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2189633
Commit-Queue: João Victor Almeida de Aguiar <joalmei@microsoft.com>
Reviewed-by: Steven Holte <holte@chromium.org>
Reviewed-by: Robert Flack <flackr@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: Xida Chen <xidachen@chromium.org>
Reviewed-by: Daniel Libby <dlibby@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#785975}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 7e5e837..ce648504 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -149,6 +149,10 @@
     "layers/video_layer_impl.h",
     "layers/viewport.cc",
     "layers/viewport.h",
+    "metrics/average_lag_tracker.cc",
+    "metrics/average_lag_tracker.h",
+    "metrics/average_lag_tracking_manager.cc",
+    "metrics/average_lag_tracking_manager.h",
     "metrics/begin_main_frame_metrics.cc",
     "metrics/begin_main_frame_metrics.h",
     "metrics/compositor_frame_reporter.cc",
@@ -660,6 +664,8 @@
     "layers/video_frame_provider_client_impl_unittest.cc",
     "layers/video_layer_impl_unittest.cc",
     "layers/viewport_unittest.cc",
+    "metrics/average_lag_tracker_unittest.cc",
+    "metrics/average_lag_tracking_manager_unittest.cc",
     "metrics/compositor_frame_reporter_unittest.cc",
     "metrics/compositor_frame_reporting_controller_unittest.cc",
     "metrics/compositor_timing_history_unittest.cc",
diff --git a/ui/latency/average_lag_tracker.cc b/cc/metrics/average_lag_tracker.cc
similarity index 71%
rename from ui/latency/average_lag_tracker.cc
rename to cc/metrics/average_lag_tracker.cc
index 347b32d..5aa9b7d 100644
--- a/ui/latency/average_lag_tracker.cc
+++ b/cc/metrics/average_lag_tracker.cc
@@ -2,44 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/latency/average_lag_tracker.h"
+#include "cc/metrics/average_lag_tracker.h"
+
+#include <algorithm>
 
 #include "base/metrics/histogram_functions.h"
 
-namespace ui {
+namespace cc {
 
 AverageLagTracker::AverageLagTracker() = default;
-
 AverageLagTracker::~AverageLagTracker() = default;
 
-void AverageLagTracker::AddLatencyInFrame(
-    const ui::LatencyInfo& latency,
-    base::TimeTicks gpu_swap_begin_timestamp,
-    const std::string& scroll_name) {
-  base::TimeTicks event_timestamp;
-  bool found_component = latency.FindLatency(
-      ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
-      &event_timestamp);
-  DCHECK(found_component);
-  // Skip if no event timestamp.
-  if (!found_component)
-    return;
-
-  if (scroll_name == "ScrollBegin") {
-    AddScrollBeginInFrame(gpu_swap_begin_timestamp, event_timestamp);
-  } else if (scroll_name == "ScrollUpdate" &&
-             !last_event_timestamp_.is_null()) {
-    AddScrollUpdateInFrame(latency, gpu_swap_begin_timestamp, event_timestamp);
+void AverageLagTracker::AddScrollEventInFrame(const EventInfo& event_info) {
+  if (event_info.event_type == EventType::ScrollBegin) {
+    AddScrollBeginInFrame(event_info);
+  } else if (!last_event_timestamp_.is_null()) {
+    AddScrollUpdateInFrame(event_info);
   }
 
-  last_event_timestamp_ = event_timestamp;
-  last_event_accumulated_delta_ += latency.scroll_update_delta();
-  last_rendered_accumulated_delta_ += latency.predicted_scroll_update_delta();
+  last_event_timestamp_ = event_info.event_timestamp;
+  last_event_accumulated_delta_ += event_info.event_scroll_delta;
+  last_rendered_accumulated_delta_ += event_info.predicted_scroll_delta;
 }
 
-void AverageLagTracker::AddScrollBeginInFrame(
-    base::TimeTicks gpu_swap_begin_timestamp,
-    base::TimeTicks event_timestamp) {
+void AverageLagTracker::AddScrollBeginInFrame(const EventInfo& event_info) {
+  DCHECK_EQ(event_info.event_type, EventType::ScrollBegin);
+
   // Flush all unfinished frames.
   while (!frame_lag_infos_.empty()) {
     frame_lag_infos_.front().lag_area += LagForUnfinishedFrame(
@@ -51,39 +39,41 @@
     CalculateAndReportAverageLagUma(frame_lag_infos_.size() == 1);
   }
   // |accumulated_lag_| should be cleared/reset.
-  DCHECK(accumulated_lag_ == 0);
+  DCHECK_EQ(accumulated_lag_, 0);
 
-  // Create ScrollBegin report, with report time equals to gpu swap time.
-  LagAreaInFrame first_frame(gpu_swap_begin_timestamp);
+  // Create ScrollBegin report, with report time equals to the frame
+  // timestamp.
+  LagAreaInFrame first_frame(event_info.finish_timestamp);
   frame_lag_infos_.push_back(first_frame);
 
   // Reset fields.
-  last_reported_time_ = event_timestamp;
-  last_finished_frame_time_ = event_timestamp;
+  last_reported_time_ = event_info.event_timestamp;
+  last_finished_frame_time_ = event_info.event_timestamp;
   last_event_accumulated_delta_ = 0;
   last_rendered_accumulated_delta_ = 0;
   is_begin_ = true;
 }
 
-void AverageLagTracker::AddScrollUpdateInFrame(
-    const LatencyInfo& latency,
-    base::TimeTicks gpu_swap_begin_timestamp,
-    base::TimeTicks event_timestamp) {
+void AverageLagTracker::AddScrollUpdateInFrame(const EventInfo& event_info) {
+  DCHECK_EQ(event_info.event_type, EventType::ScrollUpdate);
+
   // Only accept events in nondecreasing order.
-  if ((event_timestamp - last_event_timestamp_).InMilliseconds() < 0)
+  if ((event_info.event_timestamp - last_event_timestamp_).InMilliseconds() < 0)
     return;
 
   // Pop all frames where frame_time <= event_timestamp.
   while (!frame_lag_infos_.empty() &&
-         frame_lag_infos_.front().frame_time <= event_timestamp) {
+         frame_lag_infos_.front().frame_time <= event_info.event_timestamp) {
     base::TimeTicks front_time =
         std::max(last_event_timestamp_, last_finished_frame_time_);
     base::TimeTicks back_time = frame_lag_infos_.front().frame_time;
     frame_lag_infos_.front().lag_area +=
-        LagBetween(front_time, back_time, latency, event_timestamp,
+        LagBetween(front_time, back_time, event_info.event_scroll_delta,
+                   event_info.event_timestamp,
                    frame_lag_infos_.front().rendered_accumulated_delta);
     frame_lag_infos_.front().lag_area_no_prediction += LagBetween(
-        front_time, back_time, latency, event_timestamp,
+        front_time, back_time, event_info.event_scroll_delta,
+        event_info.event_timestamp,
         frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
 
     CalculateAndReportAverageLagUma();
@@ -91,8 +81,8 @@
 
   // Initialize a new LagAreaInFrame when current_frame_time > frame_time.
   if (frame_lag_infos_.empty() ||
-      gpu_swap_begin_timestamp > frame_lag_infos_.back().frame_time) {
-    LagAreaInFrame new_frame(gpu_swap_begin_timestamp,
+      event_info.finish_timestamp > frame_lag_infos_.back().frame_time) {
+    LagAreaInFrame new_frame(event_info.finish_timestamp,
                              last_rendered_accumulated_delta_,
                              last_event_accumulated_delta_);
     frame_lag_infos_.push_back(new_frame);
@@ -102,25 +92,27 @@
   if (!frame_lag_infos_.empty()) {
     // The front element in queue (if any) must satisfy frame_time >
     // event_timestamp, otherwise it would be popped in the while loop.
-    DCHECK(last_finished_frame_time_ <= event_timestamp &&
-           event_timestamp <= frame_lag_infos_.front().frame_time);
+    DCHECK_LE(last_finished_frame_time_, event_info.event_timestamp);
+    DCHECK_LE(event_info.event_timestamp, frame_lag_infos_.front().frame_time);
     base::TimeTicks front_time =
         std::max(last_finished_frame_time_, last_event_timestamp_);
-    base::TimeTicks back_time = event_timestamp;
+    base::TimeTicks back_time = event_info.event_timestamp;
 
     frame_lag_infos_.front().lag_area +=
-        LagBetween(front_time, back_time, latency, event_timestamp,
+        LagBetween(front_time, back_time, event_info.event_scroll_delta,
+                   event_info.event_timestamp,
                    frame_lag_infos_.front().rendered_accumulated_delta);
 
     frame_lag_infos_.front().lag_area_no_prediction += LagBetween(
-        front_time, back_time, latency, event_timestamp,
+        front_time, back_time, event_info.event_scroll_delta,
+        event_info.event_timestamp,
         frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
   }
 }
 
 float AverageLagTracker::LagBetween(base::TimeTicks front_time,
                                     base::TimeTicks back_time,
-                                    const LatencyInfo& latency,
+                                    const float scroll_delta,
                                     base::TimeTicks event_timestamp,
                                     float rendered_accumulated_delta) {
   // In some tests, we use const event time. return 0 to avoid divided by 0.
@@ -129,15 +121,14 @@
 
   float front_delta =
       (last_event_accumulated_delta_ +
-       (latency.scroll_update_delta() *
+       (scroll_delta *
         ((front_time - last_event_timestamp_).InMillisecondsF() /
          (event_timestamp - last_event_timestamp_).InMillisecondsF()))) -
       rendered_accumulated_delta;
 
   float back_delta =
       (last_event_accumulated_delta_ +
-       latency.scroll_update_delta() *
-
+       scroll_delta *
            ((back_time - last_event_timestamp_).InMillisecondsF() /
             (event_timestamp - last_event_timestamp_).InMillisecondsF())) -
       rendered_accumulated_delta;
@@ -168,13 +159,13 @@
   DCHECK(!frame_lag_infos_.empty());
   const LagAreaInFrame& frame_lag = frame_lag_infos_.front();
 
-  DCHECK(frame_lag.lag_area >= 0.f);
-  DCHECK(frame_lag.lag_area_no_prediction >= 0.f);
+  DCHECK_GE(frame_lag.lag_area, 0.f);
+  DCHECK_GE(frame_lag.lag_area_no_prediction, 0.f);
   accumulated_lag_ += frame_lag.lag_area;
   accumulated_lag_no_prediction_ += frame_lag.lag_area_no_prediction;
 
   if (is_begin_) {
-    DCHECK(accumulated_lag_ == accumulated_lag_no_prediction_);
+    DCHECK_EQ(accumulated_lag_, accumulated_lag_no_prediction_);
   }
 
   // |send_anyway| is true when we are flush all remaining frames on next
@@ -217,4 +208,4 @@
   frame_lag_infos_.pop_front();
 }
 
-}  // namespace ui
+}  // namespace cc
diff --git a/ui/latency/average_lag_tracker.h b/cc/metrics/average_lag_tracker.h
similarity index 62%
rename from ui/latency/average_lag_tracker.h
rename to cc/metrics/average_lag_tracker.h
index f97738f17..d113601 100644
--- a/ui/latency/average_lag_tracker.h
+++ b/cc/metrics/average_lag_tracker.h
@@ -2,25 +2,59 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_LATENCY_AVERAGE_LAG_TRACKER_H_
-#define UI_LATENCY_AVERAGE_LAG_TRACKER_H_
+#ifndef CC_METRICS_AVERAGE_LAG_TRACKER_H_
+#define CC_METRICS_AVERAGE_LAG_TRACKER_H_
 
 #include <deque>
+#include <string>
 
 #include "base/macros.h"
-#include "ui/latency/latency_info.h"
+#include "base/time/time.h"
+#include "cc/cc_export.h"
 
-namespace ui {
+namespace cc {
 
 // A class for reporting AverageLag metrics. See
 // https://docs.google.com/document/d/1e8NuzPblIv2B9bz01oSj40rmlse7_PHq5oFS3lqz6N4/
-class AverageLagTracker {
+class CC_EXPORT AverageLagTracker {
  public:
+  enum class EventType { ScrollBegin, ScrollUpdate };
+  struct EventInfo {
+    EventInfo(int trace_id,
+              float event_scroll_delta,
+              float predicted_scroll_delta,
+              base::TimeTicks event_timestamp,
+              EventType event_type)
+        : trace_id(trace_id),
+          event_scroll_delta(event_scroll_delta),
+          predicted_scroll_delta(predicted_scroll_delta),
+          event_timestamp(event_timestamp),
+          event_type(event_type) {}
+    // Id from the original LatencyInfo.
+    int trace_id;
+    // Delta reported by the scroll event (begin or update).
+    float event_scroll_delta;
+    // Delta predicted (when prediction is on, otherwise should be equals to
+    // |event_scroll_delta|).
+    float predicted_scroll_delta;
+    // Timestamp when the scroll event happened.
+    base::TimeTicks event_timestamp;
+    // Timestamp when the scroll event's frame finished, which is currently
+    // when the frame swap completed.
+    base::TimeTicks finish_timestamp;
+    // Scroll event type (begin or update).
+    EventType event_type;
+  };
+
   AverageLagTracker();
   ~AverageLagTracker();
-  void AddLatencyInFrame(const LatencyInfo& latency,
-                         base::TimeTicks gpu_swap_begin_timestamp,
-                         const std::string& scroll_name);
+
+  // Disallow copy and assign.
+  AverageLagTracker(const AverageLagTracker&) = delete;
+  AverageLagTracker& operator=(AverageLagTracker const&) = delete;
+
+  // Adds a scroll event defined by |event_info|.
+  void AddScrollEventInFrame(const EventInfo& event_info);
 
  private:
   typedef struct LagAreaInFrame {
@@ -50,11 +84,10 @@
     float lag_area_no_prediction;
   } LagAreaInFrame;
 
-  void AddScrollBeginInFrame(base::TimeTicks gpu_swap_begin_timestamp,
-                             base::TimeTicks event_timestamp);
-  void AddScrollUpdateInFrame(const LatencyInfo& latency,
-                              base::TimeTicks gpu_swap_begin_timestamp,
-                              base::TimeTicks event_timestamp);
+  // Processes |event_info| as a ScrollBegin event and add it to the Lag.
+  void AddScrollBeginInFrame(const EventInfo& event_info);
+  // Processes |event_info| as a ScrollUpdate event and add it to the Lag.
+  void AddScrollUpdateInFrame(const EventInfo& event_info);
 
   // Calculate lag in 1 seconds intervals and report UMA.
   void CalculateAndReportAverageLagUma(bool send_anyway = false);
@@ -63,7 +96,7 @@
   // |back_time|.
   float LagBetween(base::TimeTicks front_time,
                    base::TimeTicks back_time,
-                   const LatencyInfo& latency,
+                   float scroll_delta,
                    base::TimeTicks event_time,
                    float rendered_accumulated_delta);
 
@@ -77,10 +110,10 @@
   base::TimeTicks last_finished_frame_time_;
 
   // Accumulated scroll delta for actual scroll update events. Cumulated from
-  // latency.scroll_update_delta(). Reset on ScrollBegin.
+  // event_scroll_delta. Reset on ScrollBegin.
   float last_event_accumulated_delta_ = 0;
   // Accumulated scroll delta got rendered on gpu swap. Cumulated from
-  // latency.predicted_scroll_update_delta(). It always has same value as
+  // predicted_scroll_delta. It always has same value as
   // |last_event_accumulated_delta_| when scroll prediction is disabled.
   float last_rendered_accumulated_delta_ = 0;
 
@@ -97,10 +130,8 @@
   float accumulated_lag_ = 0;
   // Accumulated lag not taking into account the predicted deltas.
   float accumulated_lag_no_prediction_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(AverageLagTracker);
 };
 
-}  // namespace ui
+}  // namespace cc
 
-#endif  // UI_LATENCY_AVERAGE_LAG_TRACKER_H_
+#endif  // CC_METRICS_AVERAGE_LAG_TRACKER_H_
diff --git a/ui/latency/average_lag_tracker_unittest.cc b/cc/metrics/average_lag_tracker_unittest.cc
similarity index 75%
rename from ui/latency/average_lag_tracker_unittest.cc
rename to cc/metrics/average_lag_tracker_unittest.cc
index e2a0f223..7b0c9b4 100644
--- a/ui/latency/average_lag_tracker_unittest.cc
+++ b/cc/metrics/average_lag_tracker_unittest.cc
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/latency/average_lag_tracker.h"
+#include "cc/metrics/average_lag_tracker.h"
+
+#include <memory>
 
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -11,7 +13,7 @@
 using base::Bucket;
 using testing::ElementsAre;
 
-namespace ui {
+namespace cc {
 namespace {
 
 class AverageLagTrackerTest : public testing::Test {
@@ -28,38 +30,26 @@
     average_lag_tracker_ = std::make_unique<AverageLagTracker>();
   }
 
-  void SyntheticTouchScrollBeginLatencyInfo(base::TimeTicks event_time,
-                                            base::TimeTicks frame_time,
-                                            float delta,
-                                            float predicted_delta = 0) {
-    ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH);
-    touch_latency.set_scroll_update_delta(delta);
-    touch_latency.set_predicted_scroll_update_delta(
-        predicted_delta != 0 ? predicted_delta : delta);
-
-    touch_latency.AddLatencyNumberWithTimestamp(
-        ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
-        event_time);
-    touch_latency.AddLatencyNumberWithTimestamp(
-        ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
-    average_lag_tracker_->AddLatencyInFrame(touch_latency, frame_time,
-                                            "ScrollBegin");
+  void SyntheticTouchScrollBegin(base::TimeTicks event_time,
+                                 base::TimeTicks frame_time,
+                                 float delta,
+                                 float predicted_delta = 0) {
+    AverageLagTracker::EventInfo event_info(
+        0, delta, predicted_delta != 0 ? predicted_delta : delta, event_time,
+        AverageLagTracker::EventType::ScrollBegin);
+    event_info.finish_timestamp = frame_time;
+    average_lag_tracker_->AddScrollEventInFrame(event_info);
   }
 
-  void SyntheticTouchScrollUpdateLatencyInfo(base::TimeTicks event_time,
-                                             base::TimeTicks frame_time,
-                                             float delta,
-                                             float predicted_delta = 0) {
-    ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH);
-    touch_latency.set_scroll_update_delta(delta);
-    touch_latency.set_predicted_scroll_update_delta(
-        predicted_delta != 0 ? predicted_delta : delta);
-    touch_latency.AddLatencyNumberWithTimestamp(
-        ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, event_time);
-    touch_latency.AddLatencyNumberWithTimestamp(
-        ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
-    average_lag_tracker_->AddLatencyInFrame(touch_latency, frame_time,
-                                            "ScrollUpdate");
+  void SyntheticTouchScrollUpdate(base::TimeTicks event_time,
+                                  base::TimeTicks frame_time,
+                                  float delta,
+                                  float predicted_delta = 0) {
+    AverageLagTracker::EventInfo event_info(
+        0, delta, predicted_delta != 0 ? predicted_delta : delta, event_time,
+        AverageLagTracker::EventType::ScrollUpdate);
+    event_info.finish_timestamp = frame_time;
+    average_lag_tracker_->AddScrollEventInFrame(event_info);
   }
 
  protected:
@@ -85,7 +75,7 @@
   // ScrollBegin
   event_time += base::TimeDelta::FromMilliseconds(10);  // 15ms
   frame_time += base::TimeDelta::FromMilliseconds(10);  // 20ms
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
 
   // Send 101 ScrollUpdate events to verify that there is 1 AverageLag record
   // per 1 second.
@@ -95,8 +85,7 @@
     frame_time += base::TimeDelta::FromMilliseconds(10);
     // First 50 has positive delta, others negetive delta.
     const int sign = (i < kUpdates / 2) ? 1 : -1;
-    SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                          sign * scroll_delta);
+    SyntheticTouchScrollUpdate(event_time, frame_time, sign * scroll_delta);
   }
 
   // ScrollBegin report_time is at 20ms, so the next ScrollUpdate report_time is
@@ -133,7 +122,7 @@
   // Send another ScrollBegin to end the unfinished ScrollUpdate report.
   event_time += base::TimeDelta::FromMilliseconds(10);
   frame_time += base::TimeDelta::FromMilliseconds(10);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
 
   // The last ScrollUpdate's lag is 8.75px and truncated to 8.
   EXPECT_THAT(histogram_tester().GetAllSamples(
@@ -156,14 +145,14 @@
       event_time + base::TimeDelta::FromMilliseconds(20);
   float scroll_delta = 10;
 
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
 
   // Send 2 ScrollUpdate. The second one will record AverageLag.ScrollBegin as
   // it's event_time is larger or equal to ScrollBegin's frame_time.
   for (int i = 0; i < 2; i++) {
     event_time += base::TimeDelta::FromMilliseconds(10);
     frame_time = event_time + base::TimeDelta::FromMilliseconds(20);
-    SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time, scroll_delta);
+    SyntheticTouchScrollUpdate(event_time, frame_time, scroll_delta);
   }
 
   // ScrollBegin AveragLag are from t=10ms to t=30ms, with absolute scroll
@@ -178,7 +167,7 @@
   // compute from their frame_time.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
   // The to unfinished frames' lag are (finger_positon-rendered_position)*time,
   // AverageLag is ((30px-10px)*10ms+(30px-20px)*10ms)/20ms = 15px.
   EXPECT_THAT(histogram_tester().GetAllSamples(
@@ -191,15 +180,13 @@
   // ScrollBegin
   base::TimeTicks event_time = MillisecondsToTimeTicks(10);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
-                                       -10 /* scroll_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, -10 /* scroll_delta */);
 
   // ScrollUpdate with event_time >= ScrollBegin frame_time will generate
   // a histogram for AverageLag.ScrollBegin.
   event_time = MillisecondsToTimeTicks(20);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                        -10 /* scroll_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, -10 /* scroll_delta */);
 
   // Absolute position from -10 to -20. The AverageLag should be:
   // (0.5*(10px + 20px)*10ms/10ms) = 15px.
@@ -209,13 +196,12 @@
 
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                        5 /* scroll_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 5 /* scroll_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   // The ScrollUpdates are at t=20ms, finger_pos=-20px, rendered_pos=-10px,
   // at t=25ms, finger_pos=-15px, rendered_pos=-10px;
@@ -232,25 +218,22 @@
   // ScrollBegin
   base::TimeTicks event_time = MillisecondsToTimeTicks(10);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
-                                       10 /* scroll_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, 10 /* scroll_delta */);
 
   // At t=20, lag = 10px.
   event_time = MillisecondsToTimeTicks(20);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                        10 /* scroll_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */);
 
   // At t=30, lag = -10px.
   event_time = MillisecondsToTimeTicks(30);
   frame_time = MillisecondsToTimeTicks(40);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                        -20 /* scroll_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, -20 /* scroll_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   // From t=20 to t=30, lag_area=2*(0.5*10px*5ms)=50px*ms.
   // From t=30 to t=40, lag_area=20px*10ms=200px*ms
@@ -266,25 +249,22 @@
   // ScrollBegin, at t=5, finger_pos=5px.
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time,
-                                       5 /* scroll_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */);
 
   // ScrollUpdate, at t=15, finger_pos=15px.
   event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                        10 /* scroll_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */);
 
   // ScrollUpdate, at t=25, finger_pos=25px.
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time,
-                                        10 /* scroll_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
   EXPECT_THAT(histogram_tester().GetAllSamples(
@@ -306,27 +286,27 @@
   // Predict frame_time=10, predicted_pos = 10px.
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
-  SyntheticTouchScrollBeginLatencyInfo(
-      event_time, frame_time, 5 /* scroll_delta */, 10 /* predicted_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+                            10 /* predicted_delta */);
 
   // ScrollUpdate, at t=15, finger_pos=15px.
   // Predict frame_time=20, predicted_pos = 20px.
   event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 10 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             10 /* predicted_delta */);
 
   // ScrollUpdate, at t=25, finger_pos=25px.
   // Predict frame_time=30, predicted_pos = 30px.
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 10 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             10 /* predicted_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
   EXPECT_THAT(histogram_tester().GetAllSamples(
@@ -358,27 +338,27 @@
   // Predict frame_time=10, predicted_pos(over) = 12px.
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
-  SyntheticTouchScrollBeginLatencyInfo(
-      event_time, frame_time, 5 /* scroll_delta */, 12 /* predicted_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+                            12 /* predicted_delta */);
 
   // ScrollUpdate, at t=15, finger_pos=15px.
   // Predict frame_time=20, predicted_pos(under) = 17px.
   event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 5 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             5 /* predicted_delta */);
 
   // ScrollUpdate, at t=25, finger_pos=25px.
   // Predict frame_time=30, predicted_pos(over) = 31px.
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 14 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             14 /* predicted_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   "Event.Latency.ScrollBegin.Touch.AverageLag"),
@@ -405,27 +385,27 @@
   // Predict frame_time=10, predicted_pos(over) = 20px.
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
-  SyntheticTouchScrollBeginLatencyInfo(
-      event_time, frame_time, 5 /* scroll_delta */, 20 /* predicted_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+                            20 /* predicted_delta */);
 
   // ScrollUpdate, at t=15, finger_pos=15px.
   // Predict frame_time=20, predicted_pos(over) = 60px.
   event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 40 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             40 /* predicted_delta */);
 
   // ScrollUpdate, at t=25, finger_pos=25px.
   // Predict frame_time=30, predicted_pos(over) = 60px.
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 0 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             0 /* predicted_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   "Event.Latency.ScrollBegin.Touch.AverageLag"),
@@ -452,27 +432,27 @@
   // Predict frame_time=10, predicted_pos(over) = 25px.
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
-  SyntheticTouchScrollBeginLatencyInfo(
-      event_time, frame_time, 5 /* scroll_delta */, 25 /* predicted_delta */);
+  SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+                            25 /* predicted_delta */);
 
   // ScrollUpdate, at t=15, finger_pos=15px.
   // Predict frame_time=20, predicted_pos(over) = 32px.
   event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 7 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             7 /* predicted_delta */);
 
   // ScrollUpdate, at t=25, finger_pos=25px.
   // Predict frame_time=30, predicted_pos(over) = 37px.
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(
-      event_time, frame_time, 10 /* scroll_delta */, 5 /* predicted_delta */);
+  SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+                             5 /* predicted_delta */);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
                   "Event.Latency.ScrollBegin.Touch.AverageLag"),
@@ -501,20 +481,20 @@
   base::TimeTicks event_time = MillisecondsToTimeTicks(5);
   base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
   float scroll_delta = 5.f;
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
 
   event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(20);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollUpdate(event_time, frame_time, scroll_delta);
 
   event_time = MillisecondsToTimeTicks(25);
   frame_time = MillisecondsToTimeTicks(30);
-  SyntheticTouchScrollUpdateLatencyInfo(event_time, frame_time, scroll_delta);
+  SyntheticTouchScrollUpdate(event_time, frame_time, scroll_delta);
 
   // A ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(1000);
   frame_time = MillisecondsToTimeTicks(1000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   histogram_tester().ExpectTotalCount(
       "Event.Latency.ScrollUpdate.Touch.AverageLag", 1);
@@ -523,13 +503,12 @@
   // representing an event that gets process out of order.
   base::TimeTicks earlier_event_time = MillisecondsToTimeTicks(15);
   frame_time = MillisecondsToTimeTicks(1010);
-  SyntheticTouchScrollUpdateLatencyInfo(earlier_event_time, frame_time,
-                                        scroll_delta);
+  SyntheticTouchScrollUpdate(earlier_event_time, frame_time, scroll_delta);
 
   // Another ScrollBegin to flush unfinished frames.
   event_time = MillisecondsToTimeTicks(2000);
   frame_time = MillisecondsToTimeTicks(2000);
-  SyntheticTouchScrollBeginLatencyInfo(event_time, frame_time, 0);
+  SyntheticTouchScrollBegin(event_time, frame_time, 0);
 
   // Ensure that the event was ignored.
   histogram_tester().ExpectTotalCount(
@@ -537,4 +516,4 @@
 }
 
 }  // namespace
-}  // namespace ui
+}  // namespace cc
diff --git a/cc/metrics/average_lag_tracking_manager.cc b/cc/metrics/average_lag_tracking_manager.cc
new file mode 100644
index 0000000..3ddd0ae
--- /dev/null
+++ b/cc/metrics/average_lag_tracking_manager.cc
@@ -0,0 +1,96 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/metrics/average_lag_tracking_manager.h"
+
+#include <algorithm>
+
+#include "components/viz/common/frame_timing_details.h"
+#include "components/viz/common/quads/compositor_frame_metadata.h"
+#include "ui/latency/latency_info.h"
+
+namespace cc {
+
+AverageLagTrackingManager::AverageLagTrackingManager() = default;
+
+AverageLagTrackingManager::~AverageLagTrackingManager() {
+  // The map must contain only frames that haven't been presented (i.e. did not
+  // get a presentation feedback yet). Thus, at a given point in time, more than
+  // a handful (actually around 2) of frames without feedback is unexpected.
+  DCHECK_LE(frame_token_to_info_.size(), 20u);
+}
+
+void AverageLagTrackingManager::CollectScrollEventsFromFrame(
+    uint32_t frame_token,
+    const std::vector<ui::LatencyInfo>& latency_infos) {
+  std::vector<AverageLagTracker::EventInfo> event_infos;
+
+  for (ui::LatencyInfo latency_info : latency_infos) {
+    if (latency_info.source_event_type() != ui::SourceEventType::TOUCH)
+      continue;
+
+    bool found_scroll_begin = latency_info.FindLatency(
+        ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+        nullptr);
+    bool found_scroll_update = latency_info.FindLatency(
+        ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, nullptr);
+
+    if (!found_scroll_begin && !found_scroll_update)
+      continue;
+
+    base::TimeTicks event_timestamp;
+    bool found_event = latency_info.FindLatency(
+        ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
+        &event_timestamp);
+    DCHECK(found_event);
+
+    AverageLagTracker::EventInfo event_info(
+        latency_info.trace_id(), latency_info.scroll_update_delta(),
+        latency_info.predicted_scroll_update_delta(), event_timestamp,
+        found_scroll_begin == true
+            ? AverageLagTracker::EventType::ScrollBegin
+            : AverageLagTracker::EventType::ScrollUpdate);
+
+    event_infos.push_back(event_info);
+  }
+
+  frame_token_to_info_.push_back(
+      std::make_pair(frame_token, std::move(event_infos)));
+}
+
+void AverageLagTrackingManager::DidPresentCompositorFrame(
+    uint32_t frame_token,
+    const viz::FrameTimingDetails& frame_details) {
+  // Erase all previous frames that haven't received a feedback and get the
+  // current |frame_token| list of events.
+  std::vector<AverageLagTracker::EventInfo> infos;
+  while (!frame_token_to_info_.empty() &&
+         !viz::FrameTokenGT(frame_token_to_info_.front().first, frame_token)) {
+    if (frame_token_to_info_.front().first == frame_token)
+      infos = std::move(frame_token_to_info_.front().second);
+
+    frame_token_to_info_.pop_front();
+  }
+
+  if (infos.size() == 0)
+    return;
+
+  if (!frame_details.presentation_feedback.failed()) {
+    DCHECK(!frame_details.swap_timings.is_null());
+
+    // Sorts data by trace_id because |infos| can be in non-ascending order
+    // (ascending order of trace_id/time is required by AverageLagTracker).
+    std::sort(infos.begin(), infos.end(),
+              [](const AverageLagTracker::EventInfo& a,
+                 const AverageLagTracker::EventInfo& b) {
+                return a.trace_id < b.trace_id;
+              });
+
+    for (AverageLagTracker::EventInfo info : infos) {
+      info.finish_timestamp = frame_details.swap_timings.swap_start;
+      lag_tracker_.AddScrollEventInFrame(info);
+    }
+  }
+}
+}  // namespace cc
diff --git a/cc/metrics/average_lag_tracking_manager.h b/cc/metrics/average_lag_tracking_manager.h
new file mode 100644
index 0000000..70af5cc
--- /dev/null
+++ b/cc/metrics/average_lag_tracking_manager.h
@@ -0,0 +1,62 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_METRICS_AVERAGE_LAG_TRACKING_MANAGER_H_
+#define CC_METRICS_AVERAGE_LAG_TRACKING_MANAGER_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/containers/circular_deque.h"
+#include "cc/cc_export.h"
+#include "cc/metrics/average_lag_tracker.h"
+
+namespace ui {
+class LatencyInfo;
+}  // namespace ui
+
+namespace viz {
+struct FrameTimingDetails;
+}  // namespace viz
+
+namespace cc {
+
+// A helper to decouple the LatencyInfos and the AverageLagTracker
+class CC_EXPORT AverageLagTrackingManager {
+ public:
+  AverageLagTrackingManager();
+  ~AverageLagTrackingManager();
+
+  // Disallow copy and assign.
+  AverageLagTrackingManager(const AverageLagTrackingManager&) = delete;
+  AverageLagTrackingManager& operator=(AverageLagTrackingManager const&) =
+      delete;
+
+  // Adds all the eligible events in the collection |infos| to the |frame_token|
+  // wait list.
+  void CollectScrollEventsFromFrame(uint32_t frame_token,
+                                    const std::vector<ui::LatencyInfo>& infos);
+
+  // Sends all pending events in the |frame_token| list to the
+  // AverageLagTracker, given its |frame_details|.
+  void DidPresentCompositorFrame(uint32_t frame_token,
+                                 const viz::FrameTimingDetails& frame_details);
+
+ private:
+  // Adds an eligible event |info| to the |frame_token| wait list.
+  void CollectScrollEventFromFrame(uint32_t frame_token,
+                                   const ui::LatencyInfo& info);
+
+  AverageLagTracker lag_tracker_;
+
+  // List of events (vector) per frame (uint32_t |frame_token|) to submit to the
+  // lag trackers when DidPresentCompositorFrame is called for a |frame_token|.
+  base::circular_deque<
+      std::pair<uint32_t, std::vector<AverageLagTracker::EventInfo>>>
+      frame_token_to_info_;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_AVERAGE_LAG_TRACKING_MANAGER_H_
diff --git a/cc/metrics/average_lag_tracking_manager_unittest.cc b/cc/metrics/average_lag_tracking_manager_unittest.cc
new file mode 100644
index 0000000..5aa6095
--- /dev/null
+++ b/cc/metrics/average_lag_tracking_manager_unittest.cc
@@ -0,0 +1,228 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/metrics/average_lag_tracking_manager.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/viz/common/frame_timing_details.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/latency/latency_info.h"
+
+namespace cc {
+namespace {
+
+using base::Bucket;
+using testing::ElementsAre;
+
+// Helper for TimeTicks usage
+base::TimeTicks MillisecondsToTimeTicks(float t_ms) {
+  return base::TimeTicks() + base::TimeDelta::FromMilliseconds(t_ms);
+}
+
+// Helper for FrameTimingDetails usage in DidPresentCompositorFrame
+viz::FrameTimingDetails PrepareFrameDetails(base::TimeTicks swap_time) {
+  gfx::SwapTimings timings;
+  timings.swap_start = swap_time;
+  viz::FrameTimingDetails details;
+  details.swap_timings = timings;
+  return details;
+}
+
+class AverageLagTrackingManagerTest : public testing::Test {
+ protected:
+  AverageLagTrackingManagerTest() = default;
+
+  void SetUp() override { ResetHistograms(); }
+
+  void ResetHistograms() {
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
+  }
+
+  // Creates a scroll event each |scroll_rate| (in ms) of |scroll_delta| px.
+  // Collect events at the expected |frame_times|.
+  void SimulateConstantScroll(const std::vector<unsigned int>& frame_times,
+                              float scroll_delta,
+                              unsigned int scroll_rate) {
+    if (frame_times.size() == 0 || frame_times[0] < scroll_rate)
+      return;
+
+    // Creates 1st frame with scroll begin
+    std::vector<ui::LatencyInfo> events(frame_times[0] / scroll_rate);
+    base::TimeTicks event_time = MillisecondsToTimeTicks(scroll_rate);
+    events[0] = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin,
+                                   event_time, 0, scroll_delta);
+    for (size_t i = 1; i < events.size(); i++) {
+      event_time += base::TimeDelta::FromMilliseconds(scroll_rate);
+      events[i] = PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
+                                     event_time, i, scroll_delta);
+    }
+    average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
+
+    // Creates remaining frames
+    for (size_t frame = 1; frame < frame_times.size(); frame++) {
+      unsigned int time_delta = frame_times[frame] - frame_times[frame - 1];
+      events = std::vector<ui::LatencyInfo>(time_delta / scroll_rate);
+      for (size_t i = 0; i < events.size(); i++) {
+        event_time += base::TimeDelta::FromMilliseconds(scroll_rate);
+        events[i] =
+            PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
+                               event_time, i, scroll_delta);
+      }
+      average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame, events);
+    }
+  }
+
+  // Prepares a ui::LatencyInfo object for a ScrollEvent
+  ui::LatencyInfo PrepareScrollEvent(AverageLagTracker::EventType event_type,
+                                     base::TimeTicks event_time,
+                                     int trace_id,
+                                     float delta,
+                                     float predicted_delta = 0) {
+    ui::LatencyInfo info;
+    info.set_trace_id(trace_id);
+    info.set_source_event_type(ui::SourceEventType::TOUCH);
+
+    info.AddLatencyNumberWithTimestamp(
+        event_type == AverageLagTracker::EventType::ScrollBegin
+            ? ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT
+            : ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+        event_time);
+
+    info.AddLatencyNumberWithTimestamp(
+        ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
+
+    info.set_scroll_update_delta(delta);
+    info.set_predicted_scroll_update_delta(
+        predicted_delta != 0 ? predicted_delta : delta);
+
+    return info;
+  }
+
+  AverageLagTrackingManager average_lag_tracking_manager_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+// Simulate a simple situation that events at every 10ms and start at t=15ms,
+// frame swaps at every 10ms too and start at t=20ms and test we record one
+// UMA for ScrollUpdate in one second. Tests using CollectScrollEventAtFrame
+// (1 event per collection)
+TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) {
+  base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+  base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+  float scroll_delta = 10;
+  int frame_id = 1;
+
+  // ScrollBegin
+  event_time += base::TimeDelta::FromMilliseconds(10);  // 15ms
+  frame_time += base::TimeDelta::FromMilliseconds(10);  // 20ms
+  ui::LatencyInfo evt = PrepareScrollEvent(
+      AverageLagTracker::EventType::ScrollBegin, event_time, 1, scroll_delta);
+  average_lag_tracking_manager_.CollectScrollEventsFromFrame(
+      frame_id, std::vector<ui::LatencyInfo>{evt});
+  average_lag_tracking_manager_.DidPresentCompositorFrame(
+      frame_id, PrepareFrameDetails(frame_time));
+
+  // Send 101 ScrollUpdate events to verify that there is 1 AverageLag record
+  // per 1 second.
+  const int kUpdates = 101;
+  for (int i = 0; i < kUpdates; i++) {
+    event_time += base::TimeDelta::FromMilliseconds(10);
+    frame_time += base::TimeDelta::FromMilliseconds(10);
+    // First 50 has positive delta, others negative delta.
+    const int sign = (i < kUpdates / 2) ? 1 : -1;
+
+    evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
+                             event_time, 1, sign * scroll_delta);
+    average_lag_tracking_manager_.CollectScrollEventsFromFrame(
+        frame_id, std::vector<ui::LatencyInfo>{evt});
+    average_lag_tracking_manager_.DidPresentCompositorFrame(
+        frame_id, PrepareFrameDetails(frame_time));
+  }
+
+  // ScrollBegin report_time is at 20ms, so the next ScrollUpdate report_time is
+  // at 1020ms. The last event_time that finish this report should be later than
+  // 1020ms.
+  EXPECT_EQ(event_time, MillisecondsToTimeTicks(1025));
+  EXPECT_EQ(frame_time, MillisecondsToTimeTicks(1030));
+
+  // ScrollBegin AverageLag are the area between the event original component
+  // (time=15ms, delta=10px) to the frame swap time (time=20ms, expect finger
+  // position at delta=15px). The AverageLag scaled to 1 second is
+  // (0.5*(10px+15px)*5ms)/5ms = 12.5px.
+  EXPECT_THAT(histogram_tester_->GetAllSamples(
+                  "Event.Latency.ScrollBegin.Touch.AverageLag"),
+              ElementsAre(Bucket(12, 1)));
+  // This ScrollUpdate AverageLag are calculated as the finger uniformly scroll
+  // 10px each frame. For scroll up/down frame, the Lag at the last frame swap
+  // is 5px, and Lag at this frame swap is 15px. For the one changing direction,
+  // the Lag is from 5 to 10 and down to 5 again. So total LagArea is 99 * 100,
+  // plus 75. the AverageLag in 1 second is 9.975px.
+  EXPECT_THAT(histogram_tester_->GetAllSamples(
+                  "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+              ElementsAre(Bucket(9, 1)));
+  EXPECT_THAT(
+      histogram_tester_->GetAllSamples(
+          "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
+      ElementsAre(Bucket(0, 1)));
+  histogram_tester_->ExpectTotalCount(
+      "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
+  ResetHistograms();
+
+  // Send another ScrollBegin to end the unfinished ScrollUpdate report.
+  event_time += base::TimeDelta::FromMilliseconds(10);
+  frame_time += base::TimeDelta::FromMilliseconds(10);
+  evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin,
+                           event_time, 1, scroll_delta);
+  average_lag_tracking_manager_.CollectScrollEventsFromFrame(
+      frame_id, std::vector<ui::LatencyInfo>{evt});
+  average_lag_tracking_manager_.DidPresentCompositorFrame(
+      frame_id, PrepareFrameDetails(frame_time));
+
+  // The last ScrollUpdate's lag is 8.75px and truncated to 8.
+  EXPECT_THAT(histogram_tester_->GetAllSamples(
+                  "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+              ElementsAre(Bucket(8, 1)));
+  EXPECT_THAT(
+      histogram_tester_->GetAllSamples(
+          "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
+      ElementsAre(Bucket(0, 1)));
+  histogram_tester_->ExpectTotalCount(
+      "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
+}
+
+// This test creates 3 frames in order to check the submission of ScrollBegin
+// and ScrollUpdate events sent using CollectScrollEventsAtFrame (multiple
+// events per collection)
+TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) {
+  std::vector<unsigned int> frame_times = {400, 1400, 1600};
+  SimulateConstantScroll(frame_times, 10, 100);
+  for (size_t frame = 0; frame < frame_times.size(); frame++) {
+    average_lag_tracking_manager_.DidPresentCompositorFrame(
+        frame,
+        PrepareFrameDetails(MillisecondsToTimeTicks(frame_times[frame])));
+  }
+
+  // As the first frame is the ScrollBegin frame, the average lag is
+  // 0.5*(10 + 40) * 30 / 30 = 25. But UmaHistogramCounts1000's binning will
+  // round it to 23.
+  EXPECT_THAT(histogram_tester_->GetAllSamples(
+                  "Event.Latency.ScrollBegin.Touch.AverageLag"),
+              ElementsAre(Bucket(23, 1)));
+
+  // Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is
+  // waiting for the next frame for sumission).
+  // As there is a scroll update right at the same time as the frame submission,
+  // frame 2 starts with 0 lag at 0.4s and finishes with 100 at 1.4, thus:
+  // 0.5 * (0 + 100) / 2 = 50. It gets into the same bin as 47.
+  EXPECT_THAT(histogram_tester_->GetAllSamples(
+                  "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+              ElementsAre(Bucket(47, 1)));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 8fa1a1c..3749862 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2080,6 +2080,21 @@
     client_->NotifyThroughputTrackerResults(
         std::move(throughput_tracker_results));
   }
+
+  // Send all pending lag events waiting on the frame pointed by |frame_token|.
+  // It is posted as a task because LayerTreeHostImpl::DidPresentCompositorFrame
+  // is in the rendering critical path (it is called by AsyncLayerTreeFrameSink
+  // ::OnBeginFrame).
+  GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&LayerTreeHostImpl::LogAverageLagEvents,
+                     weak_factory_.GetWeakPtr(), frame_token, details));
+}
+
+void LayerTreeHostImpl::LogAverageLagEvents(
+    uint32_t frame_token,
+    const viz::FrameTimingDetails& details) {
+  lag_tracking_manager_.DidPresentCompositorFrame(frame_token, details);
 }
 
 void LayerTreeHostImpl::DidNotNeedBeginFrame() {
@@ -2364,6 +2379,10 @@
 
   auto compositor_frame = GenerateCompositorFrame(frame);
   frame->frame_token = compositor_frame.metadata.frame_token;
+
+  // Collect |latency_info| information for tracking
+  lag_tracking_manager_.CollectScrollEventsFromFrame(
+      frame->frame_token, compositor_frame.metadata.latency_info);
   layer_tree_frame_sink_->SubmitCompositorFrame(
       std::move(compositor_frame),
       /*hit_test_data_changed=*/false, debug_state_.show_hit_test_borders);
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 53f6f15..ec84793f 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <set>
+#include <string>
 #include <unordered_map>
 #include <utility>
 #include <vector>
@@ -29,6 +30,7 @@
 #include "cc/input/scrollbar_animation_controller.h"
 #include "cc/input/scrollbar_controller.h"
 #include "cc/layers/layer_collections.h"
+#include "cc/metrics/average_lag_tracking_manager.h"
 #include "cc/metrics/dropped_frame_counter.h"
 #include "cc/metrics/event_metrics.h"
 #include "cc/metrics/events_metrics_manager.h"
@@ -1127,6 +1129,11 @@
 
   void AllocateLocalSurfaceId();
 
+  // Log the AverageLag events from the frame identified by |frame_token| and
+  // the information in |details|.
+  void LogAverageLagEvents(uint32_t frame_token,
+                           const viz::FrameTimingDetails& details);
+
   const LayerTreeSettings settings_;
 
   // This is set to true only if:
@@ -1424,6 +1431,8 @@
   // invalidating PaintWorklets as the property values change.
   AnimatedPaintWorkletTracker paint_worklet_tracker_;
 
+  AverageLagTrackingManager lag_tracking_manager_;
+
   // Helper for de-jelly logic.
   DeJellyState de_jelly_state_;
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c08c7ed..d0c4d6b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -51396,14 +51396,19 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollBegin.Touch.AverageLag" units="pixels"
-    expires_after="M85">
-  <owner>eirage@chromium.org</owner>
-  <owner>nzolghadr@chromium.org</owner>
+    expires_after="M86">
+  <owner>flackr@chromium.org</owner>
+  <owner>joalmei@microsoft.com</owner>
+  <owner>input-dev@chromium.org</owner>
   <summary>
     Measures an average distance that represents how the page sticks to the
     finger when user scrolls. Only reports touch scrolling. See
     https://docs.google.com/document/d/154jddNiKtxELBvrjLz9v6A7sA1J3iwQQzySHgtY12Oo/
 
+    This is the lag caused by the Gesture Scroll Begin Event. In each touch
+    interaction there is a GSB followed by multiple Gesture Scroll Updates,
+    which are separately logged by Event.Latency.ScrollUpdate.Touch.AverageLag.
+
     Team: input-dev@chromium.org.
   </summary>
 </histogram>
@@ -52070,23 +52075,28 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.Touch.AverageLag" units="pixels"
-    expires_after="2020-11-29">
-  <owner>eirage@chromium.org</owner>
-  <owner>nzolghadr@chromium.org</owner>
+    expires_after="M86">
+  <owner>flackr@chromium.org</owner>
+  <owner>joalmei@microsoft.com</owner>
+  <owner>input-dev@chromium.org</owner>
   <summary>
     Measures an average distance that represents how the page sticks to the
     finger when user scrolls. Only reports touch scrolling. See
     https://docs.google.com/document/d/154jddNiKtxELBvrjLz9v6A7sA1J3iwQQzySHgtY12Oo/
 
+    This is the lag caused by the Gesture Scroll Updates occourring in the
+    lifetime of a scroll interaction.
+
     Team: input-dev@chromium.org.
   </summary>
 </histogram>
 
 <histogram
     name="Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative"
-    units="pixels" expires_after="M85">
-  <owner>eirage@chromium.org</owner>
-  <owner>nzolghadr@chromium.org</owner>
+    units="pixels" expires_after="M86">
+  <owner>flackr@chromium.org</owner>
+  <owner>joalmei@microsoft.com</owner>
+  <owner>input-dev@chromium.org</owner>
   <summary>
     Measures the negative impact on the AverageLag metric that is attributable
     to scroll prediction. This value has the same units as
@@ -52105,9 +52115,10 @@
 
 <histogram
     name="Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"
-    units="pixels" expires_after="M85">
-  <owner>eirage@chromium.org</owner>
-  <owner>nzolghadr@chromium.org</owner>
+    units="pixels" expires_after="M86">
+  <owner>flackr@chromium.org</owner>
+  <owner>joalmei@microsoft.com</owner>
+  <owner>input-dev@chromium.org</owner>
   <summary>
     Measures the positive impact on the AverageLag metric that is attributable
     to scroll prediction. This value has the same units as
diff --git a/ui/latency/BUILD.gn b/ui/latency/BUILD.gn
index 0e3639e5..c6bca839 100644
--- a/ui/latency/BUILD.gn
+++ b/ui/latency/BUILD.gn
@@ -7,8 +7,6 @@
 
 jumbo_source_set("latency") {
   sources = [
-    "average_lag_tracker.cc",
-    "average_lag_tracker.h",
     "latency_histogram_macros.h",
     "latency_info.cc",
     "latency_info.h",
@@ -33,10 +31,7 @@
 }
 
 test("latency_unittests") {
-  sources = [
-    "average_lag_tracker_unittest.cc",
-    "latency_info_unittest.cc",
-  ]
+  sources = [ "latency_info_unittest.cc" ]
 
   deps = [
     ":latency",
diff --git a/ui/latency/latency_tracker.cc b/ui/latency/latency_tracker.cc
index a724b21..7f059ff 100644
--- a/ui/latency/latency_tracker.cc
+++ b/ui/latency/latency_tracker.cc
@@ -308,11 +308,6 @@
     DCHECK_AND_RETURN_ON_FAIL(found_component);
   }
 
-  if (!IsInertialScroll(latency) && input_modality == "Touch") {
-    average_lag_tracker_.AddLatencyInFrame(latency, gpu_swap_begin_timestamp,
-                                           scroll_name);
-  }
-
   // Inertial and scrollbar scrolls are excluded from Ukm metrics.
   if ((input_modality == "Touch" && !IsInertialScroll(latency)) ||
       input_modality == "Wheel") {
diff --git a/ui/latency/latency_tracker.h b/ui/latency/latency_tracker.h
index e0bb699..88f56c4 100644
--- a/ui/latency/latency_tracker.h
+++ b/ui/latency/latency_tracker.h
@@ -7,7 +7,6 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
-#include "ui/latency/average_lag_tracker.h"
 #include "ui/latency/latency_info.h"
 
 namespace ui {
@@ -53,8 +52,6 @@
       const LatencyInfo& latency,
       bool top_controls_visible_height_changed);
 
-  AverageLagTracker average_lag_tracker_;
-
   DISALLOW_COPY_AND_ASSIGN(LatencyTracker);
 };