[go: nahoru, domu]

[Refactor] Break frame_sequence_tracker.h into 3 files

The frame_sequence_tracker.h file has 3 classes in it:
1. FrameSequenceMetrics: this class keeps track of necessary info to
   report throughput metrics.
2. FrameSequenceTracker: this class owns a FrameSequenceMetrics
   object. It tracks various frame productions and record these info
   in the FrameSequenceMetrics object.
3. FrameSequenceTrackerCollection: this class contains a set of
   FrameSequenceTrackers.

Other high level classes like LayerTreeHostImpl just talk to
FrameSequenceTrackerCollection.

This CL breaks the file into 3 classes, to make it more obvious.
Basically, frame_sequence_metrics.h is the lowest level and contains
all fundamental types and operations.

Bug: None
Change-Id: I94ac5902ab9f4b6e5857b8c6878cbed2916fb5b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2155213
Reviewed-by: Yi Gu <yigu@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: vmpstr <vmpstr@chromium.org>
Commit-Queue: Xida Chen <xidachen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#760524}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 2f8df2e..5634a507 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -162,8 +162,12 @@
     "metrics/event_metrics.h",
     "metrics/events_metrics_manager.cc",
     "metrics/events_metrics_manager.h",
+    "metrics/frame_sequence_metrics.cc",
+    "metrics/frame_sequence_metrics.h",
     "metrics/frame_sequence_tracker.cc",
     "metrics/frame_sequence_tracker.h",
