[go: nahoru, domu]

Reland: Report total event latency metrics to UI compositor

Whenever a presented frame in the browser compositor has associated
events, total latency for those events are reported back to UI
compositor. The UI compositor can decided if/how to report those
metrics.

This was originally landed as r1002811, but got reverted due to making a
bunch of interactive UI tests flaky. The reason for the flake was a
DCHECK that expected the presentation timestamp be strictly greater than
the event timestamp. However, it is conceivable that, at least in tests,
the two timestamps are equal. So, this reland changes the DCHECK to
expect the presentation timestamp is greater than or equal to the event
timestamp.

Bug: 1278417,1325180
Change-Id: I8e5719b5d6d81f7d3d15294a85016ade2b21ce26
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3650854
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Commit-Queue: Mohsen Izadi <mohsen@chromium.org>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1007406}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 2aa98b54..e52844d4 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -184,6 +184,8 @@
     "metrics/dropped_frame_counter.h",
     "metrics/event_latency_tracing_recorder.cc",
     "metrics/event_latency_tracing_recorder.h",
+    "metrics/event_latency_tracker.cc",
+    "metrics/event_latency_tracker.h",
     "metrics/event_metrics.cc",
     "metrics/event_metrics.h",
     "metrics/events_metrics_manager.cc",
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 0b041ad..96290db 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -21,6 +21,7 @@
 #include "cc/base/rolling_time_delta_history.h"
 #include "cc/metrics/dropped_frame_counter.h"
 #include "cc/metrics/event_latency_tracing_recorder.h"
+#include "cc/metrics/event_latency_tracker.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "cc/metrics/latency_ukm_reporter.h"
 #include "services/tracing/public/cpp/perfetto/macros.h"
@@ -654,7 +655,8 @@
 
   // Only report compositor latency metrics if the frame was produced.
   if (report_types_.any() &&
-      (should_report_histograms_ || global_trackers_.latency_ukm_reporter)) {
+      (should_report_histograms_ || global_trackers_.latency_ukm_reporter ||
+       global_trackers_.event_latency_tracker)) {
     DCHECK(stage_history_.size());
     DCHECK_EQ(SumOfStageHistory(), stage_history_.back().end_time -
                                        stage_history_.front().start_time);
@@ -663,6 +665,7 @@
                                 stage_history_.back().end_time);
 
     ReportCompositorLatencyMetrics();
+
     // Only report event latency metrics if the frame was presented.
     if (TestReportType(FrameReportType::kNonDroppedFrame))
       ReportEventLatencyMetrics();
@@ -900,55 +903,74 @@
         *processed_viz_breakdown_);
   }
 
