[go: nahoru, domu]

Compute fps graph with a dropped frame counter

Currently fps reflects how many frames are shown on the screen per
seconds. This isn't really helpful because we don't know how many
frames are we expecting to show on screen.

In this CL, we add a DroppedFrameCounter class, which keeps track
of how many frames are expected to show on screen, and how many
of them are dropped. With that, fps graph is different. When a
compositor frame is dropped, it shows a red vertical line. When
a main-frame that misses its commit deadline, it shows a
yellow vertical line. Otherwise, it means a frame is successfully
presented on screen, then we show a green vertical line.

With the new graph, we deprecate the throughput meter that is
beneath the fps graph. Also, instead of showing how many fps,
we show percentage representing throughput.

Bug: 1064290
Change-Id: I04ac95a07cd795c9b33c3c1ec5bfe6fa0529f6e0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2244002
Commit-Queue: Xida Chen <xidachen@chromium.org>
Reviewed-by: Robert Flack <flackr@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781076}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index bb28ea30..7e5e837 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -157,6 +157,8 @@
     "metrics/compositor_frame_reporting_controller.h",
     "metrics/compositor_timing_history.cc",
     "metrics/compositor_timing_history.h",
+    "metrics/dropped_frame_counter.cc",
+    "metrics/dropped_frame_counter.h",
     "metrics/event_metrics.cc",
     "metrics/event_metrics.h",
     "metrics/events_metrics_manager.cc",
@@ -312,8 +314,6 @@
     "trees/draw_property_utils.h",
     "trees/effect_node.cc",
     "trees/effect_node.h",
-    "trees/frame_rate_counter.cc",
-    "trees/frame_rate_counter.h",
     "trees/frame_rate_estimator.cc",
     "trees/frame_rate_estimator.h",
     "trees/image_animation_controller.cc",
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index d161ce3..b539a5d 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -20,6 +20,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "cc/debug/debug_colors.h"
+#include "cc/metrics/dropped_frame_counter.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_flags.h"
@@ -28,7 +29,6 @@
 #include "cc/paint/skia_paint_canvas.h"
 #include "cc/raster/scoped_gpu_raster.h"
 #include "cc/resources/memory_history.h"
-#include "cc/trees/frame_rate_counter.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -89,27 +89,9 @@
 
 }  // namespace
 
-HeadsUpDisplayLayerImpl::Graph::Graph(double indicator_value,
-                                      double start_upper_bound)
-    : value(0.0),
-      min(0.0),
-      max(0.0),
-      current_upper_bound(start_upper_bound),
-      default_upper_bound(start_upper_bound),
-      indicator(indicator_value) {}
-
-double HeadsUpDisplayLayerImpl::Graph::UpdateUpperBound() {
-  double target_upper_bound = std::max(max, default_upper_bound);
-  current_upper_bound += (target_upper_bound - current_upper_bound) * 0.5;
-  return current_upper_bound;
-}
-
 HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl,
                                                  int id)
-    : LayerImpl(tree_impl, id),
-      internal_contents_scale_(1.f),
-      fps_graph_(60.0, 80.0),
-      paint_time_graph_(16.0, 48.0) {}
+    : LayerImpl(tree_impl, id) {}
 
 HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl() {
   ReleaseResources();
@@ -551,18 +533,8 @@
     time_of_last_graph_update_ = now;
 
     if (debug_state.show_fps_counter) {
-      FrameRateCounter* fps_counter = layer_tree_impl()->frame_rate_counter();
-      fps_graph_.value = fps_counter->GetAverageFPS();
-      fps_counter->GetMinAndMaxFPS(&fps_graph_.min, &fps_graph_.max);
-      current_throughput_ = layer_tree_impl()->current_universal_throughput();
-      if (current_throughput_.has_value()) {
-        if (!max_throughput.has_value() ||
-            current_throughput_.value() > max_throughput.value())
-          max_throughput = current_throughput_;
-        if (!min_throughput.has_value() ||
-            current_throughput_.value() < min_throughput.value())
-          min_throughput = current_throughput_;
-      }
+      throughput_value_ =
+          layer_tree_impl()->dropped_frame_counter()->GetAverageThroughput();
     }
 
     if (debug_state.ShowMemoryStats()) {
@@ -573,9 +545,6 @@
         memory_entry_ = MemoryHistory::Entry();
     }
   }
-
-  fps_graph_.UpdateUpperBound();
-  paint_time_graph_.UpdateUpperBound();
 }
 
 void HeadsUpDisplayLayerImpl::DrawHudContents(PaintCanvas* canvas) {
@@ -598,8 +567,8 @@
     return;
   }
 
-  SkRect area =
-      DrawFPSDisplay(canvas, layer_tree_impl()->frame_rate_counter(), 0, 0);
+  SkRect area = DrawFrameThroughputDisplay(
+      canvas, layer_tree_impl()->dropped_frame_counter(), 0, 0);
   area = DrawGpuRasterizationStatus(canvas, 0, area.bottom(),
                                     std::max<SkScalar>(area.width(), 150));
 