+    "metrics/frame_sequence_tracker_collection.cc",
+    "metrics/frame_sequence_tracker_collection.h",
     "metrics/latency_ukm_reporter.cc",
     "metrics/latency_ukm_reporter.h",
     "metrics/throughput_ukm_reporter.cc",
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index ba7d4132..c4c8e82 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -15,7 +15,7 @@
 #include "cc/cc_export.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/metrics/event_metrics.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 #include "cc/scheduler/scheduler.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/frame_timing_details.h"
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 0b6da8d..d3edd9cd 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -12,13 +12,14 @@
 #include "cc/cc_export.h"
 #include "cc/metrics/compositor_frame_reporter.h"
 #include "cc/metrics/event_metrics.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 
 namespace viz {
 struct FrameTimingDetails;
 }
 
 namespace cc {
+class UkmManager;
 struct BeginMainFrameMetrics;
 
 // This is used for managing simultaneous CompositorFrameReporter instances
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc
new file mode 100644
index 0000000..94e34708
--- /dev/null
+++ b/cc/metrics/frame_sequence_metrics.cc
@@ -0,0 +1,360 @@
+// 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/frame_sequence_metrics.h"
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/traced_value.h"
+#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/throughput_ukm_reporter.h"
+
+namespace cc {
+
+namespace {
+
+// Avoid reporting any throughput metric for sequences that do not have a
+// sufficient number of frames.
+constexpr int kMinFramesForThroughputMetric = 100;
+
+constexpr int kBuiltinSequenceNum =
+    static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
+constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum;
+
+int GetIndexForMetric(FrameSequenceMetrics::ThreadType thread_type,
+                      FrameSequenceTrackerType type) {
+  if (thread_type == FrameSequenceMetrics::ThreadType::kMain)
+    return static_cast<int>(type);
+  if (thread_type == FrameSequenceMetrics::ThreadType::kCompositor)
+    return static_cast<int>(type) + kBuiltinSequenceNum;
+  return static_cast<int>(type) + 2 * kBuiltinSequenceNum;
+}
+
+std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) {
+  return base::StrCat(
+      {"Graphics.Smoothness.Checkerboarding.",
+       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
+}
+
+std::string GetThroughputHistogramName(FrameSequenceTrackerType type,
+                                       const char* thread_name) {
+  return base::StrCat(
+      {"Graphics.Smoothness.PercentDroppedFrames.", thread_name, ".",
+       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
+}
+
+std::string GetFrameSequenceLengthHistogramName(FrameSequenceTrackerType type) {
+  return base::StrCat(
+      {"Graphics.Smoothness.FrameSequenceLength.",
+       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
+}
+
+bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type,
+                              FrameSequenceMetrics::ThreadType thread_type) {
+  if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation)
+    return thread_type == FrameSequenceMetrics::ThreadType::kCompositor;
+
+  if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation ||
+      sequence_type == FrameSequenceTrackerType::kRAF)
+    return thread_type == FrameSequenceMetrics::ThreadType::kMain;
+
+  return false;
+}
+
+bool ShouldReportForInteraction(FrameSequenceMetrics* metrics,
+                                FrameSequenceMetrics::ThreadType thread_type) {
+  const auto sequence_type = metrics->type();
+
+  // For touch/wheel scroll, the slower thread is the one we want to report. For
+  // pinch-zoom, it's the compositor-thread.
+  if (sequence_type == FrameSequenceTrackerType::kTouchScroll ||
+      sequence_type == FrameSequenceTrackerType::kWheelScroll)
+    return thread_type == metrics->GetEffectiveThread();
+
+  if (sequence_type == FrameSequenceTrackerType::kPinchZoom)
+    return thread_type == FrameSequenceMetrics::ThreadType::kCompositor;
+
+  return false;
+}
+
+bool IsInteractionType(FrameSequenceTrackerType sequence_type) {
+  return sequence_type == FrameSequenceTrackerType::kTouchScroll ||
+         sequence_type == FrameSequenceTrackerType::kWheelScroll ||
+         sequence_type == FrameSequenceTrackerType::kPinchZoom;
+}
+
+}  // namespace
+
+FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type,
+                                           ThroughputUkmReporter* ukm_reporter)
+    : type_(type), throughput_ukm_reporter_(ukm_reporter) {
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+      "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "name",
+      FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type_));
+}
+
+FrameSequenceMetrics::~FrameSequenceMetrics() {
+  if (HasDataLeftForReporting()) {
+    ReportMetrics();
+  }
+}
+
+void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) {
+  DCHECK(type_ == FrameSequenceTrackerType::kTouchScroll ||
+         type_ == FrameSequenceTrackerType::kWheelScroll ||
+         type_ == FrameSequenceTrackerType::kScrollbarScroll);
+  DCHECK_EQ(scrolling_thread_, ThreadType::kUnknown);
+  scrolling_thread_ = scrolling_thread;
+}
+
+FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread()
+    const {
+  switch (type_) {
+    case FrameSequenceTrackerType::kCompositorAnimation:
+    case FrameSequenceTrackerType::kPinchZoom:
+      return ThreadType::kCompositor;
+
+    case FrameSequenceTrackerType::kMainThreadAnimation:
+    case FrameSequenceTrackerType::kRAF:
+    case FrameSequenceTrackerType::kVideo:
+      return ThreadType::kMain;
+
+    case FrameSequenceTrackerType::kTouchScroll:
+    case FrameSequenceTrackerType::kScrollbarScroll:
+    case FrameSequenceTrackerType::kWheelScroll:
+      return scrolling_thread_;
+
+    case FrameSequenceTrackerType::kUniversal:
+      return ThreadType::kSlower;
+
+    case FrameSequenceTrackerType::kCustom:
+    case FrameSequenceTrackerType::kMaxType:
+      NOTREACHED();
+  }
+  return ThreadType::kUnknown;
+}
+
+void FrameSequenceMetrics::Merge(
+    std::unique_ptr<FrameSequenceMetrics> metrics) {
+  DCHECK_EQ(type_, metrics->type_);
+  DCHECK_EQ(GetEffectiveThread(), metrics->GetEffectiveThread());
+  impl_throughput_.Merge(metrics->impl_throughput_);
+  main_throughput_.Merge(metrics->main_throughput_);
+  aggregated_throughput_.Merge(metrics->aggregated_throughput_);
+  frames_checkerboarded_ += metrics->frames_checkerboarded_;
+
+  // Reset the state of |metrics| before destroying it, so that it doesn't end
+  // up reporting the metrics.
+  metrics->impl_throughput_ = {};
+  metrics->main_throughput_ = {};
+  metrics->aggregated_throughput_ = {};
+  metrics->frames_checkerboarded_ = 0;
+}
+
+bool FrameSequenceMetrics::HasEnoughDataForReporting() const {
+  return impl_throughput_.frames_expected >= kMinFramesForThroughputMetric ||
+         main_throughput_.frames_expected >= kMinFramesForThroughputMetric;
+}
+
+bool FrameSequenceMetrics::HasDataLeftForReporting() const {
+  return impl_throughput_.frames_expected > 0 ||
+         main_throughput_.frames_expected > 0;
+}
+
+void FrameSequenceMetrics::ComputeAggregatedThroughput() {
+  // Whenever we are expecting and producing main frames, we are expecting and
+  // producing impl frames as well. As an example, if we expect one main frame
+  // to be produced, and when that main frame is presented, we are expecting 3
+  // impl frames, then the number of expected frames is 3 for the aggregated
+  // throughput.
+  aggregated_throughput_.frames_expected = impl_throughput_.frames_expected;
+  DCHECK_LE(aggregated_throughput_.frames_produced,
+            aggregated_throughput_.frames_produced);
+}
+
+void FrameSequenceMetrics::ReportMetrics() {
+  DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected);
+  DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
+  TRACE_EVENT_NESTABLE_ASYNC_END2(
+      "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "args",
+      ThroughputData::ToTracedValue(impl_throughput_, main_throughput_),
+      "checkerboard", frames_checkerboarded_);
+
+  // Data for kCustom typed tracker is handled by caller instead being
+  // reported here.
+  if (type_ == FrameSequenceTrackerType::kCustom)
+    return;
+
+  ComputeAggregatedThroughput();
+
+  // Report the throughput metrics.
+  base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram(
+      this, ThreadType::kCompositor,
+      GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor, type_),
+      impl_throughput_);
+  base::Optional<int> main_throughput_percent = ThroughputData::ReportHistogram(
+      this, ThreadType::kMain,
+      GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
+      main_throughput_);
+
+  // Report for the 'slower thread' for the metrics where it makes sense.
+  bool should_report_slower_thread =
+      IsInteractionType(type_) || type_ == FrameSequenceTrackerType::kUniversal;
+  base::Optional<int> aggregated_throughput_percent;
+  if (should_report_slower_thread) {
+    aggregated_throughput_percent = ThroughputData::ReportHistogram(
+        this, ThreadType::kSlower,
+        GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_),
+        aggregated_throughput_);
+    if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) {
+      throughput_ukm_reporter_->ReportThroughputUkm(
+          aggregated_throughput_percent, impl_throughput_percent,
+          main_throughput_percent, type_);
+    }
+  }
+
+  // Report for the 'scrolling thread' for the scrolling interactions.
+  if (scrolling_thread_ != ThreadType::kUnknown) {
+    base::Optional<int> scrolling_thread_throughput;
+    switch (scrolling_thread_) {
+      case ThreadType::kCompositor:
+        scrolling_thread_throughput = impl_throughput_percent;
+        break;
+      case ThreadType::kMain:
+        scrolling_thread_throughput = main_throughput_percent;
+        break;
+      case ThreadType::kSlower:
+      case ThreadType::kUnknown:
+        NOTREACHED();
+        break;
+    }
+    if (scrolling_thread_throughput.has_value()) {
+      // It's OK to use the UMA histogram in the following code while still
+      // using |GetThroughputHistogramName()| to get the name of the metric,
+      // since the input-params to the function never change at runtime.
+      if (type_ == FrameSequenceTrackerType::kWheelScroll) {
+        UMA_HISTOGRAM_PERCENTAGE(
+            GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll,
+                                       "ScrollingThread"),
+            scrolling_thread_throughput.value());
+      } else if (type_ == FrameSequenceTrackerType::kTouchScroll) {
+        UMA_HISTOGRAM_PERCENTAGE(
+            GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll,
+                                       "ScrollingThread"),
+            scrolling_thread_throughput.value());
+      } else {
+        DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll);
+        UMA_HISTOGRAM_PERCENTAGE(
+            GetThroughputHistogramName(
+                FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
+            scrolling_thread_throughput.value());
+      }
+    }
+  }
+
+  // Report the checkerboarding metrics.
+  if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
+    const int checkerboarding_percent = static_cast<int>(
+        100 * frames_checkerboarded_ / impl_throughput_.frames_expected);
+    STATIC_HISTOGRAM_POINTER_GROUP(
+        GetCheckerboardingHistogramName(type_), static_cast<int>(type_),
+        static_cast<int>(FrameSequenceTrackerType::kMaxType),
+        Add(checkerboarding_percent),
+        base::LinearHistogram::FactoryGet(
+            GetCheckerboardingHistogramName(type_), 1, 100, 101,
+            base::HistogramBase::kUmaTargetedHistogramFlag));
+    frames_checkerboarded_ = 0;
+  }
+
+  // Reset the metrics that reach reporting threshold.
+  if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric)
+    impl_throughput_ = {};
+  if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric)
+    main_throughput_ = {};
+  if (aggregated_throughput_percent.has_value())
+    aggregated_throughput_ = {};
+}
+
+base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
+    FrameSequenceMetrics* metrics,
+    ThreadType thread_type,
+    int metric_index,
+    const ThroughputData& data) {
+  const auto sequence_type = metrics->type();
+  DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
+
+  STATIC_HISTOGRAM_POINTER_GROUP(
+      GetFrameSequenceLengthHistogramName(sequence_type),
+      static_cast<int>(sequence_type),
+      static_cast<int>(FrameSequenceTrackerType::kMaxType),
+      Add(data.frames_expected),
+      base::Histogram::FactoryGet(
+          GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50,
+          base::HistogramBase::kUmaTargetedHistogramFlag));
+
+  if (data.frames_expected < kMinFramesForThroughputMetric)
+    return base::nullopt;
+
+  // Throughput means the percent of frames that was expected to show on the
+  // screen but didn't. In other words, the lower the throughput is, the
+  // smoother user experience.
+  const int percent =
+      std::ceil(100 * (data.frames_expected - data.frames_produced) /
+                static_cast<double>(data.frames_expected));
+
+  const bool is_animation =
+      ShouldReportForAnimation(sequence_type, thread_type);
+  const bool is_interaction = ShouldReportForInteraction(metrics, thread_type);
+
+  ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter();
+
+  if (is_animation) {
+    UMA_HISTOGRAM_PERCENTAGE(
+        "Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent);
+    if (ukm_reporter) {
+      ukm_reporter->ReportAggregateThroughput(AggregationType::kAllAnimations,
+                                              percent);
+    }
+  }
+
+  if (is_interaction) {
+    UMA_HISTOGRAM_PERCENTAGE(
+        "Graphics.Smoothness.PercentDroppedFrames.AllInteractions", percent);
+    if (ukm_reporter) {
+      ukm_reporter->ReportAggregateThroughput(AggregationType::kAllInteractions,
+                                              percent);
+    }
+  }
+
+  if (is_animation || is_interaction) {
+    UMA_HISTOGRAM_PERCENTAGE(
+        "Graphics.Smoothness.PercentDroppedFrames.AllSequences", percent);
+    if (ukm_reporter) {
+      ukm_reporter->ReportAggregateThroughput(AggregationType::kAllSequences,
+                                              percent);
+    }
+  }
+
+  if (!is_animation && !IsInteractionType(sequence_type) &&
+      sequence_type != FrameSequenceTrackerType::kUniversal &&
+      sequence_type != FrameSequenceTrackerType::kVideo) {
+    return base::nullopt;
+  }
+
+  const char* thread_name =
+      thread_type == ThreadType::kCompositor
+          ? "CompositorThread"
+          : thread_type == ThreadType::kMain ? "MainThread" : "SlowerThread";
+  STATIC_HISTOGRAM_POINTER_GROUP(
+      GetThroughputHistogramName(sequence_type, thread_name), metric_index,
+      kMaximumHistogramIndex, Add(percent),
+      base::LinearHistogram::FactoryGet(
+          GetThroughputHistogramName(sequence_type, thread_name), 1, 100, 101,
+          base::HistogramBase::kUmaTargetedHistogramFlag));
+  return percent;
+}
+
+}  // namespace cc
diff --git a/cc/metrics/frame_sequence_metrics.h b/cc/metrics/frame_sequence_metrics.h
new file mode 100644
index 0000000..e7312215
--- /dev/null
+++ b/cc/metrics/frame_sequence_metrics.h
@@ -0,0 +1,131 @@
+// 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_FRAME_SEQUENCE_METRICS_H_
+#define CC_METRICS_FRAME_SEQUENCE_METRICS_H_
+
+#include "base/optional.h"
+#include "base/trace_event/traced_value.h"
+#include "cc/cc_export.h"
+
+namespace cc {
+class ThroughputUkmReporter;
+
+enum class FrameSequenceTrackerType {
+  // Used as an enum for metrics. DO NOT reorder or delete values. Rather,
+  // add them at the end and increment kMaxType.
+  kCompositorAnimation = 0,
+  kMainThreadAnimation = 1,
+  kPinchZoom = 2,
+  kRAF = 3,
+  kTouchScroll = 4,
+  kUniversal = 5,
+  kVideo = 6,
+  kWheelScroll = 7,
+  kScrollbarScroll = 8,
+  kCustom = 9,  // Note that the metrics for kCustom are not reported on UMA,
+                // and instead are dispatched back to the LayerTreeHostClient.
+  kMaxType
+};
+
+class CC_EXPORT FrameSequenceMetrics {
+ public:
+  FrameSequenceMetrics(FrameSequenceTrackerType type,
+                       ThroughputUkmReporter* ukm_reporter);
+  ~FrameSequenceMetrics();
+
+  FrameSequenceMetrics(const FrameSequenceMetrics&) = delete;
+  FrameSequenceMetrics& operator=(const FrameSequenceMetrics&) = delete;
+
+  enum class ThreadType { kMain, kCompositor, kSlower, kUnknown };
+
+  struct ThroughputData {
+    static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue(
+        const ThroughputData& impl,
+        const ThroughputData& main);
+
+    // Returns the throughput in percent, a return value of base::nullopt
+    // indicates that no throughput metric is reported.
+    static base::Optional<int> ReportHistogram(FrameSequenceMetrics* metrics,
+                                               ThreadType thread_type,
+                                               int metric_index,
+                                               const ThroughputData& data);
+
+    void Merge(const ThroughputData& data) {
+      frames_expected += data.frames_expected;
+      frames_produced += data.frames_produced;
+#if DCHECK_IS_ON()
+      frames_processed += data.frames_processed;
+      frames_received += data.frames_received;
+#endif
+    }
+
+    // Tracks the number of frames that were expected to be shown during this
+    // frame-sequence.
+    uint32_t frames_expected = 0;
+
+    // Tracks the number of frames that were actually presented to the user
+    // during this frame-sequence.
+    uint32_t frames_produced = 0;
+
+#if DCHECK_IS_ON()
+    // Tracks the number of frames that is either submitted or reported as no
+    // damage.
+    uint32_t frames_processed = 0;
+
+    // Tracks the number of begin-frames that are received.
+    uint32_t frames_received = 0;
+#endif
+  };
+
+  void SetScrollingThread(ThreadType thread);
+
+  // Returns the 'effective thread' for the metrics (i.e. the thread most
+  // relevant for this metric).
+  ThreadType GetEffectiveThread() const;
+
+  void Merge(std::unique_ptr<FrameSequenceMetrics> metrics);
+  bool HasEnoughDataForReporting() const;
+  bool HasDataLeftForReporting() const;
+  // Report related metrics: throughput, checkboarding...
+  void ReportMetrics();
+  void ComputeAggregatedThroughputForTesting() {
+    ComputeAggregatedThroughput();
+  }
+
+  ThroughputData& impl_throughput() { return impl_throughput_; }
+  ThroughputData& main_throughput() { return main_throughput_; }
+  ThroughputData& aggregated_throughput() { return aggregated_throughput_; }
+  void add_checkerboarded_frames(int64_t frames) {
+    frames_checkerboarded_ += frames;
+  }
+  uint32_t frames_checkerboarded() const { return frames_checkerboarded_; }
+
+  FrameSequenceTrackerType type() const { return type_; }
+  ThroughputUkmReporter* ukm_reporter() const {
+    return throughput_ukm_reporter_;
+  }
+
+ private:
+  void ComputeAggregatedThroughput();
+  const FrameSequenceTrackerType type_;
+
+  // Pointer to the reporter owned by the FrameSequenceTrackerCollection.
+  ThroughputUkmReporter* const throughput_ukm_reporter_;
+
+  ThroughputData impl_throughput_;
+  ThroughputData main_throughput_;
+  // The aggregated throughput for the main/compositor thread.
+  ThroughputData aggregated_throughput_;
+
+  ThreadType scrolling_thread_ = ThreadType::kUnknown;
+
+  // Tracks the number of produced frames that had some amount of
+  // checkerboarding, and how many frames showed such checkerboarded frames.
+  uint32_t frames_checkerboarded_ = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_FRAME_SEQUENCE_METRICS_H_
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index d8404c1..71f9609 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -4,16 +4,10 @@
 
 #include "cc/metrics/frame_sequence_tracker.h"
 
-#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/strcat.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
-#include "cc/metrics/compositor_frame_reporting_controller.h"
-#include "cc/metrics/throughput_ukm_reporter.h"
-#include "cc/trees/ukm_manager.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame_metadata.h"
 #include "ui/gfx/presentation_feedback.h"
@@ -60,566 +54,6 @@
   }
 }
 
