[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"