@@ -653,30 +622,18 @@
 
 void HeadsUpDisplayLayerImpl::DrawGraphLines(PaintCanvas* canvas,
                                              PaintFlags* flags,
-                                             const SkRect& bounds,
-                                             const Graph& graph) const {
+                                             const SkRect& bounds) const {
   // Draw top and bottom line.
   flags->setColor(DebugColors::HUDSeparatorLineColor());
   canvas->drawLine(bounds.left(), bounds.top() - 1, bounds.right(),
                    bounds.top() - 1, *flags);
   canvas->drawLine(bounds.left(), bounds.bottom(), bounds.right(),
                    bounds.bottom(), *flags);
-
-  // Draw indicator line (additive blend mode to increase contrast when drawn on
-  // top of graph).
-  flags->setColor(DebugColors::HUDIndicatorLineColor());
-  flags->setBlendMode(SkBlendMode::kPlus);
-  const double indicator_top =
-      bounds.height() * (1.0 - graph.indicator / graph.current_upper_bound) -
-      1.0;
-  canvas->drawLine(bounds.left(), bounds.top() + indicator_top, bounds.right(),
-                   bounds.top() + indicator_top, *flags);
-  flags->setBlendMode(SkBlendMode::kSrcOver);
 }
 
-SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay(
+SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay(
     PaintCanvas* canvas,
-    const FrameRateCounter* fps_counter,
+    const DroppedFrameCounter* dropped_frame_counter,
     int right,
     int top) const {
   const int kPadding = 4;
@@ -686,39 +643,35 @@
   const int kFontHeight = 12;
 
   const int kGraphWidth =
-      base::saturated_cast<int>(fps_counter->time_stamp_history_size()) - 2;
+      base::saturated_cast<int>(dropped_frame_counter->frame_history_size());
   const int kGraphHeight = 40;
 
-  const int kHistogramWidth = 37;
-
-  int width = kGraphWidth + kHistogramWidth + 4 * kPadding;
-  int height =
-      2 * kTitleFontHeight + 2 * kFontHeight + kGraphHeight + 10 * kPadding + 2;
+  int width = kGraphWidth + 4 * kPadding;
+  int height = kTitleFontHeight + kFontHeight + kGraphHeight + 6 * kPadding + 2;
   int left = 0;
   SkRect area = SkRect::MakeXYWH(left, top, width, height);
 
   PaintFlags flags;
   DrawGraphBackground(canvas, &flags, area);
 
-  SkRect title_bounds = SkRect::MakeXYWH(
-      left + kPadding, top + kPadding, kGraphWidth + kHistogramWidth + kGap + 2,
-      kTitleFontHeight);
+  SkRect title_bounds =
+      SkRect::MakeXYWH(left + kPadding, top + kPadding, kGraphWidth + kGap + 2,
+                       kTitleFontHeight);
   SkRect text_bounds =
       SkRect::MakeXYWH(left + kPadding, title_bounds.bottom() + 2 * kPadding,
-                       kGraphWidth + kHistogramWidth + kGap + 2, kFontHeight);
+                       kGraphWidth + kGap + 2, kFontHeight);
   SkRect graph_bounds =
       SkRect::MakeXYWH(left + kPadding, text_bounds.bottom() + 2 * kPadding,
                        kGraphWidth, kGraphHeight);
-  SkRect histogram_bounds =
-      SkRect::MakeXYWH(graph_bounds.right() + kGap, graph_bounds.top(),
-                       kHistogramWidth, kGraphHeight);
 
   // Draw the fps meter.
-  const std::string title("Frame Rate");
-  const std::string value_text =
-      base::StringPrintf("%5.1f fps", fps_graph_.value);
-  const std::string min_max_text =
-      base::StringPrintf("%.0f-%.0f", fps_graph_.min, fps_graph_.max);
+  const std::string title("Frames");
+  const std::string value_text = base::StringPrintf("%d %%", throughput_value_);
+  const std::string dropped_frames_text =
+      base::StringPrintf("%zu (%zu m) dropped of %zu",
+                         dropped_frame_counter->total_compositor_dropped(),
+                         dropped_frame_counter->total_main_dropped(),
+                         dropped_frame_counter->total_frames());
 
   VLOG(1) << value_text;
 
@@ -729,105 +682,40 @@
   flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
   DrawText(canvas, flags, value_text, TextAlign::kLeft, kFontHeight,
            text_bounds.left(), text_bounds.bottom());
-  DrawText(canvas, flags, min_max_text, TextAlign::kRight, kFontHeight,
+  DrawText(canvas, flags, dropped_frames_text, TextAlign::kRight, kFontHeight,
            text_bounds.right(), text_bounds.bottom());
 
-  DrawGraphLines(canvas, &flags, graph_bounds, fps_graph_);
+  DrawGraphLines(canvas, &flags, graph_bounds);
 
-  // Draw the throughput meter.
-  int current_top = histogram_bounds.bottom() + kPadding;
-  const std::string throughput_title("Throughput");
-  const std::string throughput_value_text =
-      current_throughput_.has_value()
-          ? base::StringPrintf("%d %%", current_throughput_.value())
-          : base::StringPrintf("n/a");
-
-  VLOG(1) << throughput_value_text;
-
-  flags.setColor(DebugColors::HUDTitleColor());
-  DrawText(canvas, flags, throughput_title, TextAlign::kLeft, kTitleFontHeight,
-           title_bounds.left(), title_bounds.bottom() + current_top);
-
-  flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
-  DrawText(canvas, flags, throughput_value_text, TextAlign::kLeft, kFontHeight,
-           text_bounds.left(), text_bounds.bottom() + current_top);
-  if (min_throughput.has_value()) {
-    const std::string throughput_min_max_text = base::StringPrintf(
-        "%d-%d", min_throughput.value(), max_throughput.value());
-    DrawText(canvas, flags, throughput_min_max_text, TextAlign::kRight,
-             kFontHeight, text_bounds.right(),
-             text_bounds.bottom() + current_top);
-  }
-
-  // Collect fps graph and histogram data.
-  SkPath path;
-
-  const int kHistogramSize = 20;
-  double histogram[kHistogramSize] = {1.0};
-  double max_bucket_value = 1.0;
-
-  for (FrameRateCounter::RingBufferType::Iterator it = --fps_counter->end(); it;
-       --it) {
-    base::TimeDelta delta = fps_counter->RecentFrameInterval(it.index() + 1);
-
-    // Skip this particular instantaneous frame rate if it is not likely to have
-    // been valid.
-    if (!fps_counter->IsBadFrameInterval(delta)) {
-      double fps = 1.0 / delta.InSecondsF();
-
-      // Clamp the FPS to the range we want to plot visually.
-      double p = fps / fps_graph_.current_upper_bound;
-      if (p > 1.0)
-        p = 1.0;
-
-      // Plot this data point.
-      SkPoint cur =
-          SkPoint::Make(graph_bounds.left() + it.index(),
-                        graph_bounds.bottom() - p * graph_bounds.height());
-      if (path.isEmpty())
-        path.moveTo(cur);
-      else
-        path.lineTo(cur);
-
-      // Use the fps value to find the right bucket in the histogram.
-      int bucket_index = floor(p * (kHistogramSize - 1));
-
-      // Add the delta time to take the time spent at that fps rate into
-      // account.
-      histogram[bucket_index] += delta.InSecondsF();
-      max_bucket_value = std::max(histogram[bucket_index], max_bucket_value);
-    }
-  }
-
-  // Draw FPS histogram.
-  flags.setColor(DebugColors::HUDSeparatorLineColor());
-  canvas->drawLine(histogram_bounds.left() - 1, histogram_bounds.top() - 1,
-                   histogram_bounds.left() - 1, histogram_bounds.bottom() + 1,
-                   flags);
-  canvas->drawLine(histogram_bounds.right() + 1, histogram_bounds.top() - 1,
-                   histogram_bounds.right() + 1, histogram_bounds.bottom() + 1,
-                   flags);
-
-  flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
-  const double bar_height = histogram_bounds.height() / kHistogramSize;
-
-  for (int i = kHistogramSize - 1; i >= 0; --i) {
-    if (histogram[i] > 0) {
-      double bar_width =
-          histogram[i] / max_bucket_value * histogram_bounds.width();
-      canvas->drawRect(
-          SkRect::MakeXYWH(histogram_bounds.left(),
-                           histogram_bounds.bottom() - (i + 1) * bar_height,
-                           bar_width, 1),
-          flags);
-    }
+  // Collect the frames graph data.
+  SkPath good_path;
+  SkPath dropped_path;
+  SkPath partial_path;
+  for (auto it = --dropped_frame_counter->end(); it; --it) {
+    const auto state = **it;
+    int x = graph_bounds.left() + it.index();
+    SkPath& path = state == DroppedFrameCounter::kFrameStateDropped
+                       ? dropped_path
+                       : state == DroppedFrameCounter::kFrameStateComplete
+                             ? good_path
+                             : partial_path;
+    path.moveTo(x, graph_bounds.top());
+    path.lineTo(x, graph_bounds.bottom());
   }
 
   // Draw FPS graph.
   flags.setAntiAlias(true);
   flags.setStyle(PaintFlags::kStroke_Style);
   flags.setStrokeWidth(1);
-  canvas->drawPath(path, flags);
+
+  flags.setColor(SkColorSetA(SK_ColorGREEN, 128));
+  canvas->drawPath(good_path, flags);
+
+  flags.setColor(SkColorSetA(SK_ColorRED, 128));
+  canvas->drawPath(dropped_path, flags);
+
+  flags.setColor(SkColorSetA(SK_ColorYELLOW, 255));
+  canvas->drawPath(partial_path, flags);
 
   return area;
 }
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
index 323da1f..417d5a0 100644
--- a/cc/layers/heads_up_display_layer_impl.h
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -26,7 +26,7 @@
 }
 
 namespace cc {
-class FrameRateCounter;
+class DroppedFrameCounter;
 class LayerTreeFrameSink;
 class PaintCanvas;
 class PaintFlags;
@@ -76,24 +76,6 @@
   void PushPropertiesTo(LayerImpl* layer) override;
 
  private:
-  class Graph {
-   public:
-    Graph(double indicator_value, double start_upper_bound);
-
-    // Eases the upper bound, which limits what is currently visible in the
-    // graph, so that the graph always scales to either it's max or
-    // default_upper_bound.
-    double UpdateUpperBound();
-
-    double value;
-    double min;
-    double max;
-
-    double current_upper_bound;
-    const double default_upper_bound;
-    const double indicator;
-  };
-
   HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl, int id);
 
   const char* LayerTypeAsString() const override;
@@ -120,13 +102,13 @@
                            const SkRect& bounds) const;
   void DrawGraphLines(PaintCanvas* canvas,
                       PaintFlags* flags,
-                      const SkRect& bounds,
-                      const Graph& graph) const;
+                      const SkRect& bounds) const;
 