-  if (!should_report_histograms_)
-    return;
-
-  const std::string total_latency_stage_name =
-      GetStageName(StageType::kTotalLatency);
-  const std::string total_latency_histogram_name =
-      "EventLatency." + total_latency_stage_name;
+  std::vector<EventLatencyTracker::LatencyData> latencies;
 
   for (const auto& event_metrics : events_metrics_) {
     DCHECK(event_metrics);
-    const std::string histogram_base_name =
-        GetEventLatencyHistogramBaseName(*event_metrics);
-    const int event_type_index = static_cast<int>(event_metrics->type());
     auto* scroll_metrics = event_metrics->AsScroll();
     auto* pinch_metrics = event_metrics->AsPinch();
-    const int gesture_type_index =
-        scroll_metrics
-            ? static_cast<int>(scroll_metrics->scroll_type())
-            : pinch_metrics ? static_cast<int>(pinch_metrics->pinch_type()) : 0;
-    const int event_histogram_index =
-        event_type_index * kEventLatencyGestureTypeCount + gesture_type_index;
 
     const base::TimeTicks generated_timestamp =
         event_metrics->GetDispatchStageTimestamp(
             EventMetrics::DispatchStage::kGenerated);
-    DCHECK_LT(generated_timestamp, total_latency_stage.end_time);
-
-    // Report total latency up to presentation for the event.
+    // Generally, we expect that the event timestamp is strictly smaller than
+    // the end timestamp of the last stage (i.e. total latency is positive);
+    // however, at least in tests, it is possible that the timestamps are the
+    // same and total latency is zero.
+    DCHECK_LE(generated_timestamp, total_latency_stage.end_time);
     const base::TimeDelta total_latency =
         total_latency_stage.end_time - generated_timestamp;
-    const std::string event_total_latency_histogram_name =
-        base::StrCat({histogram_base_name, ".", total_latency_stage_name});
-    // Note: There's a 1:1 mapping between `event_histogram_index` and
-    // `event_total_latency_histogram_name` which allows the use of
-    // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
-    STATIC_HISTOGRAM_POINTER_GROUP(
-        event_total_latency_histogram_name, event_histogram_index,
-        kMaxEventLatencyHistogramIndex,
-        AddTimeMicrosecondsGranularity(total_latency),
-        base::Histogram::FactoryMicrosecondsTimeGet(
-            event_total_latency_histogram_name, kEventLatencyHistogramMin,
-            kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
-            base::HistogramBase::kUmaTargetedHistogramFlag));
 
-    // Also, report total latency up to presentation for all event types in an
-    // aggregate histogram.
-    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
-        total_latency_histogram_name, total_latency, kEventLatencyHistogramMin,
-        kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount);
+    if (should_report_histograms_) {
+      const std::string histogram_base_name =
+          GetEventLatencyHistogramBaseName(*event_metrics);
+      const int event_type_index = static_cast<int>(event_metrics->type());
+      const int gesture_type_index =
+          scroll_metrics  ? static_cast<int>(scroll_metrics->scroll_type())
+          : pinch_metrics ? static_cast<int>(pinch_metrics->pinch_type())
+                          : 0;
+      const int event_histogram_index =
+          event_type_index * kEventLatencyGestureTypeCount + gesture_type_index;
+
+      const std::string total_latency_stage_name =
+          GetStageName(StageType::kTotalLatency);
+      const std::string event_total_latency_histogram_name =
+          base::StrCat({histogram_base_name, ".", total_latency_stage_name});
+      // Note: There's a 1:1 mapping between `event_histogram_index` and
+      // `event_total_latency_histogram_name` which allows the use of
+      // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
+      STATIC_HISTOGRAM_POINTER_GROUP(
+          event_total_latency_histogram_name, event_histogram_index,
+          kMaxEventLatencyHistogramIndex,
+          AddTimeMicrosecondsGranularity(total_latency),
+          base::Histogram::FactoryMicrosecondsTimeGet(
+              event_total_latency_histogram_name, kEventLatencyHistogramMin,
+              kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
+              base::HistogramBase::kUmaTargetedHistogramFlag));
+
+      // Also, report total latency up to presentation for all event types in an
+      // aggregate histogram.
+      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+          "EventLatency." + total_latency_stage_name, total_latency,
+          kEventLatencyHistogramMin, kEventLatencyHistogramMax,
+          kEventLatencyHistogramBucketCount);
+    }
+
+    if (global_trackers_.event_latency_tracker) {
+      EventLatencyTracker::LatencyData& latency_data =
+          latencies.emplace_back(event_metrics->type(), total_latency);
+
+      if (scroll_metrics)
+        latency_data.input_type = scroll_metrics->scroll_type();
+      else if (pinch_metrics)
+        latency_data.input_type = pinch_metrics->pinch_type();
+    }
+  }
+
+  if (!latencies.empty()) {
+    DCHECK(global_trackers_.event_latency_tracker);
+    global_trackers_.event_latency_tracker->ReportEventLatency(
+        std::move(latencies));
   }
 }
 
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 730dc890..9b41d47 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -31,14 +31,16 @@
 }
 
 namespace cc {
-class FrameSequenceTrackerCollection;
 class DroppedFrameCounter;
+class EventLatencyTracker;
+class FrameSequenceTrackerCollection;
 class LatencyUkmReporter;
 
 struct GlobalMetricsTrackers {
   raw_ptr<DroppedFrameCounter> dropped_frame_counter = nullptr;
   raw_ptr<LatencyUkmReporter> latency_ukm_reporter = nullptr;
   raw_ptr<FrameSequenceTrackerCollection> frame_sequence_trackers = nullptr;
+  raw_ptr<EventLatencyTracker> event_latency_tracker = nullptr;
 };
 
 // This is used for tracing and reporting the duration of pipeline stages within
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 680cbe8..3d1640c 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -24,6 +24,7 @@
 
 namespace cc {
 class DroppedFrameCounter;
+class EventLatencyTracker;
 class UkmManager;
 struct BeginMainFrameMetrics;
 struct FrameInfo;
@@ -109,6 +110,10 @@
     global_trackers_.frame_sequence_trackers = frame_sequence_trackers;
   }
 
+  void set_event_latency_tracker(EventLatencyTracker* event_latency_tracker) {
+    global_trackers_.event_latency_tracker = event_latency_tracker;
+  }
+
   void BeginMainFrameStarted(base::TimeTicks begin_main_frame_start_time) {
     begin_main_frame_start_time_ = begin_main_frame_start_time;
   }
diff --git a/cc/metrics/event_latency_tracker.cc b/cc/metrics/event_latency_tracker.cc
new file mode 100644
index 0000000..17e4cf4
--- /dev/null
+++ b/cc/metrics/event_latency_tracker.cc
@@ -0,0 +1,23 @@
+// Copyright 2022 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/event_latency_tracker.h"
+
+namespace cc {
+
+EventLatencyTracker::LatencyData::LatencyData(
+    EventMetrics::EventType event_type,
+    base::TimeDelta total_latency)
+    : event_type(event_type), total_latency(total_latency) {}
+
+EventLatencyTracker::LatencyData::~LatencyData() = default;
+
+EventLatencyTracker::LatencyData::LatencyData(LatencyData&&) = default;
+EventLatencyTracker::LatencyData& EventLatencyTracker::LatencyData::operator=(
+    LatencyData&&) = default;
+
+EventLatencyTracker::EventLatencyTracker() = default;
+EventLatencyTracker::~EventLatencyTracker() = default;
+
+}  // namespace cc
diff --git a/cc/metrics/event_latency_tracker.h b/cc/metrics/event_latency_tracker.h
new file mode 100644
index 0000000..1d42588
--- /dev/null
+++ b/cc/metrics/event_latency_tracker.h
@@ -0,0 +1,54 @@
+// Copyright 2022 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_EVENT_LATENCY_TRACKER_H_
+#define CC_METRICS_EVENT_LATENCY_TRACKER_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+
+namespace cc {
+
+// Used by `CompositorFrameReporter` to report event latency information back to
+// `LayerTreeHostImpl` and eventually to UI compositor.
+class CC_EXPORT EventLatencyTracker {
+ public:
+  struct CC_EXPORT LatencyData {
+    LatencyData(EventMetrics::EventType event_type,
+                base::TimeDelta total_latency);
+    ~LatencyData();
+
+    LatencyData(const LatencyData&) = delete;
+    LatencyData& operator=(const LatencyData&) = delete;
+
+    LatencyData(LatencyData&&);
+    LatencyData& operator=(LatencyData&&);
+
+    EventMetrics::EventType event_type;
+    base::TimeDelta total_latency;
+
+    // Type of the input device if the event is a scroll or a pinch event.
+    absl::variant<absl::monostate,
+                  ScrollEventMetrics::ScrollType,
+                  PinchEventMetrics::PinchType>
+        input_type;
+  };
+
+  EventLatencyTracker();
+  virtual ~EventLatencyTracker();
+
+  EventLatencyTracker(const EventLatencyTracker&) = delete;
+  EventLatencyTracker& operator=(const EventLatencyTracker&) = delete;
+
+  // Called every time a frame has latency metrics to report for events.
+  virtual void ReportEventLatency(std::vector<LatencyData> latencies) = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_EVENT_LATENCY_TRACKER_H_
diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h
index 751b239..e669425 100644
--- a/cc/test/fake_layer_tree_host_impl_client.h
+++ b/cc/test/fake_layer_tree_host_impl_client.h
@@ -5,6 +5,8 @@
 #ifndef CC_TEST_FAKE_LAYER_TREE_HOST_IMPL_CLIENT_H_
 #define CC_TEST_FAKE_LAYER_TREE_HOST_IMPL_CLIENT_H_
 
+#include <vector>
+
 #include "cc/trees/layer_tree_host_impl.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
@@ -59,6 +61,8 @@
       const base::flat_set<viz::FrameSinkId>& ids) override {}
   void ClearHistory() override {}
   size_t CommitDurationSampleCountForTesting() const override;
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override {}
 
   void reset_did_request_impl_side_invalidation() {
     did_request_impl_side_invalidation_ = false;
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index ec26ebb2..d9d534a 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -487,6 +487,9 @@
       uint32_t frame_token,
       const gfx::PresentationFeedback& feedback) override {}
 
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override {}
+
  private:
   explicit LayerTreeHostClientForTesting(TestHooks* test_hooks)
       : test_hooks_(test_hooks) {}
diff --git a/cc/test/stub_layer_tree_host_client.h b/cc/test/stub_layer_tree_host_client.h
index aa696aa0..a5df8c35 100644
--- a/cc/test/stub_layer_tree_host_client.h
+++ b/cc/test/stub_layer_tree_host_client.h
@@ -6,6 +6,7 @@
 #define CC_TEST_STUB_LAYER_TREE_HOST_CLIENT_H_
 
 #include <memory>
+#include <vector>
 
 #include "cc/paint/element_id.h"
 #include "cc/trees/layer_tree_host_client.h"
@@ -53,6 +54,8 @@
   void DidPresentCompositorFrame(
       uint32_t frame_token,
       const gfx::PresentationFeedback& feedback) override {}
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override {}
 };
 
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 08710fd..4512d68 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1106,6 +1106,11 @@
   client_->NotifyThroughputTrackerResults(std::move(results));
 }
 
