Add event latency to compositor latency metrics
If handling of an event leads to producing a compositor frame, an UMA
histogram is recorded measuring the time between the event timestamp and
presentation of the corresponding frame to the screen, in the form of
"EventLatency.<EventType>.TotalLatency". In future, we will add
appropriate break-down metrics, too.
Bug: 1054007
Change-Id: If85f99fb83172337c21b374073a3c2d098c2fd50
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2018704
Reviewed-by: Robert Kaplow <rkaplow@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: Navid Zolghadr <nzolghadr@chromium.org>
Reviewed-by: Behdad Bakhshinategh <behdadb@chromium.org>
Commit-Queue: Mohsen Izadi <mohsen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746247}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 4f65dd8..146f433 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -156,6 +156,10 @@
"metrics/compositor_frame_reporting_controller.h",
"metrics/compositor_timing_history.cc",
"metrics/compositor_timing_history.h",
+ "metrics/event_metrics.cc",
+ "metrics/event_metrics.h",
+ "metrics/events_metrics_manager.cc",
+ "metrics/events_metrics_manager.h",
"metrics/frame_sequence_tracker.cc",
"metrics/frame_sequence_tracker.h",
"metrics/latency_ukm_reporter.cc",
@@ -647,6 +651,7 @@
"metrics/compositor_frame_reporter_unittest.cc",
"metrics/compositor_frame_reporting_controller_unittest.cc",
"metrics/compositor_timing_history_unittest.cc",
+ "metrics/events_metrics_manager_unittest.cc",
"metrics/frame_sequence_metrics_unittest.cc",
"metrics/frame_sequence_tracker_unittest.cc",
"mojo_embedder/async_layer_tree_frame_sink_unittest.cc",
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 048e434..46c3b656 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -15,6 +15,8 @@
#include "cc/input/scroll_state.h"
#include "cc/input/scrollbar.h"
#include "cc/input/touch_action.h"
+#include "cc/metrics/event_metrics.h"
+#include "cc/metrics/events_metrics_manager.h"
#include "cc/paint/element_id.h"
#include "cc/trees/swap_promise_monitor.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
@@ -255,6 +257,13 @@
virtual std::unique_ptr<SwapPromiseMonitor>
CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) = 0;
+ // During the lifetime of the returned EventsMetricsManager::ScopedMonitor, if
+ // SetNeedsOneBeginImplFrame() or SetNeedsRedraw() are called on
+ // LayerTreeHostImpl or a scroll animation is updated, |event_metrics| will be
+ // saved for reporting event latency metrics.
+ virtual std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+ GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) = 0;
+
virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0;
// Called by the single-threaded UI Compositor to get or set the scroll offset
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index f8ee98f..016e2601 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -4,7 +4,9 @@
#include "cc/metrics/compositor_frame_reporter.h"
+#include <memory>
#include <string>
+#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
@@ -13,6 +15,7 @@
#include "cc/base/rolling_time_delta_history.h"
#include "cc/metrics/frame_sequence_tracker.h"
#include "cc/metrics/latency_ukm_reporter.h"
+#include "ui/events/types/event_type.h"
namespace cc {
namespace {
@@ -126,21 +129,28 @@
// This value should be recalculated in case of changes to the number of values
// in CompositorFrameReporter::DroppedFrameReportType or in
// CompositorFrameReporter::StageType
-constexpr int kMaxHistogramIndex = kDroppedFrameReportTypeCount *
- kFrameSequenceTrackerTypeCount *
- (kStageTypeCount + kAllBreakdownCount);
-constexpr int kHistogramMin = 1;
-constexpr int kHistogramMax = 350000;
-constexpr int kHistogramBucketCount = 50;
+constexpr int kMaxCompositorLatencyHistogramIndex =
+ kDroppedFrameReportTypeCount * kFrameSequenceTrackerTypeCount *
+ (kStageTypeCount + kAllBreakdownCount);
+constexpr int kCompositorLatencyHistogramMin = 1;
+constexpr int kCompositorLatencyHistogramMax = 350000;
+constexpr int kCompositorLatencyHistogramBucketCount = 50;
+
+constexpr int kMaxEventLatencyHistogramIndex =
+ static_cast<int>(ui::EventType::ET_LAST);
+constexpr int kEventLatencyHistogramMin = 1;
+constexpr int kEventLatencyHistogramMax = 5000000;
+constexpr int kEventLatencyHistogramBucketCount = 100;
bool ShouldReportLatencyMetricsForSequenceType(
FrameSequenceTrackerType sequence_type) {
return sequence_type != FrameSequenceTrackerType::kUniversal;
}
-std::string HistogramName(const int report_type_index,
- FrameSequenceTrackerType frame_sequence_tracker_type,
- const int stage_type_index) {
+std::string GetCompositorLatencyHistogramName(
+ const int report_type_index,
+ FrameSequenceTrackerType frame_sequence_tracker_type,
+ const int stage_type_index) {
DCHECK_LE(frame_sequence_tracker_type, FrameSequenceTrackerType::kMaxType);
DCHECK(
ShouldReportLatencyMetricsForSequenceType(frame_sequence_tracker_type));
@@ -154,6 +164,11 @@
GetStageName(stage_type_index)});
}
+std::string GetEventLatencyHistogramName(const EventMetrics& event_metrics) {
+ return base::StrCat(
+ {"EventLatency.", event_metrics.GetTypeName(), ".TotalLatency"});
+}
+
} // namespace
CompositorFrameReporter::CompositorFrameReporter(
@@ -257,6 +272,12 @@
viz_breakdown_ = viz_breakdown;
}
+void CompositorFrameReporter::SetEventsMetrics(
+ std::vector<EventMetrics> events_metrics) {
+ DCHECK_EQ(0u, events_metrics_.size());
+ events_metrics_ = std::move(events_metrics);
+}
+
void CompositorFrameReporter::DroppedFrame() {
report_type_ = DroppedFrameReportType::kDroppedFrame;
}
@@ -265,20 +286,22 @@
if (frame_termination_status_ == FrameTerminationStatus::kUnknown)
TerminateFrame(FrameTerminationStatus::kUnknown, base::TimeTicks::Now());
DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
- bool report_latency = false;
+ bool report_compositor_latency = false;
+ bool report_event_latency = false;
const char* termination_status_str = nullptr;
switch (frame_termination_status_) {
case FrameTerminationStatus::kPresentedFrame:
- report_latency = true;
+ report_compositor_latency = true;
+ report_event_latency = true;
termination_status_str = "presented_frame";
break;
case FrameTerminationStatus::kDidNotPresentFrame:
- report_latency = true;
+ report_compositor_latency = true;
DroppedFrame();
termination_status_str = "did_not_present_frame";
break;
case FrameTerminationStatus::kReplacedByNewReporter:
- report_latency = true;
+ report_compositor_latency = true;
DroppedFrame();
termination_status_str = "replaced_by_new_reporter_at_same_stage";
break;
@@ -303,17 +326,20 @@
"compositor_frame_submission_status", submission_status_str);
}
- // Only report histograms if the frame was presented.
- if (report_latency) {
+ // Only report compositor latency histograms if the frame was produced.
+ if (report_compositor_latency) {
DCHECK(stage_history_.size());
stage_history_.emplace_back(StageType::kTotalLatency,
stage_history_.front().start_time,
stage_history_.back().end_time);
- ReportStageHistograms();
+ ReportCompositorLatencyHistograms();
}
+ // Only report event latency histograms if the frame was presented.
+ if (report_event_latency)
+ ReportEventLatencyHistograms();
}
-void CompositorFrameReporter::ReportStageHistograms() const {
+void CompositorFrameReporter::ReportCompositorLatencyHistograms() const {
for (const StageData& stage : stage_history_) {
ReportStageHistogramWithBreakdown(stage);
@@ -334,8 +360,9 @@
if (!ShouldReportLatencyMetricsForSequenceType(frame_sequence_tracker_type))
return;
base::TimeDelta stage_delta = stage.end_time - stage.start_time;
- ReportHistogram(frame_sequence_tracker_type,
- static_cast<int>(stage.stage_type), stage_delta);
+ ReportCompositorLatencyHistogram(frame_sequence_tracker_type,
+ static_cast<int>(stage.stage_type),
+ stage_delta);
switch (stage.stage_type) {
case StageType::kSendBeginMainFrameToCommit:
ReportBlinkBreakdowns(stage.start_time, frame_sequence_tracker_type);
@@ -368,9 +395,10 @@
begin_main_frame_start_ - start_time}};
for (const auto& pair : breakdowns) {
- ReportHistogram(frame_sequence_tracker_type,
- kBlinkBreakdownInitialIndex + static_cast<int>(pair.first),
- pair.second);
+ ReportCompositorLatencyHistogram(
+ frame_sequence_tracker_type,
+ kBlinkBreakdownInitialIndex + static_cast<int>(pair.first),
+ pair.second);
}
}
@@ -386,7 +414,7 @@
}
base::TimeDelta submit_to_receive_compositor_frame_delta =
viz_breakdown_.received_compositor_frame_timestamp - start_time;
- ReportHistogram(
+ ReportCompositorLatencyHistogram(
frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kSubmitToReceiveCompositorFrame),
@@ -397,7 +425,7 @@
base::TimeDelta received_compositor_frame_to_start_draw_delta =
viz_breakdown_.draw_start_timestamp -
viz_breakdown_.received_compositor_frame_timestamp;
- ReportHistogram(
+ ReportCompositorLatencyHistogram(
frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kReceivedCompositorFrameToStartDraw),
@@ -409,32 +437,34 @@
viz_breakdown_.swap_timings.swap_start -
viz_breakdown_.draw_start_timestamp;
- ReportHistogram(frame_sequence_tracker_type,
- kVizBreakdownInitialIndex +
- static_cast<int>(VizBreakdown::kStartDrawToSwapStart),
- start_draw_to_swap_start_delta);
+ ReportCompositorLatencyHistogram(
+ frame_sequence_tracker_type,
+ kVizBreakdownInitialIndex +
+ static_cast<int>(VizBreakdown::kStartDrawToSwapStart),
+ start_draw_to_swap_start_delta);
base::TimeDelta swap_start_to_swap_end_delta =
viz_breakdown_.swap_timings.swap_end -
viz_breakdown_.swap_timings.swap_start;
- ReportHistogram(frame_sequence_tracker_type,
- kVizBreakdownInitialIndex +
- static_cast<int>(VizBreakdown::kSwapStartToSwapEnd),
- swap_start_to_swap_end_delta);
+ ReportCompositorLatencyHistogram(
+ frame_sequence_tracker_type,
+ kVizBreakdownInitialIndex +
+ static_cast<int>(VizBreakdown::kSwapStartToSwapEnd),
+ swap_start_to_swap_end_delta);
base::TimeDelta swap_end_to_presentation_compositor_frame_delta =
viz_breakdown_.presentation_feedback.timestamp -
viz_breakdown_.swap_timings.swap_end;
- ReportHistogram(
+ ReportCompositorLatencyHistogram(
frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kSwapEndToPresentationCompositorFrame),
swap_end_to_presentation_compositor_frame_delta);
}
-void CompositorFrameReporter::ReportHistogram(
+void CompositorFrameReporter::ReportCompositorLatencyHistogram(
FrameSequenceTrackerType frame_sequence_tracker_type,
const int stage_type_index,
base::TimeDelta time_delta) const {
@@ -451,18 +481,37 @@
CHECK_GE(stage_type_index, 0);
CHECK_LT(report_type_index, kDroppedFrameReportTypeCount);
CHECK_GE(report_type_index, 0);
- CHECK_LT(histogram_index, kMaxHistogramIndex);
+ CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex);
CHECK_GE(histogram_index, 0);
STATIC_HISTOGRAM_POINTER_GROUP(
- HistogramName(report_type_index, frame_sequence_tracker_type,
- stage_type_index),
- histogram_index, kMaxHistogramIndex,
+ GetCompositorLatencyHistogramName(
+ report_type_index, frame_sequence_tracker_type, stage_type_index),
+ histogram_index, kMaxCompositorLatencyHistogramIndex,
AddTimeMicrosecondsGranularity(time_delta),
base::Histogram::FactoryGet(
- HistogramName(report_type_index, frame_sequence_tracker_type,
- stage_type_index),
- kHistogramMin, kHistogramMax, kHistogramBucketCount,
+ GetCompositorLatencyHistogramName(
+ report_type_index, frame_sequence_tracker_type, stage_type_index),
+ kCompositorLatencyHistogramMin, kCompositorLatencyHistogramMax,
+ kCompositorLatencyHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
+
+void CompositorFrameReporter::ReportEventLatencyHistograms() const {
+ for (const EventMetrics& event_metrics : events_metrics_) {
+ base::TimeDelta total_latency =
+ frame_termination_time_ - event_metrics.time_stamp();
+ const int type_index = static_cast<int>(event_metrics.type());
+ STATIC_HISTOGRAM_POINTER_GROUP(
+ GetEventLatencyHistogramName(event_metrics), type_index,
+ kMaxEventLatencyHistogramIndex,
+ AddTimeMicrosecondsGranularity(total_latency),
+ base::Histogram::FactoryGet(
+ GetEventLatencyHistogramName(event_metrics),
+ kEventLatencyHistogramMin, kEventLatencyHistogramMax,
+ kEventLatencyHistogramBucketCount,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+ }
+}
+
} // namespace cc
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 922c0c3..477be2d 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -5,6 +5,7 @@
#ifndef CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_
#define CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_
+#include <memory>
#include <vector>
#include "base/containers/flat_set.h"
@@ -12,6 +13,7 @@
#include "cc/base/base_export.h"
#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 "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/frame_timing_details.h"
@@ -137,6 +139,7 @@
void SetBlinkBreakdown(std::unique_ptr<BeginMainFrameMetrics> blink_breakdown,
base::TimeTicks begin_main_start);
void SetVizBreakdown(const viz::FrameTimingDetails& viz_breakdown);
+ void SetEventsMetrics(std::vector<EventMetrics> events_metrics);
int StageHistorySizeForTesting() { return stage_history_.size(); }
@@ -153,7 +156,7 @@
void TerminateReporter();
void EndCurrentStage(base::TimeTicks end_time);
- void ReportStageHistograms() const;
+ void ReportCompositorLatencyHistograms() const;
void ReportStageHistogramWithBreakdown(
const StageData& stage,
FrameSequenceTrackerType frame_sequence_tracker_type =
@@ -164,9 +167,11 @@
void ReportVizBreakdowns(
const base::TimeTicks start_time,
FrameSequenceTrackerType frame_sequence_tracker_type) const;
- void ReportHistogram(FrameSequenceTrackerType intraction_type,
- const int stage_type_index,
- base::TimeDelta time_delta) const;
+ void ReportCompositorLatencyHistogram(
+ FrameSequenceTrackerType intraction_type,
+ const int stage_type_index,
+ base::TimeDelta time_delta) const;
+ void ReportEventLatencyHistograms() const;
StageData current_stage_;
BeginMainFrameMetrics blink_breakdown_;
@@ -177,6 +182,9 @@
// be divided based on the frame submission status.
std::vector<StageData> stage_history_;
+ // List of metrics for events affecting this frame.
+ std::vector<EventMetrics> events_metrics_;
+
const bool is_single_threaded_;
DroppedFrameReportType report_type_ =
DroppedFrameReportType::kNonDroppedFrame;
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index 9e69117..0e91673 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -4,14 +4,24 @@
#include "cc/metrics/compositor_frame_reporter.h"
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/strings/strcat.h"
#include "base/test/metrics/histogram_tester.h"
#include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/event_metrics.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
-class CompositorFrameReporterTest;
+MATCHER(IsWhitelisted,
+ base::StrCat({negation ? "isn't" : "is", " whitelisted"})) {
+ return arg.IsWhitelisted();
+}
class CompositorFrameReporterTest : public testing::Test {
public:
@@ -177,5 +187,97 @@
histogram_tester.ExpectBucketCount(
"CompositorLatency.DroppedFrame.TotalLatency", 5, 1);
}
+
+// Tests that when a frame is presented to the user, event latency metrics are
+// reported properly.
+TEST_F(CompositorFrameReporterTest, EventLatencyForPresentedFrameReported) {
+ base::HistogramTester histogram_tester;
+
+ const base::TimeTicks event_time = Now();
+ std::vector<EventMetrics> events_metrics = {
+ {ui::ET_TOUCH_PRESSED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ };
+ EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->StartStage(
+ CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+ Now());
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->StartStage(
+ CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+ Now());
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->StartStage(
+ CompositorFrameReporter::StageType::
+ kSubmitCompositorFrameToPresentationCompositorFrame,
+ Now());
+ pipeline_reporter_->SetEventsMetrics(std::move(events_metrics));
+
+ AdvanceNowByMs(3);
+ const base::TimeTicks presentation_time = Now();
+ pipeline_reporter_->TerminateFrame(
+ CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
+ presentation_time);
+
+ pipeline_reporter_ = nullptr;
+
+ const int latency_ms = (presentation_time - event_time).InMicroseconds();
+ histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+ 1);
+ histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2);
+ histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency",
+ latency_ms, 1);
+ histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency",
+ latency_ms, 2);
+}
+
+// Tests that when the frame is not presented to the user, event latency metrics
+// are not reported.
+TEST_F(CompositorFrameReporterTest,
+ EventLatencyForDidNotPresentFrameNotReported) {
+ base::HistogramTester histogram_tester;
+
+ const base::TimeTicks event_time = Now();
+ std::vector<EventMetrics> events_metrics = {
+ {ui::ET_TOUCH_PRESSED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ };
+ EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->StartStage(
+ CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+ Now());
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->StartStage(
+ CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+ Now());
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->StartStage(
+ CompositorFrameReporter::StageType::
+ kSubmitCompositorFrameToPresentationCompositorFrame,
+ Now());
+ pipeline_reporter_->SetEventsMetrics(std::move(events_metrics));
+
+ AdvanceNowByMs(3);
+ pipeline_reporter_->TerminateFrame(
+ CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
+ Now());
+
+ pipeline_reporter_ = nullptr;
+
+ histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+ 0);
+ histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 0);
+}
+
} // namespace
} // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index 2df9ec77..e5dcd52 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -4,6 +4,8 @@
#include "cc/metrics/compositor_frame_reporting_controller.h"
+#include <utility>
+
#include "cc/metrics/compositor_frame_reporter.h"
#include "cc/metrics/latency_ukm_reporter.h"
#include "components/viz/common/frame_timing_details.h"
@@ -142,7 +144,8 @@
void CompositorFrameReportingController::DidSubmitCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameId& current_frame_id,
- const viz::BeginFrameId& last_activated_frame_id) {
+ const viz::BeginFrameId& last_activated_frame_id,
+ std::vector<EventMetrics> events_metrics) {
// If the last_activated_frame_id from scheduler is the same as
// last_submitted_frame_id_ in reporting controller, this means that we are
// submitting the Impl frame. In this case the frame will be submitted if
@@ -179,6 +182,7 @@
std::move(reporters_[PipelineStage::kActivate]);
submitted_reporter->StartStage(
StageType::kSubmitCompositorFrameToPresentationCompositorFrame, Now());
+ submitted_reporter->SetEventsMetrics(std::move(events_metrics));
submitted_compositor_frames_.emplace_back(frame_token,
std::move(submitted_reporter));
}
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 9f78fa4..8cab7e1 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -13,6 +13,7 @@
#include "cc/base/rolling_time_delta_history.h"
#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"
namespace viz {
@@ -60,7 +61,8 @@
virtual void DidSubmitCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameId& current_frame_id,
- const viz::BeginFrameId& last_activated_frame_id);
+ const viz::BeginFrameId& last_activated_frame_id,
+ std::vector<EventMetrics> events_metrics);
virtual void DidNotProduceFrame(const viz::BeginFrameId& id);
virtual void OnFinishImplFrame(const viz::BeginFrameId& id);
virtual void DidPresentCompositorFrame(
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index ea2b5ef..dd96349 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -4,23 +4,30 @@
#include "cc/metrics/compositor_frame_reporting_controller.h"
+#include <utility>
+#include <vector>
+
#include "base/macros.h"
+#include "base/strings/strcat.h"
#include "base/test/metrics/histogram_tester.h"
+#include "cc/metrics/event_metrics.h"
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
-class CompositorFrameReportingControllerTest;
+MATCHER(IsWhitelisted,
+ base::StrCat({negation ? "isn't" : "is", " whitelisted"})) {
+ return arg.IsWhitelisted();
+}
class TestCompositorFrameReportingController
: public CompositorFrameReportingController {
public:
- TestCompositorFrameReportingController(
- CompositorFrameReportingControllerTest* test)
- : CompositorFrameReportingController(), test_(test) {}
+ TestCompositorFrameReportingController() = default;
TestCompositorFrameReportingController(
const TestCompositorFrameReportingController& controller) = delete;
@@ -38,16 +45,11 @@
}
return count;
}
-
- protected:
- CompositorFrameReportingControllerTest* test_;
};
class CompositorFrameReportingControllerTest : public testing::Test {
public:
- CompositorFrameReportingControllerTest() : reporting_controller_(this) {
- current_id_ = viz::BeginFrameId(1, 1);
- }
+ CompositorFrameReportingControllerTest() : current_id_(1, 1) {}
// The following functions simulate the actions that would
// occur for each phase of the reporting controller.
@@ -89,34 +91,35 @@
[CompositorFrameReportingController::PipelineStage::kCommit]);
reporting_controller_.WillActivate();
reporting_controller_.DidActivate();
- last_activated_id_ = viz::BeginFrameId(current_id_);
+ last_activated_id_ = current_id_;
}
- void SimulateSubmitCompositorFrame(uint32_t frame_token) {
+ void SimulateSubmitCompositorFrame(uint32_t frame_token,
+ std::vector<EventMetrics> events_metrics) {
if (!reporting_controller_.reporters()
[CompositorFrameReportingController::PipelineStage::kActivate])
SimulateActivate();
CHECK(reporting_controller_.reporters()
[CompositorFrameReportingController::PipelineStage::kActivate]);
- reporting_controller_.DidSubmitCompositorFrame(frame_token, current_id_,
- last_activated_id_);
+ reporting_controller_.DidSubmitCompositorFrame(
+ frame_token, current_id_, last_activated_id_, events_metrics);
}
void SimulatePresentCompositorFrame() {
++next_token_;
- SimulateSubmitCompositorFrame(*next_token_);
+ SimulateSubmitCompositorFrame(*next_token_, std::vector<EventMetrics>());
viz::FrameTimingDetails details = {};
details.presentation_feedback.timestamp = base::TimeTicks::Now();
reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
}
+ void IncrementCurrentId() { current_id_.sequence_number++; }
+
protected:
TestCompositorFrameReportingController reporting_controller_;
viz::BeginFrameId current_id_;
viz::BeginFrameId last_activated_id_;
base::TimeTicks begin_main_start_;
-
- private:
viz::FrameTokenGenerator next_token_;
};
@@ -157,15 +160,15 @@
reporting_controller_.WillBeginMainFrame(current_id_);
reporting_controller_.WillActivate();
reporting_controller_.DidActivate();
- last_activated_id_ = viz::BeginFrameId(current_id_);
+ last_activated_id_ = current_id_;
reporting_controller_.WillCommit();
reporting_controller_.DidCommit();
reporting_controller_.WillActivate();
reporting_controller_.DidActivate();
EXPECT_EQ(1, reporting_controller_.ActiveReporters());
- reporting_controller_.DidSubmitCompositorFrame(0, current_id_,
- last_activated_id_);
+ reporting_controller_.DidSubmitCompositorFrame(
+ 0, current_id_, last_activated_id_, std::vector<EventMetrics>());
EXPECT_EQ(0, reporting_controller_.ActiveReporters());
// 4 simultaneous reporters active.
@@ -256,9 +259,9 @@
TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) {
base::HistogramTester histogram_tester;
- viz::BeginFrameId current_id_1_ = viz::BeginFrameId(1, 1);
- viz::BeginFrameId current_id_2_ = viz::BeginFrameId(1, 2);
- viz::BeginFrameId current_id_3_ = viz::BeginFrameId(1, 3);
+ viz::BeginFrameId current_id_1_(1, 1);
+ viz::BeginFrameId current_id_2_(1, 2);
+ viz::BeginFrameId current_id_3_(1, 3);
reporting_controller_.WillBeginImplFrame(current_id_1_);
reporting_controller_.WillBeginMainFrame(current_id_1_);
@@ -288,8 +291,8 @@
reporting_controller_.WillBeginMainFrame(current_id_);
reporting_controller_.BeginMainFrameAborted(current_id_);
reporting_controller_.OnFinishImplFrame(current_id_);
- reporting_controller_.DidSubmitCompositorFrame(1, current_id_,
- last_activated_id_);
+ reporting_controller_.DidSubmitCompositorFrame(
+ 1, current_id_, last_activated_id_, std::vector<EventMetrics>());
viz::FrameTimingDetails details = {};
reporting_controller_.DidPresentCompositorFrame(1, details);
@@ -307,9 +310,9 @@
TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted2) {
base::HistogramTester histogram_tester;
- viz::BeginFrameId current_id_1_ = viz::BeginFrameId(1, 1);
- viz::BeginFrameId current_id_2_ = viz::BeginFrameId(1, 2);
- viz::BeginFrameId current_id_3_ = viz::BeginFrameId(1, 3);
+ viz::BeginFrameId current_id_1_(1, 1);
+ viz::BeginFrameId current_id_2_(1, 2);
+ viz::BeginFrameId current_id_3_(1, 3);
reporting_controller_.WillBeginImplFrame(current_id_1_);
reporting_controller_.OnFinishImplFrame(current_id_1_);
reporting_controller_.WillBeginMainFrame(current_id_1_);
@@ -321,8 +324,8 @@
reporting_controller_.WillBeginMainFrame(current_id_2_);
reporting_controller_.OnFinishImplFrame(current_id_2_);
reporting_controller_.BeginMainFrameAborted(current_id_2_);
- reporting_controller_.DidSubmitCompositorFrame(1, current_id_2_,
- current_id_1_);
+ reporting_controller_.DidSubmitCompositorFrame(
+ 1, current_id_2_, current_id_1_, std::vector<EventMetrics>());
viz::FrameTimingDetails details = {};
reporting_controller_.DidPresentCompositorFrame(1, details);
histogram_tester.ExpectTotalCount(
@@ -340,8 +343,8 @@
histogram_tester.ExpectTotalCount(
"CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
1);
- reporting_controller_.DidSubmitCompositorFrame(2, current_id_2_,
- current_id_1_);
+ reporting_controller_.DidSubmitCompositorFrame(
+ 2, current_id_2_, current_id_1_, std::vector<EventMetrics>());
reporting_controller_.DidPresentCompositorFrame(2, details);
histogram_tester.ExpectTotalCount(
"CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
@@ -360,8 +363,8 @@
2);
reporting_controller_.WillBeginImplFrame(current_id_3_);
reporting_controller_.OnFinishImplFrame(current_id_3_);
- reporting_controller_.DidSubmitCompositorFrame(3, current_id_3_,
- current_id_1_);
+ reporting_controller_.DidSubmitCompositorFrame(
+ 3, current_id_3_, current_id_1_, std::vector<EventMetrics>());
reporting_controller_.DidPresentCompositorFrame(3, details);
histogram_tester.ExpectTotalCount(
"CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
@@ -436,5 +439,76 @@
"CompositorLatency.SendBeginMainFrameToCommit.BeginMainSentToStarted", 1);
}
+// Tests that EventLatency histograms are reported properly when a frame is
+// presented to the user.
+TEST_F(CompositorFrameReportingControllerTest,
+ EventLatencyForPresentedFrameReported) {
+ base::HistogramTester histogram_tester;
+
+ const base::TimeTicks event_time = base::TimeTicks::Now();
+ std::vector<EventMetrics> events_metrics = {
+ {ui::ET_TOUCH_PRESSED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ };
+ EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+ // Submit a compositor frame and notify CompositorFrameReporter of the events
+ // affecting the frame.
+ ++next_token_;
+ SimulateSubmitCompositorFrame(*next_token_, events_metrics);
+
+ // Present the submitted compositor frame to the user.
+ const base::TimeTicks presentation_time = base::TimeTicks::Now();
+ viz::FrameTimingDetails details;
+ details.presentation_feedback.timestamp = presentation_time;
+ reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
+
+ // Verify that EventLatency histograms are recorded.
+ const int latency_ms = (presentation_time - event_time).InMicroseconds();
+ histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+ 1);
+ histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2);
+ histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency",
+ latency_ms, 1);
+ histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency",
+ latency_ms, 2);
+}
+
+// Tests that EventLatency histograms are not reported when the frame is dropped
+// and not presented to the user.
+TEST_F(CompositorFrameReportingControllerTest,
+ EventLatencyForDidNotPresentFrameNotReported) {
+ base::HistogramTester histogram_tester;
+
+ const base::TimeTicks event_time = base::TimeTicks::Now();
+ std::vector<EventMetrics> events_metrics = {
+ {ui::ET_TOUCH_PRESSED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ {ui::ET_TOUCH_MOVED, event_time},
+ };
+ EXPECT_THAT(events_metrics, ::testing::Each(IsWhitelisted()));
+
+ // Submit a compositor frame and notify CompositorFrameReporter of the events
+ // affecting the frame.
+ ++next_token_;
+ SimulateSubmitCompositorFrame(*next_token_, events_metrics);
+
+ // Submit another compositor frame.
+ ++next_token_;
+ IncrementCurrentId();
+ SimulateSubmitCompositorFrame(*next_token_, std::vector<EventMetrics>());
+
+ // Present the second compositor frame to the uesr, dropping the first one.
+ viz::FrameTimingDetails details;
+ details.presentation_feedback.timestamp = base::TimeTicks::Now();
+ reporting_controller_.DidPresentCompositorFrame(*next_token_, details);
+
+ // Verify that no EventLatency histogram is recorded.
+ histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
+ 0);
+ histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 0);
+}
+
} // namespace
} // namespace cc
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index 8e2d0c12..91e8fc8 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -6,6 +6,9 @@
#include <stddef.h>
#include <stdint.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
@@ -979,10 +982,12 @@
void CompositorTimingHistory::DidSubmitCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameId& current_frame_id,
- const viz::BeginFrameId& last_activated_frame_id) {
+ const viz::BeginFrameId& last_activated_frame_id,
+ std::vector<EventMetrics> events_metrics) {
DCHECK_EQ(base::TimeTicks(), submit_start_time_);
compositor_frame_reporting_controller_->DidSubmitCompositorFrame(
- frame_token, current_frame_id, last_activated_frame_id);
+ frame_token, current_frame_id, last_activated_frame_id,
+ std::move(events_metrics));
submit_start_time_ = Now();
}
diff --git a/cc/metrics/compositor_timing_history.h b/cc/metrics/compositor_timing_history.h
index 8f7c267c..613f007 100644
--- a/cc/metrics/compositor_timing_history.h
+++ b/cc/metrics/compositor_timing_history.h
@@ -6,9 +6,11 @@
#define CC_METRICS_COMPOSITOR_TIMING_HISTORY_H_
#include <memory>
+#include <vector>
#include "cc/base/rolling_time_delta_history.h"
#include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
#include "cc/tiles/tile_priority.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
@@ -98,7 +100,8 @@
void DidSubmitCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameId& current_frame_id,
- const viz::BeginFrameId& last_activated_frame_id);
+ const viz::BeginFrameId& last_activated_frame_id,
+ std::vector<EventMetrics> events_metrics);
void DidNotProduceFrame(const viz::BeginFrameId& id);
void DidReceiveCompositorFrameAck();
void DidPresentCompositorFrame(uint32_t frame_token,
diff --git a/cc/metrics/event_metrics.cc b/cc/metrics/event_metrics.cc
new file mode 100644
index 0000000..ebdddd7
--- /dev/null
+++ b/cc/metrics/event_metrics.cc
@@ -0,0 +1,64 @@
+// 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/event_metrics.h"
+
+#include <tuple>
+
+#include "base/stl_util.h"
+
+namespace cc {
+
+EventMetrics::EventMetrics(ui::EventType type, base::TimeTicks time_stamp)
+ : type_(type), time_stamp_(time_stamp) {}
+
+const char* EventMetrics::GetTypeName() const {
+ DCHECK(IsWhitelisted()) << "Event type is not whitelisted for event metrics: "
+ << type_;
+
+ switch (type_) {
+ case ui::ET_MOUSE_PRESSED:
+ return "MousePressed";
+ case ui::ET_MOUSE_RELEASED:
+ return "MouseReleased";
+ case ui::ET_MOUSEWHEEL:
+ return "MouseWheel";
+ case ui::ET_KEY_PRESSED:
+ return "KeyPressed";
+ case ui::ET_KEY_RELEASED:
+ return "KeyReleased";
+ case ui::ET_TOUCH_PRESSED:
+ return "TouchPressed";
+ case ui::ET_TOUCH_RELEASED:
+ return "TouchReleased";
+ case ui::ET_TOUCH_MOVED:
+ return "TouchMoved";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+bool EventMetrics::IsWhitelisted() const {
+ switch (type_) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_MOUSEWHEEL:
+ case ui::ET_KEY_PRESSED:
+ case ui::ET_KEY_RELEASED:
+ case ui::ET_TOUCH_PRESSED:
+ case ui::ET_TOUCH_RELEASED:
+ case ui::ET_TOUCH_MOVED:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EventMetrics::operator==(const EventMetrics& other) const {
+ return std::tie(type_, time_stamp_) ==
+ std::tie(other.type_, other.time_stamp_);
+}
+
+} // namespace cc
diff --git a/cc/metrics/event_metrics.h b/cc/metrics/event_metrics.h
new file mode 100644
index 0000000..ea3e78b
--- /dev/null
+++ b/cc/metrics/event_metrics.h
@@ -0,0 +1,34 @@
+// 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_EVENT_METRICS_H_
+#define CC_METRICS_EVENT_METRICS_H_
+
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+#include "ui/events/types/event_type.h"
+
+namespace cc {
+
+// Data about an event useful in generating event latency metrics.
+class CC_EXPORT EventMetrics {
+ public:
+ EventMetrics(ui::EventType type, base::TimeTicks time_stamp);
+
+ const char* GetTypeName() const;
+ bool IsWhitelisted() const;
+
+ ui::EventType type() const { return type_; }
+ base::TimeTicks time_stamp() const { return time_stamp_; }
+
+ bool operator==(const EventMetrics& other) const;
+
+ private:
+ ui::EventType type_;
+ base::TimeTicks time_stamp_;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_EVENT_METRICS_H_
diff --git a/cc/metrics/events_metrics_manager.cc b/cc/metrics/events_metrics_manager.cc
new file mode 100644
index 0000000..9303a2f
--- /dev/null
+++ b/cc/metrics/events_metrics_manager.cc
@@ -0,0 +1,65 @@
+// 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/events_metrics_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/stl_util.h"
+
+namespace cc {
+namespace {
+
+class ScopedMonitorImpl : public EventsMetricsManager::ScopedMonitor {
+ public:
+ ScopedMonitorImpl(
+ base::flat_map<ScopedMonitor*, EventMetrics>* events_metrics,
+ const EventMetrics& event_metrics)
+ : events_metrics_(events_metrics) {
+ events_metrics_->emplace(this, event_metrics);
+ }
+
+ ~ScopedMonitorImpl() override { events_metrics_->erase(this); }
+
+ private:
+ base::flat_map<ScopedMonitor*, EventMetrics>* const events_metrics_;
+};
+
+} // namespace
+
+EventsMetricsManager::ScopedMonitor::~ScopedMonitor() = default;
+
+EventsMetricsManager::EventsMetricsManager() = default;
+EventsMetricsManager::~EventsMetricsManager() = default;
+
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+EventsMetricsManager::GetScopedMonitor(const EventMetrics& event_metrics) {
+ if (!event_metrics.IsWhitelisted())
+ return nullptr;
+ return std::make_unique<ScopedMonitorImpl>(&active_events_, event_metrics);
+}
+
+void EventsMetricsManager::SaveActiveEventsMetrics() {
+ saved_events_.reserve(saved_events_.size() + active_events_.size());
+ for (auto it = active_events_.begin(); it != active_events_.end();) {
+ saved_events_.push_back(it->second);
+ it = active_events_.erase(it);
+ }
+}
+
+std::vector<EventMetrics> EventsMetricsManager::TakeSavedEventsMetrics() {
+ std::vector<EventMetrics> result;
+ result.swap(saved_events_);
+ return result;
+}
+
+void EventsMetricsManager::AppendToSavedEventsMetrics(
+ std::vector<EventMetrics> events_metrics) {
+ saved_events_.reserve(saved_events_.size() + events_metrics.size());
+ saved_events_.insert(saved_events_.end(), events_metrics.begin(),
+ events_metrics.end());
+}
+
+} // namespace cc
diff --git a/cc/metrics/events_metrics_manager.h b/cc/metrics/events_metrics_manager.h
new file mode 100644
index 0000000..36d0818
--- /dev/null
+++ b/cc/metrics/events_metrics_manager.h
@@ -0,0 +1,67 @@
+// 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_EVENTS_METRICS_MANAGER_H_
+#define CC_METRICS_EVENTS_METRICS_MANAGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
+
+namespace cc {
+
+// Manages a list of active EventMetrics objects. Each thread (main or impl) has
+// its own instance of this class to help it determine which events have led to
+// a frame update.
+class CC_EXPORT EventsMetricsManager {
+ public:
+ // This interface is used to denote the scope of an event handling. The scope
+ // is started as soon as an instance is constructed and ended when instance is
+ // desctucted. EventsMetricsManager uses this to determine whether a frame
+ // update has happened due to handling of a specific event or not.
+ class ScopedMonitor {
+ public:
+ ScopedMonitor() = default;
+ virtual ~ScopedMonitor() = 0;
+
+ ScopedMonitor(const ScopedMonitor&) = delete;
+ ScopedMonitor& operator=(const ScopedMonitor&) = delete;
+ };
+
+ EventsMetricsManager();
+ ~EventsMetricsManager();
+
+ EventsMetricsManager(const EventsMetricsManager&) = delete;
+ EventsMetricsManager& operator=(const EventsMetricsManager&) = delete;
+
+ // Called by clients when they start handling an event. Destruction of the
+ // scoped monitor indicates the end of event handling.
+ std::unique_ptr<ScopedMonitor> GetScopedMonitor(
+ const EventMetrics& event_metrics);
+
+ // Called by clients when a frame needs to be produced. If any scoped monitor
+ // is active at this time, its corresponding event metrics would be saved.
+ void SaveActiveEventsMetrics();
+
+ // Empties the list of saved EventMetrics objects, returning them to the
+ // caller.
+ std::vector<EventMetrics> TakeSavedEventsMetrics();
+
+ void AppendToSavedEventsMetrics(std::vector<EventMetrics> events_metrics);
+
+ private:
+ // Map from scoped monitor to corresponding active EventMetrics.
+ base::flat_map<ScopedMonitor*, EventMetrics> active_events_;
+
+ // List of saved event metrics.
+ std::vector<EventMetrics> saved_events_;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_EVENTS_METRICS_MANAGER_H_
diff --git a/cc/metrics/events_metrics_manager_unittest.cc b/cc/metrics/events_metrics_manager_unittest.cc
new file mode 100644
index 0000000..02c057d
--- /dev/null
+++ b/cc/metrics/events_metrics_manager_unittest.cc
@@ -0,0 +1,90 @@
+// 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/events_metrics_manager.h"
+
+#include <utility>
+#include <vector>
+
+#include "cc/metrics/event_metrics.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/types/event_type.h"
+
+namespace cc {
+namespace {
+base::TimeTicks TimeAtMs(int ms) {
+ return base::TimeTicks() + base::TimeDelta::FromMilliseconds(ms);
+}
+} // namespace
+
+class EventsMetricsManagerTest : public testing::Test {
+ public:
+ EventsMetricsManagerTest() = default;
+ ~EventsMetricsManagerTest() override = default;
+
+ protected:
+ EventsMetricsManager manager_;
+};
+
+// Tests that EventMetrics are saved only if they have a whitelisted event type
+// and SaveActiveEventsMetrics() is called inside their corresponding monitor's
+// scope.
+TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) {
+ enum class Behavior {
+ kDoNotSave,
+ kSaveInsideScope,
+ kSaveOutsideScope,
+ };
+
+ std::vector<std::pair<EventMetrics, Behavior>> events = {
+ // A whitelisted event type for which SaveActiveEventsMetrics() is not
+ // called.
+ {{ui::ET_MOUSE_PRESSED, TimeAtMs(0)}, Behavior::kDoNotSave},
+
+ // A whitelisted event type for which SaveActiveEventsMetrics() is called
+ // inside its monitor scope.
+ {{ui::ET_MOUSE_PRESSED, TimeAtMs(1)}, Behavior::kSaveInsideScope},
+
+ // A whitelisted event type for which SaveActiveEventsMetrics() is called
+ // after its monitor scope is finished.
+ {{ui::ET_MOUSE_PRESSED, TimeAtMs(2)}, Behavior::kSaveOutsideScope},
+
+ // A non-whitelisted event type for which SaveActiveEventsMetrics() is
+ // called inside its monitor scope.
+ {{ui::ET_MOUSE_MOVED, TimeAtMs(3)}, Behavior::kSaveInsideScope},
+ };
+ EXPECT_TRUE(events[0].first.IsWhitelisted());
+ EXPECT_TRUE(events[1].first.IsWhitelisted());
+ EXPECT_TRUE(events[2].first.IsWhitelisted());
+ EXPECT_FALSE(events[3].first.IsWhitelisted());
+
+ // Out of the above events, only those with a whitelisted event type, for
+ // which SaveActiveEventsMetrics() is called inside its monitor scope, are
+ // expected to be saved.
+ std::vector<EventMetrics> expected_saved_events = {
+ events[1].first,
+ };
+
+ for (auto& event : events) {
+ {
+ auto monitor = manager_.GetScopedMonitor(event.first);
+ if (event.second == Behavior::kSaveInsideScope)
+ manager_.SaveActiveEventsMetrics();
+ // Ending the scope destroys the |monitor|.
+ }
+ if (event.second == Behavior::kSaveOutsideScope)
+ manager_.SaveActiveEventsMetrics();
+ }
+
+ // Check saved event metrics are as expected.
+ EXPECT_THAT(manager_.TakeSavedEventsMetrics(),
+ testing::ContainerEq(expected_saved_events));
+
+ // The first call to TakeSavedEventsMetrics() should remove events metrics
+ // from the manager, so the second call should return empty list.
+ EXPECT_THAT(manager_.TakeSavedEventsMetrics(), testing::IsEmpty());
+}
+
+} // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 96b6ca8c77..326d7d92 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -5,6 +5,7 @@
#include "cc/scheduler/scheduler.h"
#include <algorithm>
+#include <vector>
#include "base/auto_reset.h"
#include "base/bind.h"
@@ -145,10 +146,12 @@
ProcessScheduledActions();
}
-void Scheduler::DidSubmitCompositorFrame(uint32_t frame_token) {
+void Scheduler::DidSubmitCompositorFrame(
+ uint32_t frame_token,
+ std::vector<EventMetrics> events_metrics) {
compositor_timing_history_->DidSubmitCompositorFrame(
frame_token, begin_main_frame_args_.frame_id,
- last_activate_origin_frame_args_.frame_id);
+ last_activate_origin_frame_args_.frame_id, std::move(events_metrics));
state_machine_.DidSubmitCompositorFrame();
// There is no need to call ProcessScheduledActions here because
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 02149ef..77c63e6 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -7,10 +7,12 @@
#include <memory>
#include <string>
+#include <vector>
#include "base/cancelable_callback.h"
#include "base/time/time.h"
#include "cc/cc_export.h"
+#include "cc/metrics/event_metrics.h"
#include "cc/scheduler/begin_frame_tracker.h"
#include "cc/scheduler/draw_result.h"
#include "cc/scheduler/scheduler.h"
@@ -170,7 +172,8 @@
// Drawing should result in submitting a CompositorFrame to the
// LayerTreeFrameSink and then calling this.
- void DidSubmitCompositorFrame(uint32_t frame_token);
+ void DidSubmitCompositorFrame(uint32_t frame_token,
+ std::vector<EventMetrics> events_metrics);
// The LayerTreeFrameSink acks when it is ready for a new frame which
// should result in this getting called to unblock the next draw.
void DidReceiveCompositorFrameAck();
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 9a20d45..5da0a78 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -19,6 +19,7 @@
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "cc/metrics/begin_main_frame_metrics.h"
+#include "cc/metrics/event_metrics.h"
#include "cc/test/scheduler_test_common.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/test/begin_frame_args_test.h"
@@ -163,7 +164,7 @@
draw_will_happen_ && swap_will_happen_if_draw_happens_;
if (swap_will_happen) {
last_begin_frame_ack_ = scheduler_->CurrentBeginFrameAckForActiveTree();
- scheduler_->DidSubmitCompositorFrame(0);
+ scheduler_->DidSubmitCompositorFrame(0, std::vector<EventMetrics>());
if (automatic_ack_)
scheduler_->DidReceiveCompositorFrameAck();
diff --git a/cc/test/fake_compositor_frame_reporting_controller.cc b/cc/test/fake_compositor_frame_reporting_controller.cc
index 04e285f..b1183fe 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.cc
+++ b/cc/test/fake_compositor_frame_reporting_controller.cc
@@ -3,6 +3,10 @@
// found in the LICENSE file.
#include "cc/test/fake_compositor_frame_reporting_controller.h"
+
+#include <utility>
+#include <vector>
+
#include "components/viz/common/frame_timing_details.h"
namespace cc {
@@ -51,9 +55,11 @@
void FakeCompositorFrameReportingController::DidSubmitCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameId& current_frame_id,
- const viz::BeginFrameId& last_activated_frame_id) {
+ const viz::BeginFrameId& last_activated_frame_id,
+ std::vector<EventMetrics> events_metrics) {
CompositorFrameReportingController::DidSubmitCompositorFrame(
- frame_token, current_frame_id, last_activated_frame_id);
+ frame_token, current_frame_id, last_activated_frame_id,
+ std::move(events_metrics));
viz::FrameTimingDetails details;
details.presentation_feedback.timestamp = base::TimeTicks::Now();
diff --git a/cc/test/fake_compositor_frame_reporting_controller.h b/cc/test/fake_compositor_frame_reporting_controller.h
index 0536f550f77..b71c857 100644
--- a/cc/test/fake_compositor_frame_reporting_controller.h
+++ b/cc/test/fake_compositor_frame_reporting_controller.h
@@ -5,6 +5,8 @@
#ifndef CC_TEST_FAKE_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_
#define CC_TEST_FAKE_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_
+#include <vector>
+
#include "cc/metrics/compositor_frame_reporting_controller.h"
namespace viz {
@@ -36,7 +38,8 @@
void DidSubmitCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameId& current_frame_id,
- const viz::BeginFrameId& last_activated_frame_id) override;
+ const viz::BeginFrameId& last_activated_frame_id,
+ std::vector<EventMetrics> events_metrics) override;
void DidPresentCompositorFrame(
uint32_t frame_token,
const viz::FrameTimingDetails& details) override;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 2a8622f..13573155 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -242,6 +242,16 @@
return &swap_promise_manager_;
}
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+LayerTreeHost::GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) {
+ return events_metrics_manager_.GetScopedMonitor(event_metrics);
+}
+
+void LayerTreeHost::ClearEventsMetrics() {
+ // Take evens metrics and drop them.
+ events_metrics_manager_.TakeSavedEventsMetrics();
+}
+
const LayerTreeSettings& LayerTreeHost::GetSettings() const {
return settings_;
}
@@ -348,6 +358,8 @@
PushLayerTreeHostPropertiesTo(host_impl);
sync_tree->PassSwapPromises(swap_promise_manager_.TakeSwapPromises());
+ host_impl->AppendEventsMetrics(
+ events_metrics_manager_.TakeSavedEventsMetrics());
sync_tree->set_ui_resource_request_queue(
ui_resource_manager_->TakeUIResourcesRequests());
@@ -590,17 +602,20 @@
void LayerTreeHost::SetNeedsAnimate() {
proxy_->SetNeedsAnimate();
swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit();
+ events_metrics_manager_.SaveActiveEventsMetrics();
}
DISABLE_CFI_PERF
void LayerTreeHost::SetNeedsUpdateLayers() {
proxy_->SetNeedsUpdateLayers();
swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit();
+ events_metrics_manager_.SaveActiveEventsMetrics();
}
void LayerTreeHost::SetNeedsCommit() {
proxy_->SetNeedsCommit();
swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit();
+ events_metrics_manager_.SaveActiveEventsMetrics();
}
bool LayerTreeHost::RequestedMainFramePendingForTesting() {
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 943c40f..64812b6 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -34,6 +34,8 @@
#include "cc/layers/layer_collections.h"
#include "cc/layers/layer_list_iterator.h"
#include "cc/metrics/begin_main_frame_metrics.h"
+#include "cc/metrics/event_metrics.h"
+#include "cc/metrics/events_metrics_manager.h"
#include "cc/metrics/frame_sequence_tracker.h"
#include "cc/paint/node_id.h"
#include "cc/trees/browser_controls_params.h"
@@ -176,6 +178,10 @@
// when a main frame is requested.
SwapPromiseManager* GetSwapPromiseManager();
+ std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+ GetScopedEventMetricsMonitor(const EventMetrics& event_metrics);
+ void ClearEventsMetrics();
+
// Visibility and LayerTreeFrameSink -------------------------------
// Sets or gets if the LayerTreeHost is visible. When not visible it will:
@@ -899,6 +905,8 @@
// result in a commit (due to no change in content).
base::TimeTicks impl_commit_start_time_;
+ EventsMetricsManager events_metrics_manager_;
+
// Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate
// objects.
base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a49da2c..c270e01 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -967,6 +967,12 @@
new LatencyInfoSwapPromiseMonitor(latency, nullptr, this));
}
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+LayerTreeHostImpl::GetScopedEventMetricsMonitor(
+ const EventMetrics& event_metrics) {
+ return events_metrics_manager_.GetScopedMonitor(event_metrics);
+}
+
ScrollElasticityHelper* LayerTreeHostImpl::CreateScrollElasticityHelper() {
DCHECK(!scroll_elasticity_helper_);
if (settings_.enable_elastic_overscroll) {
@@ -3233,11 +3239,13 @@
// SwapPromiseMonitor to say something happened that may cause a swap in the
// future. The name should not refer to SetNeedsRedraw but it does for now.
NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+ events_metrics_manager_.SaveActiveEventsMetrics();
client_->SetNeedsOneBeginImplFrameOnImplThread();
}
void LayerTreeHostImpl::SetNeedsRedraw() {
NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+ events_metrics_manager_.SaveActiveEventsMetrics();
client_->SetNeedsRedrawOnImplThread();
}
@@ -5860,6 +5868,7 @@
// event, the LatencyInfo associated with the input event will not be
// added as a swap promise and we won't get any swap results.
NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+ events_metrics_manager_.SaveActiveEventsMetrics();
// The animation is no longer targeting a snap position. By clearing the
// target, this will ensure that we attempt to resnap at the end of this
@@ -6171,6 +6180,15 @@
client_->NeedsImplSideInvalidation(needs_first_draw_on_activation);
}
+std::vector<EventMetrics> LayerTreeHostImpl::TakeEventsMetrics() {
+ return events_metrics_manager_.TakeSavedEventsMetrics();
+}
+
+void LayerTreeHostImpl::AppendEventsMetrics(
+ std::vector<EventMetrics> events_metrics) {
+ events_metrics_manager_.AppendToSavedEventsMetrics(std::move(events_metrics));
+}
+
base::WeakPtr<LayerTreeHostImpl> LayerTreeHostImpl::AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 6b7938e..b9afa750 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -28,6 +28,8 @@
#include "cc/input/scrollbar_animation_controller.h"
#include "cc/input/scrollbar_controller.h"
#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/paint/discardable_image_map.h"
#include "cc/paint/paint_worklet_job.h"
@@ -301,6 +303,8 @@
const gfx::Point& viewport_point) const override;
std::unique_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
ui::LatencyInfo* latency) override;
+ std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+ GetScopedEventMetricsMonitor(const EventMetrics& event_metrics) override;
ScrollElasticityHelper* CreateScrollElasticityHelper() override;
bool GetScrollOffsetForLayer(ElementId element_id,
gfx::ScrollOffset* offset) override;
@@ -325,6 +329,9 @@
void RequestBeginFrameForAnimatedImages() override;
void RequestInvalidationForAnimatedImages() override;
+ std::vector<EventMetrics> TakeEventsMetrics();
+ void AppendEventsMetrics(std::vector<EventMetrics> events_metrics);
+
base::WeakPtr<LayerTreeHostImpl> AsWeakPtr();
void set_resourceless_software_draw_for_testing() {
@@ -1341,6 +1348,8 @@
// Helper for de-jelly logic.
DeJellyState de_jelly_state_;
+ EventsMetricsManager events_metrics_manager_;
+
// Must be the last member to ensure this is destroyed first in the
// destruction order and invalidates all weak pointers.
base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this};
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 2ad76b8..fa484a8a 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -728,7 +728,8 @@
if (host_impl_->DrawLayers(&frame)) {
DCHECK_NE(frame.frame_token, 0u);
// Drawing implies we submitted a frame to the LayerTreeFrameSink.
- scheduler_->DidSubmitCompositorFrame(frame.frame_token);
+ scheduler_->DidSubmitCompositorFrame(frame.frame_token,
+ host_impl_->TakeEventsMetrics());
}
result = DRAW_SUCCESS;
} else {
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index b933c98..5fb3d5c 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -308,6 +308,11 @@
TRACE_EVENT_INSTANT0("cc", "EarlyOut_NoUpdates", TRACE_EVENT_SCOPE_THREAD);
std::vector<std::unique_ptr<SwapPromise>> swap_promises =
layer_tree_host_->GetSwapPromiseManager()->TakeSwapPromises();
+
+ // Since the BeginMainFrame has been aborted, handling of events on the main
+ // frame had no effect and no metrics should be reported for such events.
+ layer_tree_host_->ClearEventsMetrics();
+
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 1c424d4..3d36a55d 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -695,7 +695,7 @@
if (scheduler_on_impl_thread_) {
// Drawing implies we submitted a frame to the LayerTreeFrameSink.
scheduler_on_impl_thread_->DidSubmitCompositorFrame(
- frame->frame_token);
+ frame->frame_token, host_impl_->TakeEventsMetrics());
}
single_thread_client_->DidSubmitCompositorFrame();
}