-  SkRect DrawFPSDisplay(PaintCanvas* canvas,
-                        const FrameRateCounter* fps_counter,
-                        int right,
-                        int top) const;
+  SkRect DrawFrameThroughputDisplay(
+      PaintCanvas* canvas,
+      const DroppedFrameCounter* dropped_frame_counter,
+      int right,
+      int top) const;
   SkRect DrawMemoryDisplay(PaintCanvas* canvas,
                            int top,
                            int right,
@@ -154,21 +136,15 @@
   sk_sp<SkTypeface> typeface_;
   std::vector<gfx::Rect> layout_shift_rects_;
 
-  float internal_contents_scale_;
+  float internal_contents_scale_ = 1.0f;
   gfx::Size internal_content_bounds_;
 
-  Graph fps_graph_;
-  Graph paint_time_graph_;
+  uint32_t throughput_value_ = 0.0f;
   MemoryHistory::Entry memory_entry_;
   int paint_rects_fade_step_ = 0;
   int layout_shift_rects_fade_step_ = 0;
   std::vector<DebugRect> paint_rects_;
   std::vector<DebugRect> layout_shift_debug_rects_;
-  base::Optional<int> current_throughput_;
-  // The worst and best throughput we have seen so far, they either both have no
-  // value, or both have value.
-  base::Optional<int> min_throughput;
-  base::Optional<int> max_throughput;
 
   base::TimeTicks time_of_last_graph_update_;
 };
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 4e0df1a..c5b037e 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -15,6 +15,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/base/rolling_time_delta_history.h"
+#include "cc/metrics/dropped_frame_counter.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "cc/metrics/latency_ukm_reporter.h"
 #include "services/tracing/public/cpp/perfetto/macros.h"