+void LayerTreeHost::ReportEventLatency(
+    std::vector<EventLatencyTracker::LatencyData> latencies) {
+  client_->ReportEventLatency(std::move(latencies));
+}
+
 const base::WeakPtr<CompositorDelegateForInput>&
 LayerTreeHost::GetDelegateForInput() const {
   DCHECK(IsMainThread());
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 204a20a..010ebac 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -716,6 +716,8 @@
   void NotifyThroughputTrackerResults(CustomTrackerResults results);
   void NotifyTransitionRequestsFinished(
       const std::vector<uint32_t>& sequence_ids);
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies);
 
   LayerTreeHostClient* client() {
     DCHECK(IsMainThread());
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h
index d06f576..f52db87 100644
--- a/cc/trees/layer_tree_host_client.h
+++ b/cc/trees/layer_tree_host_client.h
@@ -6,10 +6,12 @@
 #define CC_TREES_LAYER_TREE_HOST_CLIENT_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "cc/input/browser_controls_state.h"
+#include "cc/metrics/event_latency_tracker.h"
 #include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "cc/trees/paint_holding_reason.h"
 #include "cc/trees/property_tree.h"
@@ -188,6 +190,8 @@
   // RecordEndOfFrameMetrics.
   virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0;
   virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0;
+  virtual void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) = 0;
 
   // Should only be implemented by Blink.
   virtual std::unique_ptr<WebVitalMetrics> GetWebVitalMetrics() = 0;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 61bc842..2bcfe92 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -462,14 +462,16 @@
   compositor_frame_reporting_controller_->SetFrameSequenceTrackerCollection(
       &frame_trackers_);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   const bool is_ui = settings.is_layer_tree_for_ui;
   if (is_ui) {
+    compositor_frame_reporting_controller_->set_event_latency_tracker(this);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     dropped_frame_counter_.EnableReporForUI();
     compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
         FrameInfo::SmoothEffectDrivingThread::kMain, true);