-namespace {
-
-// Avoid reporting any throughput metric for sequences that do not have a
-// sufficient number of frames.
-constexpr int kMinFramesForThroughputMetric = 100;
-
-constexpr int kBuiltinSequenceNum =
-    static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
-constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum;
-
-int GetIndexForMetric(FrameSequenceMetrics::ThreadType thread_type,
-                      FrameSequenceTrackerType type) {
-  if (thread_type == FrameSequenceMetrics::ThreadType::kMain)
-    return static_cast<int>(type);
-  if (thread_type == FrameSequenceMetrics::ThreadType::kCompositor)
-    return static_cast<int>(type) + kBuiltinSequenceNum;
-  return static_cast<int>(type) + 2 * kBuiltinSequenceNum;
-}
-
-std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) {
-  return base::StrCat(
-      {"Graphics.Smoothness.Checkerboarding.",
-       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
-}
-
-std::string GetThroughputHistogramName(FrameSequenceTrackerType type,
-                                       const char* thread_name) {
-  return base::StrCat(
-      {"Graphics.Smoothness.PercentDroppedFrames.", thread_name, ".",
-       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
-}
-
-std::string GetFrameSequenceLengthHistogramName(FrameSequenceTrackerType type) {
-  return base::StrCat(
-      {"Graphics.Smoothness.FrameSequenceLength.",
-       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
-}
-
-bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type,
-                              FrameSequenceMetrics::ThreadType thread_type) {
-  if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation)
-    return thread_type == FrameSequenceMetrics::ThreadType::kCompositor;
-
-  if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation ||
-      sequence_type == FrameSequenceTrackerType::kRAF)
-    return thread_type == FrameSequenceMetrics::ThreadType::kMain;
-
-  return false;
-}
-
-bool ShouldReportForInteraction(FrameSequenceMetrics* metrics,
-                                FrameSequenceMetrics::ThreadType thread_type) {
-  const auto sequence_type = metrics->type();
-
-  // For touch/wheel scroll, the slower thread is the one we want to report. For
-  // pinch-zoom, it's the compositor-thread.
-  if (sequence_type == FrameSequenceTrackerType::kTouchScroll ||
-      sequence_type == FrameSequenceTrackerType::kWheelScroll)
-    return thread_type == metrics->GetEffectiveThread();
-
-  if (sequence_type == FrameSequenceTrackerType::kPinchZoom)
-    return thread_type == FrameSequenceMetrics::ThreadType::kCompositor;
-
-  return false;
-}
-
-bool IsInteractionType(FrameSequenceTrackerType sequence_type) {
-  return sequence_type == FrameSequenceTrackerType::kTouchScroll ||
-         sequence_type == FrameSequenceTrackerType::kWheelScroll ||
-         sequence_type == FrameSequenceTrackerType::kPinchZoom;
-}
-
-}  // namespace
-
-////////////////////////////////////////////////////////////////////////////////
-// FrameSequenceMetrics
-
-FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type,
-                                           ThroughputUkmReporter* ukm_reporter)
-    : type_(type),
-      throughput_ukm_reporter_(ukm_reporter) {
-  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
-      "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "name",
-      FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type_));
-}
-
-FrameSequenceMetrics::~FrameSequenceMetrics() {
-  if (HasDataLeftForReporting()) {
-    ReportMetrics();
-  }
-}
-
-void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) {
-  DCHECK(type_ == FrameSequenceTrackerType::kTouchScroll ||
-         type_ == FrameSequenceTrackerType::kWheelScroll ||
-         type_ == FrameSequenceTrackerType::kScrollbarScroll);
-  DCHECK_EQ(scrolling_thread_, ThreadType::kUnknown);
-  scrolling_thread_ = scrolling_thread;
-}
-
-FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread()
-    const {
-  switch (type_) {
-    case FrameSequenceTrackerType::kCompositorAnimation:
-    case FrameSequenceTrackerType::kPinchZoom:
-      return ThreadType::kCompositor;
-
-    case FrameSequenceTrackerType::kMainThreadAnimation:
-    case FrameSequenceTrackerType::kRAF:
-    case FrameSequenceTrackerType::kVideo:
-      return ThreadType::kMain;
-
-    case FrameSequenceTrackerType::kTouchScroll:
-    case FrameSequenceTrackerType::kScrollbarScroll:
-    case FrameSequenceTrackerType::kWheelScroll:
-      return scrolling_thread_;
-
-    case FrameSequenceTrackerType::kUniversal:
-      return ThreadType::kSlower;
-
-    case FrameSequenceTrackerType::kCustom:
-    case FrameSequenceTrackerType::kMaxType:
-      NOTREACHED();
-  }
-  return ThreadType::kUnknown;
-}
-
-void FrameSequenceMetrics::Merge(
-    std::unique_ptr<FrameSequenceMetrics> metrics) {
-  DCHECK_EQ(type_, metrics->type_);
-  DCHECK_EQ(GetEffectiveThread(), metrics->GetEffectiveThread());
-  impl_throughput_.Merge(metrics->impl_throughput_);
-  main_throughput_.Merge(metrics->main_throughput_);
-  aggregated_throughput_.Merge(metrics->aggregated_throughput_);
-  frames_checkerboarded_ += metrics->frames_checkerboarded_;
-
-  // Reset the state of |metrics| before destroying it, so that it doesn't end
-  // up reporting the metrics.
-  metrics->impl_throughput_ = {};
-  metrics->main_throughput_ = {};
-  metrics->aggregated_throughput_ = {};
-  metrics->frames_checkerboarded_ = 0;
-}
-
-bool FrameSequenceMetrics::HasEnoughDataForReporting() const {
-  return impl_throughput_.frames_expected >= kMinFramesForThroughputMetric ||
-         main_throughput_.frames_expected >= kMinFramesForThroughputMetric;
-}
-
-bool FrameSequenceMetrics::HasDataLeftForReporting() const {
-  return impl_throughput_.frames_expected > 0 ||
-         main_throughput_.frames_expected > 0;
-}
-
-void FrameSequenceMetrics::ComputeAggregatedThroughput() {
-  // Whenever we are expecting and producing main frames, we are expecting and
-  // producing impl frames as well. As an example, if we expect one main frame
-  // to be produced, and when that main frame is presented, we are expecting 3
-  // impl frames, then the number of expected frames is 3 for the aggregated
-  // throughput.
-  aggregated_throughput_.frames_expected = impl_throughput_.frames_expected;
-  DCHECK_LE(aggregated_throughput_.frames_produced,
-            aggregated_throughput_.frames_produced);
-}
-
-void FrameSequenceMetrics::ReportMetrics() {
-  DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected);
-  DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
-  TRACE_EVENT_NESTABLE_ASYNC_END2(
-      "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "args",
-      ThroughputData::ToTracedValue(impl_throughput_, main_throughput_),
-      "checkerboard", frames_checkerboarded_);
-
-  // Data for kCustom typed tracker is handled by caller instead being
-  // reported here.
-  if (type_ == FrameSequenceTrackerType::kCustom)
-    return;
-
-  ComputeAggregatedThroughput();
-
-  // Report the throughput metrics.
-  base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram(
-      this, ThreadType::kCompositor,
-      GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor, type_),
-      impl_throughput_);
-  base::Optional<int> main_throughput_percent = ThroughputData::ReportHistogram(
-      this, ThreadType::kMain,
-      GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
-      main_throughput_);
-
-  // Report for the 'slower thread' for the metrics where it makes sense.
-  bool should_report_slower_thread =
-      IsInteractionType(type_) || type_ == FrameSequenceTrackerType::kUniversal;
-  base::Optional<int> aggregated_throughput_percent;
-  if (should_report_slower_thread) {
-    aggregated_throughput_percent = ThroughputData::ReportHistogram(
-        this, ThreadType::kSlower,
-        GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_),
-        aggregated_throughput_);
-    if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) {
-      throughput_ukm_reporter_->ReportThroughputUkm(
-          aggregated_throughput_percent, impl_throughput_percent,
-          main_throughput_percent, type_);
-    }
-  }
-
-  // Report for the 'scrolling thread' for the scrolling interactions.
-  if (scrolling_thread_ != ThreadType::kUnknown) {
-    base::Optional<int> scrolling_thread_throughput;
-    switch (scrolling_thread_) {
-      case ThreadType::kCompositor:
-        scrolling_thread_throughput = impl_throughput_percent;
-        break;
-      case ThreadType::kMain:
-        scrolling_thread_throughput = main_throughput_percent;
-        break;
-      case ThreadType::kSlower:
-      case ThreadType::kUnknown:
-        NOTREACHED();
-        break;
-    }
-    if (scrolling_thread_throughput.has_value()) {
-      // It's OK to use the UMA histogram in the following code while still
-      // using |GetThroughputHistogramName()| to get the name of the metric,
-      // since the input-params to the function never change at runtime.
-      if (type_ == FrameSequenceTrackerType::kWheelScroll) {
-        UMA_HISTOGRAM_PERCENTAGE(
-            GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll,
-                                       "ScrollingThread"),
-            scrolling_thread_throughput.value());
-      } else if (type_ == FrameSequenceTrackerType::kTouchScroll) {
-        UMA_HISTOGRAM_PERCENTAGE(
-            GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll,
-                                       "ScrollingThread"),
-            scrolling_thread_throughput.value());
-      } else {
-        DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll);
-        UMA_HISTOGRAM_PERCENTAGE(
-            GetThroughputHistogramName(
-                FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
-            scrolling_thread_throughput.value());
-      }
-    }
-  }
-
-  // Report the checkerboarding metrics.
-  if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
-    const int checkerboarding_percent = static_cast<int>(
-        100 * frames_checkerboarded_ / impl_throughput_.frames_expected);
-    STATIC_HISTOGRAM_POINTER_GROUP(
-        GetCheckerboardingHistogramName(type_), static_cast<int>(type_),
-        static_cast<int>(FrameSequenceTrackerType::kMaxType),
-        Add(checkerboarding_percent),
-        base::LinearHistogram::FactoryGet(
-            GetCheckerboardingHistogramName(type_), 1, 100, 101,
-            base::HistogramBase::kUmaTargetedHistogramFlag));
-    frames_checkerboarded_ = 0;
-  }
-
-  // Reset the metrics that reach reporting threshold.
-  if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric)
-    impl_throughput_ = {};
-  if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric)
-    main_throughput_ = {};
-  if (aggregated_throughput_percent.has_value())
-    aggregated_throughput_ = {};
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// FrameSequenceTrackerCollection
-
-FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
-    bool is_single_threaded,
-    CompositorFrameReportingController* compositor_frame_reporting_controller)
-    : is_single_threaded_(is_single_threaded),
-      compositor_frame_reporting_controller_(
-          compositor_frame_reporting_controller) {}
-
-FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
-  frame_trackers_.clear();
-  removal_trackers_.clear();
-}
-
-FrameSequenceMetrics* FrameSequenceTrackerCollection::StartSequence(
-    FrameSequenceTrackerType type) {
-  DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
-
-  if (is_single_threaded_)
-    return nullptr;
-  if (frame_trackers_.contains(type))
-    return frame_trackers_[type]->metrics();
-  auto tracker = base::WrapUnique(
-      new FrameSequenceTracker(type, throughput_ukm_reporter_.get()));
-  frame_trackers_[type] = std::move(tracker);
-
-  if (compositor_frame_reporting_controller_)
-    compositor_frame_reporting_controller_->AddActiveTracker(type);
-  return frame_trackers_[type]->metrics();
-}
-
-void FrameSequenceTrackerCollection::StopSequence(
-    FrameSequenceTrackerType type) {
-  DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
-
-  if (!frame_trackers_.contains(type))
-    return;
-
-  std::unique_ptr<FrameSequenceTracker> tracker =
-      std::move(frame_trackers_[type]);
-
-  if (compositor_frame_reporting_controller_)
-    compositor_frame_reporting_controller_->RemoveActiveTracker(tracker->type_);
-
-  frame_trackers_.erase(type);
-  tracker->ScheduleTerminate();
-  removal_trackers_.push_back(std::move(tracker));
-  DestroyTrackers();
-}
-
-void FrameSequenceTrackerCollection::StartCustomSequence(int sequence_id) {
-  DCHECK(!base::Contains(custom_frame_trackers_, sequence_id));
-
-  custom_frame_trackers_[sequence_id] = base::WrapUnique(
-      new FrameSequenceTracker(FrameSequenceTrackerType::kCustom,
-                               /*throughput_ukm_reporter=*/nullptr,
-                               /*custom_sequence_id=*/sequence_id));
-}
-
-void FrameSequenceTrackerCollection::StopCustomSequence(int sequence_id) {
-  auto it = custom_frame_trackers_.find(sequence_id);
-  // This happens when an animation is aborted before starting.
-  if (it == custom_frame_trackers_.end())
-    return;
-
-  std::unique_ptr<FrameSequenceTracker> tracker = std::move(it->second);
-  custom_frame_trackers_.erase(it);
-  tracker->ScheduleTerminate();
-  removal_trackers_.push_back(std::move(tracker));
-}
-
-void FrameSequenceTrackerCollection::ClearAll() {
-  frame_trackers_.clear();
-  custom_frame_trackers_.clear();
-  removal_trackers_.clear();
-}
-
-void FrameSequenceTrackerCollection::NotifyBeginImplFrame(
-    const viz::BeginFrameArgs& args) {
-  RecreateTrackers(args);
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportBeginImplFrame(args);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportBeginImplFrame(args);
-}
-
-void FrameSequenceTrackerCollection::NotifyBeginMainFrame(
-    const viz::BeginFrameArgs& args) {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportBeginMainFrame(args);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportBeginMainFrame(args);
-}
-
-void FrameSequenceTrackerCollection::NotifyMainFrameProcessed(
-    const viz::BeginFrameArgs& args) {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportMainFrameProcessed(args);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportMainFrameProcessed(args);
-}
-
-void FrameSequenceTrackerCollection::NotifyImplFrameCausedNoDamage(
-    const viz::BeginFrameAck& ack) {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportImplFrameCausedNoDamage(ack);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportImplFrameCausedNoDamage(ack);
-
-  // Removal trackers continue to process any frames which they started
-  // observing.
-  for (auto& tracker : removal_trackers_)
-    tracker->ReportImplFrameCausedNoDamage(ack);
-}
-
-void FrameSequenceTrackerCollection::NotifyMainFrameCausedNoDamage(
-    const viz::BeginFrameArgs& args) {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportMainFrameCausedNoDamage(args);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportMainFrameCausedNoDamage(args);
-}
-
-void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->PauseFrameProduction();
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->PauseFrameProduction();
-}
-
-void FrameSequenceTrackerCollection::NotifySubmitFrame(
-    uint32_t frame_token,
-    bool has_missing_content,
-    const viz::BeginFrameAck& ack,
-    const viz::BeginFrameArgs& origin_args) {
-  for (auto& tracker : frame_trackers_) {
-    tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack,
-                                      origin_args);
-  }
-  for (auto& tracker : custom_frame_trackers_) {
-    tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack,
-                                      origin_args);
-  }
-
-  // Removal trackers continue to process any frames which they started
-  // observing.
-  for (auto& tracker : removal_trackers_) {
-    tracker->ReportSubmitFrame(frame_token, has_missing_content, ack,
-                               origin_args);
-  }
-}
-
-void FrameSequenceTrackerCollection::NotifyFrameEnd(
-    const viz::BeginFrameArgs& args,
-    const viz::BeginFrameArgs& main_args) {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportFrameEnd(args, main_args);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportFrameEnd(args, main_args);
-
-  // Removal trackers continue to process any frames which they started
-  // observing.
-  for (auto& tracker : removal_trackers_)
-    tracker->ReportFrameEnd(args, main_args);
-  DestroyTrackers();
-}
-
-void FrameSequenceTrackerCollection::NotifyFramePresented(
-    uint32_t frame_token,
-    const gfx::PresentationFeedback& feedback) {
-  for (auto& tracker : frame_trackers_)
-    tracker.second->ReportFramePresented(frame_token, feedback);
-  for (auto& tracker : custom_frame_trackers_)
-    tracker.second->ReportFramePresented(frame_token, feedback);
-
-  for (auto& tracker : removal_trackers_)
-    tracker->ReportFramePresented(frame_token, feedback);
-
-  for (auto& tracker : removal_trackers_) {
-    if (tracker->termination_status() ==
-        FrameSequenceTracker::TerminationStatus::kReadyForTermination) {
-      // The tracker is ready to be terminated.
-      // For non kCustom typed trackers, take the metrics from the tracker.
-      // merge with any outstanding metrics from previous trackers of the same
-      // type. If there are enough frames to report the metrics, then report the
-      // metrics and destroy it. Otherwise, retain it to be merged with
-      // follow-up sequences.
-      // For kCustom typed trackers, put its result in |custom_tracker_results_|
-      // to be picked up by caller.
-      auto metrics = tracker->TakeMetrics();
-      if (tracker->type() == FrameSequenceTrackerType::kCustom) {
-        custom_tracker_results_[tracker->custom_sequence_id()] =
-            metrics->main_throughput();
-        // |custom_tracker_results_| should be picked up timely.
-        DCHECK_LT(custom_tracker_results_.size(), 500u);
-        continue;
-      }
-
-      auto key = std::make_pair(tracker->type(), metrics->GetEffectiveThread());
-      if (accumulated_metrics_.contains(key)) {
-        metrics->Merge(std::move(accumulated_metrics_[key]));
-        accumulated_metrics_.erase(key);
-      }
-
-      if (metrics->HasEnoughDataForReporting()) {
-        if (tracker->type() == FrameSequenceTrackerType::kUniversal) {
-          uint32_t frames_expected = metrics->impl_throughput().frames_expected;
-          uint32_t frames_produced =
-              metrics->aggregated_throughput().frames_produced;
-          current_universal_throughput_ = std::floor(
-              100 * frames_produced / static_cast<float>(frames_expected));
-        }
-        metrics->ReportMetrics();
-      }
-      if (metrics->HasDataLeftForReporting())
-        accumulated_metrics_[key] = std::move(metrics);
-    }
-  }
-
-  DestroyTrackers();
-}
-
-void FrameSequenceTrackerCollection::DestroyTrackers() {
-  base::EraseIf(
-      removal_trackers_,
-      [](const std::unique_ptr<FrameSequenceTracker>& tracker) {
-        return tracker->termination_status() ==
-               FrameSequenceTracker::TerminationStatus::kReadyForTermination;
-      });
-}
-
-void FrameSequenceTrackerCollection::RecreateTrackers(
-    const viz::BeginFrameArgs& args) {
-  std::vector<FrameSequenceTrackerType> recreate_trackers;
-  for (const auto& tracker : frame_trackers_) {
-    if (tracker.second->ShouldReportMetricsNow(args))
-      recreate_trackers.push_back(tracker.first);
-  }
-
-  for (const auto& tracker_type : recreate_trackers) {
-    // StopSequence put the tracker in the |removal_trackers_|, which will
-    // report its throughput data when its frame is presented.
-    StopSequence(tracker_type);
-    // The frame sequence is still active, so create a new tracker to keep
-    // tracking this sequence.
-    StartSequence(tracker_type);
-  }
-}
-
-ActiveFrameSequenceTrackers
-FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() {
-  ActiveFrameSequenceTrackers encoded_types = 0;
-  for (const auto& tracker : frame_trackers_) {
-    encoded_types |= static_cast<ActiveFrameSequenceTrackers>(
-        1 << static_cast<unsigned>(tracker.first));
-  }
-  return encoded_types;
-}
-
-CustomTrackerResults
-FrameSequenceTrackerCollection::TakeCustomTrackerResults() {
-  return std::move(custom_tracker_results_);
-}
-
-FrameSequenceTracker* FrameSequenceTrackerCollection::GetTrackerForTesting(
-    FrameSequenceTrackerType type) {
-  if (!frame_trackers_.contains(type))
-    return nullptr;
-  return frame_trackers_[type].get();
-}
-
-FrameSequenceTracker*
-FrameSequenceTrackerCollection::GetRemovalTrackerForTesting(
-    FrameSequenceTrackerType type) {
-  for (const auto& tracker : removal_trackers_)
-    if (tracker->type_ == type)
-      return tracker.get();
-  return nullptr;
-}
-
-void FrameSequenceTrackerCollection::SetUkmManager(UkmManager* manager) {
-  DCHECK(frame_trackers_.empty());
-  if (manager)
-    throughput_ukm_reporter_ = std::make_unique<ThroughputUkmReporter>(manager);
-  else
-    throughput_ukm_reporter_ = nullptr;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// FrameSequenceTracker
-
 FrameSequenceTracker::FrameSequenceTracker(
     FrameSequenceTrackerType type,
     ThroughputUkmReporter* throughput_ukm_reporter,
@@ -1199,85 +633,6 @@
   return std::move(metrics_);
 }
 
-base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
-    FrameSequenceMetrics* metrics,
-    ThreadType thread_type,
-    int metric_index,
-    const ThroughputData& data) {
-  const auto sequence_type = metrics->type();
-  DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
-
-  STATIC_HISTOGRAM_POINTER_GROUP(
-      GetFrameSequenceLengthHistogramName(sequence_type),
-      static_cast<int>(sequence_type),
-      static_cast<int>(FrameSequenceTrackerType::kMaxType),
-      Add(data.frames_expected),
-      base::Histogram::FactoryGet(
-          GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50,
-          base::HistogramBase::kUmaTargetedHistogramFlag));
-
-  if (data.frames_expected < kMinFramesForThroughputMetric)
-    return base::nullopt;
-
-  // Throughput means the percent of frames that was expected to show on the
-  // screen but didn't. In other words, the lower the throughput is, the
-  // smoother user experience.
-  const int percent =
-      std::ceil(100 * (data.frames_expected - data.frames_produced) /
-                static_cast<double>(data.frames_expected));
-
-  const bool is_animation =
-      ShouldReportForAnimation(sequence_type, thread_type);
-  const bool is_interaction = ShouldReportForInteraction(metrics, thread_type);
-
-  ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter();
-
-  if (is_animation) {
-    UMA_HISTOGRAM_PERCENTAGE(
-        "Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent);
-    if (ukm_reporter) {
-      ukm_reporter->ReportAggregateThroughput(AggregationType::kAllAnimations,
-                                              percent);
-    }
-  }
-
-  if (is_interaction) {
-    UMA_HISTOGRAM_PERCENTAGE(
-        "Graphics.Smoothness.PercentDroppedFrames.AllInteractions", percent);
-    if (ukm_reporter) {
-      ukm_reporter->ReportAggregateThroughput(AggregationType::kAllInteractions,
-                                              percent);
-    }
-  }
-
-  if (is_animation || is_interaction) {
-    UMA_HISTOGRAM_PERCENTAGE(
-        "Graphics.Smoothness.PercentDroppedFrames.AllSequences", percent);
-    if (ukm_reporter) {
-      ukm_reporter->ReportAggregateThroughput(AggregationType::kAllSequences,
-                                              percent);
-    }
-  }
-
-  if (!is_animation && !IsInteractionType(sequence_type) &&
-      sequence_type != FrameSequenceTrackerType::kUniversal &&
-      sequence_type != FrameSequenceTrackerType::kVideo) {
-    return base::nullopt;
-  }
-
-  const char* thread_name =
-      thread_type == ThreadType::kCompositor
-          ? "CompositorThread"
-          : thread_type == ThreadType::kMain ? "MainThread" : "SlowerThread";
-  STATIC_HISTOGRAM_POINTER_GROUP(
-      GetThroughputHistogramName(sequence_type, thread_name), metric_index,
-      kMaximumHistogramIndex, Add(percent),
-      base::LinearHistogram::FactoryGet(
-          GetThroughputHistogramName(sequence_type, thread_name), 1, 100, 101,
-          base::HistogramBase::kUmaTargetedHistogramFlag));
-  return percent;
-}
-
 FrameSequenceTracker::CheckerboardingData::CheckerboardingData() = default;
 FrameSequenceTracker::CheckerboardingData::~CheckerboardingData() = default;
 
diff --git a/cc/metrics/frame_sequence_tracker.h b/cc/metrics/frame_sequence_tracker.h
index 554e3d69..fce4a1a 100644
--- a/cc/metrics/frame_sequence_tracker.h
+++ b/cc/metrics/frame_sequence_tracker.h
@@ -5,20 +5,12 @@
 #ifndef CC_METRICS_FRAME_SEQUENCE_TRACKER_H_
 #define CC_METRICS_FRAME_SEQUENCE_TRACKER_H_
 
-#include <stdint.h>
-#include <memory>
-#include <set>
-#include <utility>
-#include <vector>
-
-#include "base/callback_helpers.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
-#include "base/macros.h"
 #include "base/optional.h"
-#include "base/trace_event/traced_value.h"
 #include "cc/cc_export.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 
 namespace gfx {
 struct PresentationFeedback;
@@ -31,242 +23,7 @@
 }  // namespace viz
 
 namespace cc {
-class FrameSequenceTracker;
-class CompositorFrameReportingController;
 class ThroughputUkmReporter;
-class UkmManager;
-
-enum class FrameSequenceTrackerType {
-  // Used as an enum for metrics. DO NOT reorder or delete values. Rather,
-  // add them at the end and increment kMaxType.
-  kCompositorAnimation = 0,
-  kMainThreadAnimation = 1,
-  kPinchZoom = 2,
-  kRAF = 3,
-  kTouchScroll = 4,
-  kUniversal = 5,
-  kVideo = 6,
-  kWheelScroll = 7,
-  kScrollbarScroll = 8,
-  kCustom = 9,  // Note that the metrics for kCustom are not reported on UMA,
-                // and instead are dispatched back to the LayerTreeHostClient.
-  kMaxType
-};
-
-typedef uint16_t ActiveFrameSequenceTrackers;
-
-class CC_EXPORT FrameSequenceMetrics {
- public:
-  FrameSequenceMetrics(FrameSequenceTrackerType type,
-                       ThroughputUkmReporter* ukm_reporter);
-  ~FrameSequenceMetrics();
-
-  FrameSequenceMetrics(const FrameSequenceMetrics&) = delete;
-  FrameSequenceMetrics& operator=(const FrameSequenceMetrics&) = delete;
-
-  enum class ThreadType { kMain, kCompositor, kSlower, kUnknown };
-
-  struct ThroughputData {
-    static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue(
-        const ThroughputData& impl,
-        const ThroughputData& main);
-
-    // Returns the throughput in percent, a return value of base::nullopt
-    // indicates that no throughput metric is reported.
-    static base::Optional<int> ReportHistogram(FrameSequenceMetrics* metrics,
-                                               ThreadType thread_type,
-                                               int metric_index,
-                                               const ThroughputData& data);
-
-    void Merge(const ThroughputData& data) {
-      frames_expected += data.frames_expected;
-      frames_produced += data.frames_produced;
-#if DCHECK_IS_ON()
-      frames_processed += data.frames_processed;
-      frames_received += data.frames_received;
-#endif
-    }
-
-    // Tracks the number of frames that were expected to be shown during this
-    // frame-sequence.
-    uint32_t frames_expected = 0;
-
-    // Tracks the number of frames that were actually presented to the user
-    // during this frame-sequence.
-    uint32_t frames_produced = 0;
-
-#if DCHECK_IS_ON()
-    // Tracks the number of frames that is either submitted or reported as no
-    // damage.
-    uint32_t frames_processed = 0;
-
-    // Tracks the number of begin-frames that are received.
-    uint32_t frames_received = 0;
-#endif
-  };
-
-  void SetScrollingThread(ThreadType thread);
-
-  // Returns the 'effective thread' for the metrics (i.e. the thread most
-  // relevant for this metric).
-  ThreadType GetEffectiveThread() const;
-
-  void Merge(std::unique_ptr<FrameSequenceMetrics> metrics);
-  bool HasEnoughDataForReporting() const;
-  bool HasDataLeftForReporting() const;
-  // Report related metrics: throughput, checkboarding...
-  void ReportMetrics();
-  void ComputeAggregatedThroughputForTesting() {
-    ComputeAggregatedThroughput();
-  }
-
-  ThroughputData& impl_throughput() { return impl_throughput_; }
-  ThroughputData& main_throughput() { return main_throughput_; }
-  ThroughputData& aggregated_throughput() { return aggregated_throughput_; }
-  void add_checkerboarded_frames(int64_t frames) {
-    frames_checkerboarded_ += frames;
-  }
-  uint32_t frames_checkerboarded() const { return frames_checkerboarded_; }
-
-  FrameSequenceTrackerType type() const { return type_; }
-  ThroughputUkmReporter* ukm_reporter() const {
-    return throughput_ukm_reporter_;
-  }
-
- private:
-  void ComputeAggregatedThroughput();
-  const FrameSequenceTrackerType type_;
-
-  // Pointer to the reporter owned by the FrameSequenceTrackerCollection.
-  ThroughputUkmReporter* const throughput_ukm_reporter_;
-
-  ThroughputData impl_throughput_;
-  ThroughputData main_throughput_;
-  // The aggregated throughput for the main/compositor thread.
-  ThroughputData aggregated_throughput_;
-
-  ThreadType scrolling_thread_ = ThreadType::kUnknown;
-
-  // Tracks the number of produced frames that had some amount of
-  // checkerboarding, and how many frames showed such checkerboarded frames.
-  uint32_t frames_checkerboarded_ = 0;
-};
-
-// Map of kCustom tracker results keyed by a sequence id.
-using CustomTrackerResults =
-    base::flat_map<int, FrameSequenceMetrics::ThroughputData>;
-
-// Used for notifying attached FrameSequenceTracker's of begin-frames and
-// submitted frames.
-class CC_EXPORT FrameSequenceTrackerCollection {
- public:
-  FrameSequenceTrackerCollection(
-      bool is_single_threaded,
-      CompositorFrameReportingController* frame_reporting_controller);
-  ~FrameSequenceTrackerCollection();
-
-  FrameSequenceTrackerCollection(const FrameSequenceTrackerCollection&) =
-      delete;
-  FrameSequenceTrackerCollection& operator=(
-      const FrameSequenceTrackerCollection&) = delete;
-
-  // Creates a tracker for the specified sequence-type.
-  FrameSequenceMetrics* StartSequence(FrameSequenceTrackerType type);
-
-  // Schedules |tracker| for destruction. This is preferred instead of outright
-  // desrtruction of the tracker, since this ensures that the actual tracker
-  // instance is destroyed *after* the presentation-feedbacks have been received
-  // for all submitted frames.
-  void StopSequence(FrameSequenceTrackerType type);
-
-  // Creates a kCustom tracker for the given sequence id. It is an error and
-  // DCHECKs if there is already a tracker associated with the sequence id.
-  void StartCustomSequence(int sequence_id);
-
-  // Schedules the kCustom tracker representing |sequence_id| for destruction.
-  // It is a no-op if there is no tracker associated with the sequence id.
-  // Similar to StopSequence above, the tracker instance is destroyed *after*
-  // the presentation feedbacks have been received for all submitted frames.
-  void StopCustomSequence(int sequence_id);
-
-  // Removes all trackers. This also immediately destroys all trackers that had
-  // been scheduled for destruction, even if there are pending
-  // presentation-feedbacks. This is typically used if the client no longer
-  // expects to receive presentation-feedbacks for the previously submitted
-  // frames (e.g. when the gpu process dies).
-  void ClearAll();
-
-  // Notifies all trackers of various events.
-  void NotifyBeginImplFrame(const viz::BeginFrameArgs& args);
-  void NotifyBeginMainFrame(const viz::BeginFrameArgs& args);
-  void NotifyMainFrameProcessed(const viz::BeginFrameArgs& args);
-  void NotifyImplFrameCausedNoDamage(const viz::BeginFrameAck& ack);
-  void NotifyMainFrameCausedNoDamage(const viz::BeginFrameArgs& args);
-  void NotifyPauseFrameProduction();
-  void NotifySubmitFrame(uint32_t frame_token,
-                         bool has_missing_content,
-                         const viz::BeginFrameAck& ack,
-                         const viz::BeginFrameArgs& origin_args);
-  void NotifyFrameEnd(const viz::BeginFrameArgs& args,
-                      const viz::BeginFrameArgs& main_args);
-
-  // Note that this notifies the trackers of the presentation-feedbacks, and
-  // destroys any tracker that had been scheduled for destruction (using
-  // |ScheduleRemoval()|) if it has no more pending frames. Data from non
-  // kCustom typed trackers are reported to UMA. Data from kCustom typed
-  // trackers are added to |custom_tracker_results_| for caller to pick up.
-  void NotifyFramePresented(uint32_t frame_token,
-                            const gfx::PresentationFeedback& feedback);
-
-  // Return the type of each active frame tracker, encoded into a 16 bit
-  // integer with the bit at each position corresponding to the enum value of
-  // each type.
-  ActiveFrameSequenceTrackers FrameSequenceTrackerActiveTypes();
-
-  // Reports the accumulated kCustom tracker results and clears it.
-  CustomTrackerResults TakeCustomTrackerResults();
-
-  FrameSequenceTracker* GetTrackerForTesting(FrameSequenceTrackerType type);
-  FrameSequenceTracker* GetRemovalTrackerForTesting(
-      FrameSequenceTrackerType type);
-
-  void SetUkmManager(UkmManager* manager);
-
-  base::Optional<int> current_universal_throughput() {
-    return current_universal_throughput_;
-  }
-
- private:
-  friend class FrameSequenceTrackerTest;
-
-  void RecreateTrackers(const viz::BeginFrameArgs& args);
-  // Destroy the trackers that are ready to be terminated.
-  void DestroyTrackers();
-
-  const bool is_single_threaded_;
-  // The callsite can use the type to manipulate the tracker.
-  base::flat_map<FrameSequenceTrackerType,
-                 std::unique_ptr<FrameSequenceTracker>>
-      frame_trackers_;
-
-  // Custom trackers are keyed by a custom sequence id.
-  base::flat_map<int, std::unique_ptr<FrameSequenceTracker>>
-      custom_frame_trackers_;
-  CustomTrackerResults custom_tracker_results_;
-
-  std::vector<std::unique_ptr<FrameSequenceTracker>> removal_trackers_;
-  CompositorFrameReportingController* const
-      compositor_frame_reporting_controller_;
-
-  // The reporter takes throughput data and connect to UkmManager to report it.
-  std::unique_ptr<ThroughputUkmReporter> throughput_ukm_reporter_;
-
-  base::flat_map<
-      std::pair<FrameSequenceTrackerType, FrameSequenceMetrics::ThreadType>,
-      std::unique_ptr<FrameSequenceMetrics>>
-      accumulated_metrics_;
-  base::Optional<int> current_universal_throughput_;
-};
 
 // Tracks a sequence of frames to determine the throughput. It tracks this by
 // tracking the vsync sequence-numbers (from |BeginFrameArgs::sequence_number|),
diff --git a/cc/metrics/frame_sequence_tracker_collection.cc b/cc/metrics/frame_sequence_tracker_collection.cc
new file mode 100644
index 0000000..580241d
--- /dev/null
+++ b/cc/metrics/frame_sequence_tracker_collection.cc
@@ -0,0 +1,300 @@
+// 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/frame_sequence_tracker_collection.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/throughput_ukm_reporter.h"
+
+namespace cc {
+
+FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
+    bool is_single_threaded,
+    CompositorFrameReportingController* compositor_frame_reporting_controller)
+    : is_single_threaded_(is_single_threaded),
+      compositor_frame_reporting_controller_(
+          compositor_frame_reporting_controller) {}
+
+FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
+  frame_trackers_.clear();
+  removal_trackers_.clear();
+}
+
+FrameSequenceMetrics* FrameSequenceTrackerCollection::StartSequence(
+    FrameSequenceTrackerType type) {
+  DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
+
+  if (is_single_threaded_)
+    return nullptr;
+  if (frame_trackers_.contains(type))
+    return frame_trackers_[type]->metrics();
+  auto tracker = base::WrapUnique(
+      new FrameSequenceTracker(type, throughput_ukm_reporter_.get()));
+  frame_trackers_[type] = std::move(tracker);
+
+  if (compositor_frame_reporting_controller_)
+    compositor_frame_reporting_controller_->AddActiveTracker(type);
+  return frame_trackers_[type]->metrics();
+}
+
+void FrameSequenceTrackerCollection::StopSequence(
+    FrameSequenceTrackerType type) {
+  DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
+
+  if (!frame_trackers_.contains(type))
+    return;
+
+  std::unique_ptr<FrameSequenceTracker> tracker =
+      std::move(frame_trackers_[type]);
+
+  if (compositor_frame_reporting_controller_)
+    compositor_frame_reporting_controller_->RemoveActiveTracker(tracker->type_);
+
+  frame_trackers_.erase(type);
+  tracker->ScheduleTerminate();
+  removal_trackers_.push_back(std::move(tracker));
+  DestroyTrackers();
+}
+
+void FrameSequenceTrackerCollection::StartCustomSequence(int sequence_id) {
+  DCHECK(!base::Contains(custom_frame_trackers_, sequence_id));
+
+  custom_frame_trackers_[sequence_id] = base::WrapUnique(
+      new FrameSequenceTracker(FrameSequenceTrackerType::kCustom,
+                               /*throughput_ukm_reporter=*/nullptr,
+                               /*custom_sequence_id=*/sequence_id));
+}
+
+void FrameSequenceTrackerCollection::StopCustomSequence(int sequence_id) {
+  auto it = custom_frame_trackers_.find(sequence_id);
+  // This happens when an animation is aborted before starting.
+  if (it == custom_frame_trackers_.end())
+    return;
+
+  std::unique_ptr<FrameSequenceTracker> tracker = std::move(it->second);
+  custom_frame_trackers_.erase(it);
+  tracker->ScheduleTerminate();
+  removal_trackers_.push_back(std::move(tracker));
+}
+
+void FrameSequenceTrackerCollection::ClearAll() {
+  frame_trackers_.clear();
+  custom_frame_trackers_.clear();
+  removal_trackers_.clear();
+}
+
+void FrameSequenceTrackerCollection::NotifyBeginImplFrame(
+    const viz::BeginFrameArgs& args) {
+  RecreateTrackers(args);
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportBeginImplFrame(args);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportBeginImplFrame(args);
+}
+
+void FrameSequenceTrackerCollection::NotifyBeginMainFrame(
+    const viz::BeginFrameArgs& args) {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportBeginMainFrame(args);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportBeginMainFrame(args);
+}
+
+void FrameSequenceTrackerCollection::NotifyMainFrameProcessed(
+    const viz::BeginFrameArgs& args) {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportMainFrameProcessed(args);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportMainFrameProcessed(args);
+}
+
+void FrameSequenceTrackerCollection::NotifyImplFrameCausedNoDamage(
+    const viz::BeginFrameAck& ack) {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportImplFrameCausedNoDamage(ack);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportImplFrameCausedNoDamage(ack);
+
+  // Removal trackers continue to process any frames which they started
+  // observing.
+  for (auto& tracker : removal_trackers_)
+    tracker->ReportImplFrameCausedNoDamage(ack);
+}
+
+void FrameSequenceTrackerCollection::NotifyMainFrameCausedNoDamage(
+    const viz::BeginFrameArgs& args) {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportMainFrameCausedNoDamage(args);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportMainFrameCausedNoDamage(args);
+}
+
+void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->PauseFrameProduction();
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->PauseFrameProduction();
+}
+
+void FrameSequenceTrackerCollection::NotifySubmitFrame(
+    uint32_t frame_token,
+    bool has_missing_content,
+    const viz::BeginFrameAck& ack,
+    const viz::BeginFrameArgs& origin_args) {
+  for (auto& tracker : frame_trackers_) {
+    tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack,
+                                      origin_args);
+  }
+  for (auto& tracker : custom_frame_trackers_) {
+    tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack,
+                                      origin_args);
+  }
+
+  // Removal trackers continue to process any frames which they started
+  // observing.
+  for (auto& tracker : removal_trackers_) {
+    tracker->ReportSubmitFrame(frame_token, has_missing_content, ack,
+                               origin_args);
+  }
+}
+
+void FrameSequenceTrackerCollection::NotifyFrameEnd(
+    const viz::BeginFrameArgs& args,
+    const viz::BeginFrameArgs& main_args) {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportFrameEnd(args, main_args);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportFrameEnd(args, main_args);
+
+  // Removal trackers continue to process any frames which they started
+  // observing.
+  for (auto& tracker : removal_trackers_)
+    tracker->ReportFrameEnd(args, main_args);
+  DestroyTrackers();
+}
+
+void FrameSequenceTrackerCollection::NotifyFramePresented(
+    uint32_t frame_token,
+    const gfx::PresentationFeedback& feedback) {
+  for (auto& tracker : frame_trackers_)
+    tracker.second->ReportFramePresented(frame_token, feedback);
+  for (auto& tracker : custom_frame_trackers_)
+    tracker.second->ReportFramePresented(frame_token, feedback);
+
+  for (auto& tracker : removal_trackers_)
+    tracker->ReportFramePresented(frame_token, feedback);
+
+  for (auto& tracker : removal_trackers_) {
+    if (tracker->termination_status() ==
+        FrameSequenceTracker::TerminationStatus::kReadyForTermination) {
+      // The tracker is ready to be terminated.
+      // For non kCustom typed trackers, take the metrics from the tracker.
+      // merge with any outstanding metrics from previous trackers of the same
+      // type. If there are enough frames to report the metrics, then report the
+      // metrics and destroy it. Otherwise, retain it to be merged with
+      // follow-up sequences.
+      // For kCustom typed trackers, put its result in |custom_tracker_results_|
+      // to be picked up by caller.
+      auto metrics = tracker->TakeMetrics();
+      if (tracker->type() == FrameSequenceTrackerType::kCustom) {
+        custom_tracker_results_[tracker->custom_sequence_id()] =
+            metrics->main_throughput();
+        // |custom_tracker_results_| should be picked up timely.
+        DCHECK_LT(custom_tracker_results_.size(), 500u);
+        continue;
+      }
+
+      auto key = std::make_pair(tracker->type(), metrics->GetEffectiveThread());
+      if (accumulated_metrics_.contains(key)) {
+        metrics->Merge(std::move(accumulated_metrics_[key]));
+        accumulated_metrics_.erase(key);
+      }
+
+      if (metrics->HasEnoughDataForReporting()) {
+        if (tracker->type() == FrameSequenceTrackerType::kUniversal) {
+          uint32_t frames_expected = metrics->impl_throughput().frames_expected;
+          uint32_t frames_produced =
+              metrics->aggregated_throughput().frames_produced;
+          current_universal_throughput_ = std::floor(
+              100 * frames_produced / static_cast<float>(frames_expected));
+        }
+        metrics->ReportMetrics();
+      }
+      if (metrics->HasDataLeftForReporting())
+        accumulated_metrics_[key] = std::move(metrics);
+    }
+  }
+
+  DestroyTrackers();
+}
+
+void FrameSequenceTrackerCollection::DestroyTrackers() {
+  base::EraseIf(
+      removal_trackers_,
+      [](const std::unique_ptr<FrameSequenceTracker>& tracker) {
+        return tracker->termination_status() ==
+               FrameSequenceTracker::TerminationStatus::kReadyForTermination;
+      });
+}
+
+void FrameSequenceTrackerCollection::RecreateTrackers(
+    const viz::BeginFrameArgs& args) {
+  std::vector<FrameSequenceTrackerType> recreate_trackers;
+  for (const auto& tracker : frame_trackers_) {
+    if (tracker.second->ShouldReportMetricsNow(args))
+      recreate_trackers.push_back(tracker.first);
+  }
+
+  for (const auto& tracker_type : recreate_trackers) {
+    // StopSequence put the tracker in the |removal_trackers_|, which will
+    // report its throughput data when its frame is presented.
+    StopSequence(tracker_type);
+    // The frame sequence is still active, so create a new tracker to keep
+    // tracking this sequence.
+    StartSequence(tracker_type);
+  }
+}
+
+ActiveFrameSequenceTrackers
+FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() {
+  ActiveFrameSequenceTrackers encoded_types = 0;
+  for (const auto& tracker : frame_trackers_) {
+    encoded_types |= static_cast<ActiveFrameSequenceTrackers>(
+        1 << static_cast<unsigned>(tracker.first));
+  }
+  return encoded_types;
+}
+
+CustomTrackerResults
+FrameSequenceTrackerCollection::TakeCustomTrackerResults() {
+  return std::move(custom_tracker_results_);
+}
+
+FrameSequenceTracker* FrameSequenceTrackerCollection::GetTrackerForTesting(
+    FrameSequenceTrackerType type) {
+  if (!frame_trackers_.contains(type))
+    return nullptr;
+  return frame_trackers_[type].get();
+}
+
+FrameSequenceTracker*
+FrameSequenceTrackerCollection::GetRemovalTrackerForTesting(
+    FrameSequenceTrackerType type) {
+  for (const auto& tracker : removal_trackers_)
+    if (tracker->type_ == type)
+      return tracker.get();
+  return nullptr;
+}
+
+void FrameSequenceTrackerCollection::SetUkmManager(UkmManager* manager) {
+  DCHECK(frame_trackers_.empty());
+  if (manager)
+    throughput_ukm_reporter_ = std::make_unique<ThroughputUkmReporter>(manager);
+  else
+    throughput_ukm_reporter_ = nullptr;
+}
+
+}  // namespace cc
diff --git a/cc/metrics/frame_sequence_tracker_collection.h b/cc/metrics/frame_sequence_tracker_collection.h
new file mode 100644
index 0000000..9224593
--- /dev/null
+++ b/cc/metrics/frame_sequence_tracker_collection.h
@@ -0,0 +1,150 @@
+// 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_FRAME_SEQUENCE_TRACKER_COLLECTION_H_
+#define CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/optional.h"
+#include "cc/cc_export.h"
+#include "cc/metrics/frame_sequence_metrics.h"
+
+namespace gfx {
+struct PresentationFeedback;
+}
+
+namespace viz {
+struct BeginFrameAck;
+struct BeginFrameArgs;
+}  // namespace viz
+
+namespace cc {
+class FrameSequenceTracker;
+class CompositorFrameReportingController;
+class ThroughputUkmReporter;
+class UkmManager;
+
+// Map of kCustom tracker results keyed by a sequence id.
+using CustomTrackerResults =
+    base::flat_map<int, FrameSequenceMetrics::ThroughputData>;
+
+typedef uint16_t ActiveFrameSequenceTrackers;
+
+// Used for notifying attached FrameSequenceTracker's of begin-frames and
+// submitted frames.
+class CC_EXPORT FrameSequenceTrackerCollection {
+ public:
+  FrameSequenceTrackerCollection(
+      bool is_single_threaded,
+      CompositorFrameReportingController* frame_reporting_controller);
+  ~FrameSequenceTrackerCollection();
+
+  FrameSequenceTrackerCollection(const FrameSequenceTrackerCollection&) =
+      delete;
+  FrameSequenceTrackerCollection& operator=(
+      const FrameSequenceTrackerCollection&) = delete;
+
+  // Creates a tracker for the specified sequence-type.
+  FrameSequenceMetrics* StartSequence(FrameSequenceTrackerType type);
+
+  // Schedules |tracker| for destruction. This is preferred instead of outright
+  // desrtruction of the tracker, since this ensures that the actual tracker
+  // instance is destroyed *after* the presentation-feedbacks have been received
+  // for all submitted frames.
+  void StopSequence(FrameSequenceTrackerType type);
+
+  // Creates a kCustom tracker for the given sequence id. It is an error and
+  // DCHECKs if there is already a tracker associated with the sequence id.
+  void StartCustomSequence(int sequence_id);
+
+  // Schedules the kCustom tracker representing |sequence_id| for destruction.
+  // It is a no-op if there is no tracker associated with the sequence id.
+  // Similar to StopSequence above, the tracker instance is destroyed *after*
+  // the presentation feedbacks have been received for all submitted frames.
+  void StopCustomSequence(int sequence_id);
+
+  // Removes all trackers. This also immediately destroys all trackers that had
+  // been scheduled for destruction, even if there are pending
+  // presentation-feedbacks. This is typically used if the client no longer
+  // expects to receive presentation-feedbacks for the previously submitted
+  // frames (e.g. when the gpu process dies).
+  void ClearAll();
+
+  // Notifies all trackers of various events.
+  void NotifyBeginImplFrame(const viz::BeginFrameArgs& args);
+  void NotifyBeginMainFrame(const viz::BeginFrameArgs& args);
+  void NotifyMainFrameProcessed(const viz::BeginFrameArgs& args);
+  void NotifyImplFrameCausedNoDamage(const viz::BeginFrameAck& ack);
+  void NotifyMainFrameCausedNoDamage(const viz::BeginFrameArgs& args);
+  void NotifyPauseFrameProduction();
+  void NotifySubmitFrame(uint32_t frame_token,
+                         bool has_missing_content,
+                         const viz::BeginFrameAck& ack,
+                         const viz::BeginFrameArgs& origin_args);
+  void NotifyFrameEnd(const viz::BeginFrameArgs& args,
+                      const viz::BeginFrameArgs& main_args);
+
+  // Note that this notifies the trackers of the presentation-feedbacks, and
+  // destroys any tracker that had been scheduled for destruction (using
+  // |ScheduleRemoval()|) if it has no more pending frames. Data from non
+  // kCustom typed trackers are reported to UMA. Data from kCustom typed
+  // trackers are added to |custom_tracker_results_| for caller to pick up.
+  void NotifyFramePresented(uint32_t frame_token,
+                            const gfx::PresentationFeedback& feedback);
+
+  // Return the type of each active frame tracker, encoded into a 16 bit
+  // integer with the bit at each position corresponding to the enum value of
+  // each type.
+  ActiveFrameSequenceTrackers FrameSequenceTrackerActiveTypes();
+
+  // Reports the accumulated kCustom tracker results and clears it.
+  CustomTrackerResults TakeCustomTrackerResults();
+
+  FrameSequenceTracker* GetTrackerForTesting(FrameSequenceTrackerType type);
+  FrameSequenceTracker* GetRemovalTrackerForTesting(
+      FrameSequenceTrackerType type);
+
+  void SetUkmManager(UkmManager* manager);
+
+  base::Optional<int> current_universal_throughput() {
+    return current_universal_throughput_;
+  }
+
+ private:
+  friend class FrameSequenceTrackerTest;
+
+  void RecreateTrackers(const viz::BeginFrameArgs& args);
+  // Destroy the trackers that are ready to be terminated.
+  void DestroyTrackers();
+
+  const bool is_single_threaded_;
+  // The callsite can use the type to manipulate the tracker.
+  base::flat_map<FrameSequenceTrackerType,
+                 std::unique_ptr<FrameSequenceTracker>>
+      frame_trackers_;
+
+  // Custom trackers are keyed by a custom sequence id.
+  base::flat_map<int, std::unique_ptr<FrameSequenceTracker>>
+      custom_frame_trackers_;
+  CustomTrackerResults custom_tracker_results_;
+
+  std::vector<std::unique_ptr<FrameSequenceTracker>> removal_trackers_;
+  CompositorFrameReportingController* const
+      compositor_frame_reporting_controller_;
+
+  // The reporter takes throughput data and connect to UkmManager to report it.
+  std::unique_ptr<ThroughputUkmReporter> throughput_ukm_reporter_;
+
+  base::flat_map<
+      std::pair<FrameSequenceTrackerType, FrameSequenceMetrics::ThreadType>,
+      std::unique_ptr<FrameSequenceMetrics>>
+      accumulated_metrics_;
+  base::Optional<int> current_universal_throughput_;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index ccd3beba..9a4e585 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -4,11 +4,10 @@
 
 #include "cc/metrics/frame_sequence_tracker.h"
 
-#include <vector>
-
 #include "base/macros.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/cc/metrics/throughput_ukm_reporter.h b/cc/metrics/throughput_ukm_reporter.h
index b544946..441be89 100644
--- a/cc/metrics/throughput_ukm_reporter.h
+++ b/cc/metrics/throughput_ukm_reporter.h
@@ -7,7 +7,7 @@
 
 #include "base/optional.h"
 #include "cc/cc_export.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 
 namespace cc {
 class UkmManager;
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h
index cae83337..927cc3c 100644
--- a/cc/trees/layer_tree_host_client.h
+++ b/cc/trees/layer_tree_host_client.h
@@ -10,7 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "cc/input/browser_controls_state.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "ui/gfx/geometry/scroll_offset.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 47af38b..b6e0177 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -55,6 +55,7 @@
 #include "cc/layers/surface_layer_impl.h"
 #include "cc/layers/viewport.h"
 #include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/paint/paint_worklet_job.h"
 #include "cc/paint/paint_worklet_layer_painter.h"
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index db6d6d5..03e8e4a 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -32,7 +32,7 @@
 #include "cc/layers/layer_collections.h"
 #include "cc/metrics/event_metrics.h"
 #include "cc/metrics/events_metrics_manager.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "cc/paint/discardable_image_map.h"
 #include "cc/paint/paint_worklet_job.h"
 #include "cc/resources/ui_resource_client.h"
diff --git a/cc/trees/proxy_common.h b/cc/trees/proxy_common.h
index bc45efa8b..8dacb9c 100644
--- a/cc/trees/proxy_common.h
+++ b/cc/trees/proxy_common.h
@@ -9,7 +9,7 @@
 
 #include "base/callback_forward.h"
 #include "cc/cc_export.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
 namespace cc {
diff --git a/cc/trees/ukm_manager.h b/cc/trees/ukm_manager.h
index 08cce51e..ee5e5c3 100644
--- a/cc/trees/ukm_manager.h
+++ b/cc/trees/ukm_manager.h
@@ -7,7 +7,7 @@
 
 #include "cc/cc_export.h"
 #include "cc/metrics/compositor_frame_reporter.h"
-#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "url/gurl.h"