@@ -298,6 +299,7 @@
       StageType::kBeginImplFrameToSendBeginMainFrame;
   new_reporter->current_stage_.start_time = stage_history_.front().start_time;
   new_reporter->set_tick_clock(tick_clock_);
+  new_reporter->SetDroppedFrameCounter(dropped_frame_counter_);
   return new_reporter;
 }
 
@@ -448,6 +450,17 @@
     if (TestReportType(FrameReportType::kNonDroppedFrame))
       ReportEventLatencyHistograms();
   }
+
+  if (dropped_frame_counter_) {
+    if (TestReportType(FrameReportType::kDroppedFrame)) {
+      dropped_frame_counter_->AddDroppedFrame();
+    } else {
+      if (has_partial_update_)
+        dropped_frame_counter_->AddPartialFrame();
+      else
+        dropped_frame_counter_->AddGoodFrame();
+    }
+  }
 }
 
 void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) {
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index c137f85..2ff3b98 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -27,6 +27,7 @@
 }
 
 namespace cc {
+class DroppedFrameCounter;
 class LatencyUkmReporter;
 
 // This is used for tracing and reporting the duration of pipeline stages within
@@ -188,6 +189,11 @@
     tick_clock_ = tick_clock;
   }
 
+  void SetDroppedFrameCounter(DroppedFrameCounter* counter) {
+    dropped_frame_counter_ = counter;
+  }
+  void SetHasPartialUpdate() { has_partial_update_ = true; }
+
   const viz::BeginFrameId& frame_id() const { return args_.frame_id; }
 
  private:
@@ -286,6 +292,9 @@
   base::Optional<base::TimeTicks> main_frame_abort_time_;
 
   const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
+
+  DroppedFrameCounter* dropped_frame_counter_ = nullptr;
+  bool has_partial_update_ = false;
 };
 
 }  // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index 6c2ae1d..b19d869 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -73,6 +73,7 @@
   reporter->set_tick_clock(tick_clock_);
   reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
                        begin_time);
+  reporter->SetDroppedFrameCounter(dropped_frame_counter_);
   reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter);
 }
 
@@ -98,6 +99,7 @@
         should_report_metrics_);
     reporter->set_tick_clock(tick_clock_);
     reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
+    reporter->SetDroppedFrameCounter(dropped_frame_counter_);
     reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter);
   }
 }
@@ -204,6 +206,7 @@
     if (reporter) {
       reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
                            reporter->impl_frame_finish_time());