-  }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  }
 
   dropped_frame_counter_.set_total_counter(&total_frame_counter_);
   frame_trackers_.set_custom_tracker_results_added_callback(
@@ -2234,6 +2236,11 @@
   SetNeedsCommit();
 }
 
+void LayerTreeHostImpl::ReportEventLatency(
+    std::vector<EventLatencyTracker::LatencyData> latencies) {
+  client_->ReportEventLatency(std::move(latencies));
+}
+
 void LayerTreeHostImpl::OnCanDrawStateChangedForTree() {
   client_->OnCanDrawStateChanged(CanDraw());
 }
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 7361275..b234b87 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -32,6 +32,7 @@
 #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_latency_tracker.h"
 #include "cc/metrics/event_metrics.h"
 #include "cc/metrics/events_metrics_manager.h"
 #include "cc/metrics/frame_sequence_tracker_collection.h"
@@ -182,6 +183,9 @@
 
   virtual size_t CommitDurationSampleCountForTesting() const = 0;
 
+  virtual void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) = 0;
+
  protected:
   virtual ~LayerTreeHostImplClient() = default;
 };
@@ -195,7 +199,8 @@
                                     public VideoFrameControllerClient,
                                     public MutatorHostClient,
                                     public ImageAnimationController::Client,
-                                    public CompositorDelegateForInput {
+                                    public CompositorDelegateForInput,
+                                    public EventLatencyTracker {
  public:
   // This structure is used to build all the state required for producing a
   // single CompositorFrame. The |render_passes| list becomes the set of
@@ -568,6 +573,10 @@
   void OnCompositorFrameTransitionDirectiveProcessed(
       uint32_t sequence_id) override;
 
+  // EventLatencyTracker implementation.
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override;
+
   // Called from LayerTreeImpl.
   void OnCanDrawStateChangedForTree();
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index f24d489..85594d9 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -277,6 +277,8 @@
   void NotifyPaintWorkletStateChange(
       Scheduler::PaintWorkletState state) override {}
   void NotifyThroughputTrackerResults(CustomTrackerResults results) override {}
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override {}
 
   void DidObserveFirstScrollDelay(
       base::TimeDelta first_scroll_delay,
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index db40235..ad45570 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -310,6 +310,14 @@
   NOTREACHED();
 }
 
+void ProxyImpl::ReportEventLatency(
+    std::vector<EventLatencyTracker::LatencyData> latencies) {
+  DCHECK(IsImplThread());
+  MainThreadTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&ProxyMain::ReportEventLatency,
+                                proxy_main_weak_ptr_, std::move(latencies)));
+}
+
 void ProxyImpl::NotifyReadyToCommitOnImpl(
     CompletionEvent* completion_event,
     std::unique_ptr<CommitState> commit_state,
diff --git a/cc/trees/proxy_impl.h b/cc/trees/proxy_impl.h
index 6240406..e2a678d 100644
--- a/cc/trees/proxy_impl.h
+++ b/cc/trees/proxy_impl.h
@@ -134,6 +134,8 @@
   bool IsInSynchronousComposite() const override;
   void FrameSinksToThrottleUpdated(
       const base::flat_set<viz::FrameSinkId>& id) override;
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override;
 
   // SchedulerClient implementation
   bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index 165278b..4bd11516 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -480,6 +480,11 @@
                                                first_scroll_timestamp);
 }
 