+      reporter->SetHasPartialUpdate();
       impl_reporter = std::move(reporter);
     }
   }
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 5d3d13a9..2659e92 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -20,6 +20,7 @@
 }
 
 namespace cc {
+class DroppedFrameCounter;
 class UkmManager;
 struct BeginMainFrameMetrics;
 
@@ -85,6 +86,10 @@
 
   std::unique_ptr<CompositorFrameReporter>* reporters() { return reporters_; }
 
+  void SetDroppedFrameCounter(DroppedFrameCounter* counter) {
+    dropped_frame_counter_ = counter;
+  }
+
  protected:
   struct SubmittedCompositorFrame {
     uint32_t frame_token;
@@ -131,6 +136,8 @@
   base::circular_deque<SubmittedCompositorFrame> submitted_compositor_frames_;
 
   const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
+
+  DroppedFrameCounter* dropped_frame_counter_ = nullptr;
 };
 }  // namespace cc
 
diff --git a/cc/metrics/dropped_frame_counter.cc b/cc/metrics/dropped_frame_counter.cc
new file mode 100644
index 0000000..56564a2
--- /dev/null
+++ b/cc/metrics/dropped_frame_counter.cc
@@ -0,0 +1,45 @@
+// 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/dropped_frame_counter.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+
+namespace cc {
+
+DroppedFrameCounter::DroppedFrameCounter() = default;
+
+uint32_t DroppedFrameCounter::GetAverageThroughput() const {
+  size_t good_frames = 0;
+  for (auto it = --end(); it; --it) {
+    if (**it == kFrameStateComplete)
+      ++good_frames;
+  }
+  double throughput = 100. * good_frames / ring_buffer_.BufferSize();
+  return static_cast<uint32_t>(throughput);
+}
+
+void DroppedFrameCounter::AddGoodFrame() {
+  ring_buffer_.SaveToBuffer(kFrameStateComplete);
+  ++total_frames_;
+}
+
+void DroppedFrameCounter::AddPartialFrame() {
+  ring_buffer_.SaveToBuffer(kFrameStatePartial);
+  ++total_frames_;
+  ++total_partial_;
+}
+
+void DroppedFrameCounter::AddDroppedFrame() {
+  ring_buffer_.SaveToBuffer(kFrameStateDropped);
+  ++total_frames_;
+  ++total_dropped_;
+}
+
+}  // namespace cc
diff --git a/cc/metrics/dropped_frame_counter.h b/cc/metrics/dropped_frame_counter.h
new file mode 100644
index 0000000..4929295
--- /dev/null
+++ b/cc/metrics/dropped_frame_counter.h
@@ -0,0 +1,55 @@
+// 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_DROPPED_FRAME_COUNTER_H_
+#define CC_METRICS_DROPPED_FRAME_COUNTER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/containers/ring_buffer.h"
+#include "base/time/time.h"
+
+namespace cc {
+
+// This class maintains a counter for produced/dropped frames, and can be used
+// to estimate the recent throughput.
+class DroppedFrameCounter {
+ public:
+  enum FrameState {
+    kFrameStateDropped,
+    kFrameStatePartial,
+    kFrameStateComplete
+  };
+  DroppedFrameCounter();
+
+  DroppedFrameCounter(const DroppedFrameCounter&) = delete;
+  DroppedFrameCounter& operator=(const DroppedFrameCounter&) = delete;
+
+  size_t frame_history_size() const { return ring_buffer_.BufferSize(); }
+  size_t total_frames() const { return total_frames_; }
+  size_t total_compositor_dropped() const { return total_dropped_; }
+  size_t total_main_dropped() const { return total_partial_; }
+
+  uint32_t GetAverageThroughput() const;
+
+  typedef base::RingBuffer<FrameState, 180> RingBufferType;
+  RingBufferType::Iterator begin() const { return ring_buffer_.Begin(); }
+  RingBufferType::Iterator end() const { return ring_buffer_.End(); }
+
+  void AddGoodFrame();
+  void AddPartialFrame();
+  void AddDroppedFrame();
+
+ private:
+  RingBufferType ring_buffer_;
+  size_t total_frames_ = 0;
+  size_t total_partial_ = 0;
+  size_t total_dropped_ = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_DROPPED_FRAME_COUNTER_H_
diff --git a/cc/metrics/frame_sequence_metrics_unittest.cc b/cc/metrics/frame_sequence_metrics_unittest.cc
index 8ef23cf..fbfa11c 100644
--- a/cc/metrics/frame_sequence_metrics_unittest.cc
+++ b/cc/metrics/frame_sequence_metrics_unittest.cc
@@ -54,8 +54,7 @@
   metric->aggregated_throughput().frames_produced = 150u;
 
   metric = nullptr;
-  DCHECK(reporter.current_universal_throughput().has_value());
-  EXPECT_EQ(reporter.current_universal_throughput().value(), 88);
+  EXPECT_EQ(reporter.TakeLastAggregatedPercent(), 12);
 }
 
 // Test that ThroughputUkmReporter::ReportThroughputUkm isn't called for the
diff --git a/cc/metrics/frame_sequence_tracker_collection.cc b/cc/metrics/frame_sequence_tracker_collection.cc
index 0ca5612..bae891e 100644
--- a/cc/metrics/frame_sequence_tracker_collection.cc
+++ b/cc/metrics/frame_sequence_tracker_collection.cc
@@ -262,12 +262,6 @@
   return throughput_ukm_reporter_->TakeLastMainPercent();
 }
 
-base::Optional<int>
-FrameSequenceTrackerCollection::CurrentUniversalThroughput() {
-  DCHECK(throughput_ukm_reporter_);
-  return throughput_ukm_reporter_->current_universal_throughput();
-}
-
 void FrameSequenceTrackerCollection::ComputeUniversalThroughputForTesting() {
   DCHECK(throughput_ukm_reporter_);
   const auto type = FrameSequenceTrackerType::kUniversal;
diff --git a/cc/metrics/frame_sequence_tracker_collection.h b/cc/metrics/frame_sequence_tracker_collection.h
index 0372a533..f89d0d6 100644
--- a/cc/metrics/frame_sequence_tracker_collection.h
+++ b/cc/metrics/frame_sequence_tracker_collection.h
@@ -112,8 +112,6 @@
 
   void SetUkmManager(UkmManager* manager);
 
-  base::Optional<int> CurrentUniversalThroughput();
-
   // These methods directly calls corresponding APIs in ThroughputUkmReporter,
   // please refer to the ThroughputUkmReporter for details.
   bool HasThroughputData() const;
diff --git a/cc/metrics/throughput_ukm_reporter.cc b/cc/metrics/throughput_ukm_reporter.cc
index a03cb1a2..28ea0f2c 100644
--- a/cc/metrics/throughput_ukm_reporter.cc
+++ b/cc/metrics/throughput_ukm_reporter.cc
@@ -71,7 +71,6 @@
   last_main_percent_ = metrics->main_throughput().DroppedFramePercent();
   last_aggregated_percent_ =
       metrics->aggregated_throughput().DroppedFramePercent();
-  current_universal_throughput_ = 100 - last_aggregated_percent_.value();
 }
 
 bool ThroughputUkmReporter::HasThroughputData() const {
diff --git a/cc/metrics/throughput_ukm_reporter.h b/cc/metrics/throughput_ukm_reporter.h
index 5e51bbab..4faa359 100644
--- a/cc/metrics/throughput_ukm_reporter.h
+++ b/cc/metrics/throughput_ukm_reporter.h
@@ -38,10 +38,6 @@
 
   void ComputeUniversalThroughput(FrameSequenceMetrics* metrics);
 
-  base::Optional<int> current_universal_throughput() {
-    return current_universal_throughput_;
-  }
-
   // Once the kUniversal tracker reported its throughput to UMA, this returns
   // true. In this case, the |last_aggregated_percent_| and |last_impl_percent_|
   // must have value.
@@ -83,10 +79,6 @@
   base::Optional<int> last_aggregated_percent_;
   base::Optional<int> last_main_percent_;
   base::Optional<int> last_impl_percent_;
-
-  // Use by the throughput meter.
-  // TODO(xidachen): move this into a separate class such as ThroughputMeter.
-  base::Optional<int> current_universal_throughput_;
 };
 
 }  // namespace cc