+void ProxyMain::ReportEventLatency(
+    std::vector<EventLatencyTracker::LatencyData> latencies) {
+  layer_tree_host_->ReportEventLatency(std::move(latencies));
+}
+
 bool ProxyMain::IsStarted() const {
   DCHECK(IsMainThread());
   return started_;
diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h
index 7b6736a..1f0be86 100644
--- a/cc/trees/proxy_main.h
+++ b/cc/trees/proxy_main.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "cc/cc_export.h"
 #include "cc/input/browser_controls_state.h"
+#include "cc/metrics/event_latency_tracker.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/paint_holding_reason.h"
 #include "cc/trees/proxy.h"
@@ -71,6 +72,8 @@
   void NotifyThroughputTrackerResults(CustomTrackerResults results);
   void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay,
                                   base::TimeTicks first_scroll_timestamp);
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies);
 
   CommitPipelineStage max_requested_pipeline_stage() const {
     return max_requested_pipeline_stage_;
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 15f1bf4..0066ee6 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -894,6 +894,14 @@
       ->CommitDurationSampleCountForTesting();  // IN-TEST
 }
 
+void SingleThreadProxy::ReportEventLatency(
+    std::vector<EventLatencyTracker::LatencyData> latencies) {
+  DCHECK(!task_runner_provider_->HasImplThread() ||
+         task_runner_provider_->IsImplThread());
+  DebugScopedSetMainThread main(task_runner_provider_);
+  layer_tree_host_->ReportEventLatency(std::move(latencies));
+}
+
 void SingleThreadProxy::SetRenderFrameObserver(
     std::unique_ptr<RenderFrameMetadataObserver> observer) {
   DCHECK(task_runner_provider_->IsMainThread());
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 0b0c476..fef5133 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -147,6 +147,8 @@
       const base::flat_set<viz::FrameSinkId>& ids) override;
   void ClearHistory() override;
   size_t CommitDurationSampleCountForTesting() const override;
+  void ReportEventLatency(
+      std::vector<EventLatencyTracker::LatencyData> latencies) override;
 
   void RequestNewLayerTreeFrameSink();
 
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 51e4c88e..df7ddf8 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -164,6 +164,8 @@
   void DidObserveFirstScrollDelay(
       base::TimeDelta first_scroll_delay,
       base::TimeTicks first_scroll_timestamp) override {}
+  void ReportEventLatency(
+      std::vector<cc::EventLatencyTracker::LatencyData> latencies) override {}
 
   // LayerTreeHostSingleThreadClient implementation.
   void DidSubmitCompositorFrame() override;
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index ae5f6589..e0752fe 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -474,19 +474,22 @@
   // this test to fail. This observer will let us know if this is happening.
   SpuriousMouseMoveEventObserver mouse_observer(GetRenderWidgetHost());
 
-  base::TimeTicks timestamp = ui::EventTimeForNow();
+  // TODO(crbug.com/1322921): Use a mock timer to generate timestamps for
+  // events. This would need injecting the mock timer into
+  // `cc::CompositorFrameReportingController`.
   ui::TouchEvent press(
       ui::ET_TOUCH_PRESSED,
-      gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5), timestamp,
+      gfx::Point(bounds.x() + bounds.width() / 2, bounds.y() + 5),
+      ui::EventTimeForNow(),
       ui::PointerDetails(ui::EventPointerType::kTouch, 0));
   ui::EventDispatchDetails details = sink->OnEventFromSource(&press);
   ASSERT_FALSE(details.dispatcher_destroyed);
   EXPECT_EQ(1, GetCurrentIndex());
 