diff --git a/cc/trees/frame_rate_counter.cc b/cc/trees/frame_rate_counter.cc
deleted file mode 100644
index 816877c..0000000
--- a/cc/trees/frame_rate_counter.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2012 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/trees/frame_rate_counter.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <limits>
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-
-namespace cc {
-
-// The following constants are measured in seconds.
-
-// Two thresholds (measured in seconds) that describe what is considered to be a
-// "no-op frame" that should not be counted.
-// - if the frame is too fast, then given our compositor implementation, the
-// frame probably was a no-op and did not draw.
-// - if the frame is too slow, then there is probably not animating content, so
-// we should not pollute the average.
-static const double kFrameTooFast = 1.0 / 70.0;
-static const double kFrameTooSlow = 1.5;
-
-// If a frame takes longer than this threshold (measured in seconds) then we
-// (naively) assume that it missed a screen refresh; that is, we dropped a
-// frame.
-// TODO(brianderson): Determine this threshold based on monitor refresh rate,
-// crbug.com/138642.
-static const double kDroppedFrameTime = 1.0 / 50.0;
-
-// static
-std::unique_ptr<FrameRateCounter> FrameRateCounter::Create(
-    bool has_impl_thread) {
-  return base::WrapUnique(new FrameRateCounter(has_impl_thread));
-}
-
-base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const {
-  DCHECK_GT(n, 0u);
-  DCHECK_LT(n, ring_buffer_.BufferSize());
-  return ring_buffer_.ReadBuffer(n) - ring_buffer_.ReadBuffer(n - 1);
-}
-
-FrameRateCounter::FrameRateCounter(bool has_impl_thread)
-    : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {}
-
-void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) {
-  ring_buffer_.SaveToBuffer(timestamp);
-
-  // Check if frame interval can be computed.
-  if (ring_buffer_.CurrentIndex() < 2)
-    return;
-
-  base::TimeDelta frame_interval_seconds =
-      RecentFrameInterval(ring_buffer_.BufferSize() - 1);
-
-  if (!IsBadFrameInterval(frame_interval_seconds) &&
-      frame_interval_seconds.InSecondsF() > kDroppedFrameTime)
-    dropped_frame_count_ +=
-        frame_interval_seconds.InSecondsF() / kDroppedFrameTime;
-}
-
-bool FrameRateCounter::IsBadFrameInterval(
-    base::TimeDelta interval_between_consecutive_frames) const {
-  double delta = interval_between_consecutive_frames.InSecondsF();
-  bool scheduler_allows_double_frames = !has_impl_thread_;
-  bool interval_too_fast =
-      scheduler_allows_double_frames ? delta < kFrameTooFast : delta <= 0.0;
-  bool interval_too_slow = delta > kFrameTooSlow;
-  return interval_too_fast || interval_too_slow;
-}
-
-void FrameRateCounter::GetMinAndMaxFPS(double* min_fps, double* max_fps) const {
-  *min_fps = std::numeric_limits<double>::max();
-  *max_fps = 0.0;
-
-  for (RingBufferType::Iterator it = --ring_buffer_.End(); it; --it) {
-    base::TimeDelta delta = RecentFrameInterval(it.index() + 1);
-
-    if (IsBadFrameInterval(delta))
-      continue;
-
-    DCHECK_GT(delta.InSecondsF(), 0.f);
-    double fps = 1.0 / delta.InSecondsF();
-
-    *min_fps = std::min(fps, *min_fps);
-    *max_fps = std::max(fps, *max_fps);
-  }
-
-  if (*min_fps > *max_fps)
-    *min_fps = *max_fps;
-}
-
-double FrameRateCounter::GetAverageFPS() const {
-  int frame_count = 0;
-  double frame_times_total = 0.0;
-  double average_fps = 0.0;
-
-  // Walk backwards through the samples looking for a run of good frame
-  // timings from which to compute the mean.
-  //
-  // Slow frames occur just because the user is inactive, and should be
-  // ignored. Fast frames are ignored if the scheduler is in single-thread
-  // mode in order to represent the true frame rate in spite of the fact that
-  // the first few swapbuffers happen instantly which skews the statistics
-  // too much for short lived animations.
-  //
-  // IsBadFrameInterval encapsulates the frame too slow/frame too fast logic.
-
-  for (RingBufferType::Iterator it = --ring_buffer_.End();
-       it && frame_times_total < 1.0; --it) {
-    base::TimeDelta delta = RecentFrameInterval(it.index() + 1);
-
-    if (!IsBadFrameInterval(delta)) {
-      frame_count++;
-      frame_times_total += delta.InSecondsF();
-    } else if (frame_count) {
-      break;
-    }
-  }
-
-  if (frame_count) {
-    DCHECK_GT(frame_times_total, 0.0);
-    average_fps = frame_count / frame_times_total;
-  }
-
-  return average_fps;
-}
-
-}  // namespace cc
diff --git a/cc/trees/frame_rate_counter.h b/cc/trees/frame_rate_counter.h
deleted file mode 100644
index f1445fb..0000000
--- a/cc/trees/frame_rate_counter.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2012 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_TREES_FRAME_RATE_COUNTER_H_
-#define CC_TREES_FRAME_RATE_COUNTER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/containers/ring_buffer.h"
-#include "base/time/time.h"
-
-namespace cc {
-
-// This class maintains a history of timestamps, and provides functionality to
-// intelligently compute average frames per second.
-class FrameRateCounter {
- public:
-  static std::unique_ptr<FrameRateCounter> Create(bool has_impl_thread);
-
-  FrameRateCounter(const FrameRateCounter&) = delete;
-  FrameRateCounter& operator=(const FrameRateCounter&) = delete;
-
-  size_t current_frame_number() const { return ring_buffer_.CurrentIndex(); }
-  int dropped_frame_count() const { return dropped_frame_count_; }
-  size_t time_stamp_history_size() const { return ring_buffer_.BufferSize(); }
-
-  void SaveTimeStamp(base::TimeTicks timestamp);
-
-  // n = 0 returns the oldest frame interval retained in the history, while n =
-  // time_stamp_history_size() - 1 returns the most recent frame interval.
-  base::TimeDelta RecentFrameInterval(size_t n) const;
-
-  // This is a heuristic that can be used to ignore frames in a reasonable way.
-  // Returns true if the given frame interval is too fast or too slow, based on
-  // constant thresholds.
-  bool IsBadFrameInterval(
-      base::TimeDelta interval_between_consecutive_frames) const;
-
-  void GetMinAndMaxFPS(double* min_fps, double* max_fps) const;
-  double GetAverageFPS() const;
-
-  typedef base::RingBuffer<base::TimeTicks, 136> RingBufferType;
-  RingBufferType::Iterator begin() const { return ring_buffer_.Begin(); }
-  RingBufferType::Iterator end() const { return ring_buffer_.End(); }
-
- private:
-  explicit FrameRateCounter(bool has_impl_thread);
-
-  RingBufferType ring_buffer_;
-
-  bool has_impl_thread_;
-  int dropped_frame_count_;
-};
-
-}  // namespace cc
-
-#endif  // CC_TREES_FRAME_RATE_COUNTER_H_
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 3f281a5..6eec5cd 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -82,7 +82,6 @@
 #include "cc/trees/debug_rect_history.h"
 #include "cc/trees/draw_property_utils.h"
 #include "cc/trees/effect_node.h"