-  timestamp += base::Milliseconds(10);
-  ui::TouchEvent move1(
-      ui::ET_TOUCH_MOVED, gfx::Point(bounds.right() - 10, bounds.y() + 5),
-      timestamp, ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+  ui::TouchEvent move1(ui::ET_TOUCH_MOVED,
+                       gfx::Point(bounds.right() - 10, bounds.y() + 5),
+                       ui::EventTimeForNow(),
+                       ui::PointerDetails(ui::EventPointerType::kTouch, 0));
   details = sink->OnEventFromSource(&move1);
   ASSERT_FALSE(details.dispatcher_destroyed);
   EXPECT_EQ(1, GetCurrentIndex());
@@ -494,30 +497,27 @@
   // Swipe back from the right edge, back to the left edge, back to the right
   // edge.
 
-  for (int x = bounds.right() - 10; x >= bounds.x() + 10; x-= 10) {
-    timestamp += base::Milliseconds(10);
+  for (int x = bounds.right() - 10; x >= bounds.x() + 10; x -= 10) {
     ui::TouchEvent inc(ui::ET_TOUCH_MOVED, gfx::Point(x, bounds.y() + 5),
-                       timestamp,
+                       ui::EventTimeForNow(),
                        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
     details = sink->OnEventFromSource(&inc);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(1, GetCurrentIndex());
   }
 
-  for (int x = bounds.x() + 10; x <= bounds.width() - 10; x+= 10) {
-    timestamp += base::Milliseconds(10);
+  for (int x = bounds.x() + 10; x <= bounds.width() - 10; x += 10) {
     ui::TouchEvent inc(ui::ET_TOUCH_MOVED, gfx::Point(x, bounds.y() + 5),
-                       timestamp,
+                       ui::EventTimeForNow(),
                        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
     details = sink->OnEventFromSource(&inc);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(1, GetCurrentIndex());
   }
 
-  for (int x = bounds.width() - 10; x >= bounds.x() + 10; x-= 10) {
-    timestamp += base::Milliseconds(10);
+  for (int x = bounds.width() - 10; x >= bounds.x() + 10; x -= 10) {
     ui::TouchEvent inc(ui::ET_TOUCH_MOVED, gfx::Point(x, bounds.y() + 5),
-                       timestamp,
+                       ui::EventTimeForNow(),
                        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
     details = sink->OnEventFromSource(&inc);
     ASSERT_FALSE(details.dispatcher_destroyed);
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
index 08aebbfb..d502f77 100644
--- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
@@ -387,6 +387,13 @@
     delegate_->RunPaintBenchmark(repeat_count, result);
 }
 
+void LayerTreeView::ReportEventLatency(
+    std::vector<cc::EventLatencyTracker::LatencyData> latencies) {
+  // EventLatency metrics for the renderers are reported in
+  // `CompositorFrameReporter`, so this functions should not be called.
+  NOTREACHED();
+}
+
 void LayerTreeView::DidRunBeginMainFrame() {
   if (!delegate_ || !web_main_thread_scheduler_)
     return;
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
index de3c905..aba6751 100644
--- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
+++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
@@ -108,6 +108,8 @@
       base::TimeTicks first_scroll_timestamp) override;
   void RunPaintBenchmark(int repeat_count,
                          cc::PaintBenchmarkResult& result) override;
+  void ReportEventLatency(
+      std::vector<cc::EventLatencyTracker::LatencyData> latencies) override;
 
   // cc::LayerTreeHostSingleThreadClient implementation.
   void DidSubmitCompositorFrame() override;
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 67d7e64..39771c3 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -755,6 +755,11 @@
     ReportMetricsForTracker(pair.first, std::move(pair.second));
 }
 
+void Compositor::ReportEventLatency(
+    std::vector<cc::EventLatencyTracker::LatencyData> latencies) {
+  // TODO(crbug.com/1321193): Report EventLatency as appropriate.
+}
+
 void Compositor::DidReceiveCompositorFrameAck() {
   ++activated_frame_count_;
   for (auto& observer : observer_list_)
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 80bff73a..2f6d2ee 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -384,6 +384,8 @@
   void DidObserveFirstScrollDelay(
       base::TimeDelta first_scroll_delay,
       base::TimeTicks first_scroll_timestamp) override {}
+  void ReportEventLatency(
+      std::vector<cc::EventLatencyTracker::LatencyData> latencies) override;
 
   // cc::LayerTreeHostSingleThreadClient implementation.
   void DidSubmitCompositorFrame() override;