-#include "cc/trees/frame_rate_counter.h"
 #include "cc/trees/image_animation_controller.h"
 #include "cc/trees/latency_info_swap_promise_monitor.h"
 #include "cc/trees/layer_tree_frame_sink.h"
@@ -372,8 +371,6 @@
                         ? std::numeric_limits<size_t>::max()
                         : settings.scheduled_raster_task_limit,
                     settings.ToTileManagerSettings()),
-      fps_counter_(
-          FrameRateCounter::Create(task_runner_provider_->HasImplThread())),
       memory_history_(MemoryHistory::Create()),
       debug_rect_history_(DebugRectHistory::Create()),
       mutator_host_(std::move(mutator_host)),
@@ -424,6 +421,8 @@
   }
 
   SetDebugState(settings.initial_debug_state);
+  compositor_frame_reporting_controller_->SetDroppedFrameCounter(
+      &dropped_frame_counter_);
 }
 
 LayerTreeHostImpl::~LayerTreeHostImpl() {
@@ -1760,7 +1759,7 @@
 }
 
 size_t LayerTreeHostImpl::SourceAnimationFrameNumberForTesting() const {
-  return fps_counter_->current_frame_number();
+  return *next_frame_token_;
 }
 
 void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy(
@@ -2450,8 +2449,6 @@
                          TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                          "step", "GenerateCompositorFrame");
-  base::TimeTicks frame_time = CurrentBeginFrameArgs().frame_time;
-  fps_counter_->SaveTimeStamp(frame_time);
   rendering_stats_instrumentation_->IncrementFrameCount(1);
 
   memory_history_->SaveEntry(tile_manager_.memory_stats_from_last_assign());
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 7077a7f..f09d8d6 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -29,6 +29,7 @@
 #include "cc/input/scrollbar_animation_controller.h"
 #include "cc/input/scrollbar_controller.h"
 #include "cc/layers/layer_collections.h"
+#include "cc/metrics/dropped_frame_counter.h"
 #include "cc/metrics/event_metrics.h"
 #include "cc/metrics/events_metrics_manager.h"
 #include "cc/metrics/frame_sequence_tracker_collection.h"
@@ -83,7 +84,6 @@
 class CompositorFrameReportingController;
 class DebugRectHistory;
 class EvictionTilePriorityQueue;
-class FrameRateCounter;
 class ImageAnimationController;
 class LCDTextMetricsReporter;
 class LayerImpl;
@@ -655,9 +655,8 @@
   const gfx::Transform& DrawTransform() const;
 
   std::unique_ptr<ScrollAndScaleSet> ProcessScrollDeltas();
-  FrameRateCounter* fps_counter() { return fps_counter_.get(); }
-  base::Optional<int> current_universal_throughput() {
-    return frame_trackers_.CurrentUniversalThroughput();
+  DroppedFrameCounter* dropped_frame_counter() {
+    return &dropped_frame_counter_;
   }
   MemoryHistory* memory_history() { return memory_history_.get(); }
   DebugRectHistory* debug_rect_history() { return debug_rect_history_.get(); }
@@ -1258,7 +1257,7 @@
 
   std::unique_ptr<PageScaleAnimation> page_scale_animation_;
 
-  std::unique_ptr<FrameRateCounter> fps_counter_;
+  DroppedFrameCounter dropped_frame_counter_;
   std::unique_ptr<MemoryHistory> memory_history_;
   std::unique_ptr<DebugRectHistory> debug_rect_history_;
 
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 025ffee..2d94507 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -52,7 +52,6 @@
 #include "cc/test/test_layer_tree_frame_sink.h"
 #include "cc/trees/clip_node.h"
 #include "cc/trees/effect_node.h"
-#include "cc/trees/frame_rate_counter.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/scroll_and_scale_set.h"
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index ba8dac4..2216ca4 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1614,12 +1614,8 @@
   return host_impl_->image_animation_controller();
 }
 
-FrameRateCounter* LayerTreeImpl::frame_rate_counter() const {
-  return host_impl_->fps_counter();
-}
-
-base::Optional<int> LayerTreeImpl::current_universal_throughput() {
-  return host_impl_->current_universal_throughput();
+DroppedFrameCounter* LayerTreeImpl::dropped_frame_counter() const {
+  return host_impl_->dropped_frame_counter();
 }
 
 MemoryHistory* LayerTreeImpl::memory_history() const {
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 6a46046..d458397 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -46,7 +46,7 @@
 namespace cc {
 
 class DebugRectHistory;
-class FrameRateCounter;
+class DroppedFrameCounter;
 class HeadsUpDisplayLayerImpl;
 class ImageDecodeCache;
 class LayerTreeDebugState;
@@ -127,8 +127,7 @@
   TileManager* tile_manager() const;
   ImageDecodeCache* image_decode_cache() const;
   ImageAnimationController* image_animation_controller() const;
-  FrameRateCounter* frame_rate_counter() const;
-  base::Optional<int> current_universal_throughput();
+  DroppedFrameCounter* dropped_frame_counter() const;
   MemoryHistory* memory_history() const;
   DebugRectHistory* debug_rect_history() const;
   bool IsActiveTree() const;