blob: 80cf4b06b8ce132beba1c10f137841e97e772240 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/metrics/compositor_frame_reporter.h"
#include <algorithm>
#include <array>
#include <memory>
#include <utility>
#include <vector>
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "cc/base/features.h"
#include "cc/metrics/compositor_frame_reporting_controller.h"
#include "cc/metrics/event_metrics.h"
#include "cc/metrics/frame_sorter.h"
#include "cc/scheduler/scheduler.h"
#include "cc/test/event_metrics_test_creator.h"
#include "components/viz/common/frame_timing_details.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/types/scroll_input_type.h"
namespace cc {
namespace {
using ::testing::_;
using ::testing::Each;
using ::testing::IsEmpty;
using ::testing::NotNull;
using ::testing::SaveArg;
class MockFrameSorter : public FrameSorter {
public:
MockFrameSorter() = default;
~MockFrameSorter() override = default;
MOCK_METHOD(void, AddFrameInfoToBuffer, (const FrameInfo&), (override));
MOCK_METHOD(void, AddNewFrame, (const viz::BeginFrameArgs&), (override));
MOCK_METHOD(void,
AddFrameResult,
(const viz::BeginFrameArgs&, const FrameInfo&),
(override));
};
class CompositorFrameReporterTest : public testing::Test {
public:
CompositorFrameReporterTest() : pipeline_reporter_(CreatePipelineReporter()) {
AdvanceNowByUs(1);
}
protected:
base::TimeTicks AdvanceNowByUs(int advance_us) {
test_tick_clock_.Advance(base::Microseconds(advance_us));
return test_tick_clock_.NowTicks();
}
base::TimeTicks Now() { return test_tick_clock_.NowTicks(); }
std::unique_ptr<BeginMainFrameMetrics> BuildBlinkBreakdown() {
auto breakdown = std::make_unique<BeginMainFrameMetrics>();
breakdown->handle_input_events = base::Microseconds(10);
breakdown->animate = base::Microseconds(9);
breakdown->style_update = base::Microseconds(8);
breakdown->layout_update = base::Microseconds(7);
breakdown->compositing_inputs = base::Microseconds(6);
breakdown->prepaint = base::Microseconds(5);
breakdown->paint = base::Microseconds(3);
breakdown->composite_commit = base::Microseconds(2);
breakdown->update_layers = base::Microseconds(1);
// Advance now by the sum of the breakdowns.
AdvanceNowByUs(10 + 9 + 8 + 7 + 6 + 5 + 3 + 2 + 1);
return breakdown;
}
viz::FrameTimingDetails BuildVizBreakdown() {
viz::FrameTimingDetails viz_breakdown;
viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(1);
viz_breakdown.draw_start_timestamp = AdvanceNowByUs(2);
viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(3);
viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(4);
viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(5);
return viz_breakdown;
}
viz::FrameTimingDetails BuildVizBreakdownWithTreesInVizTimestamps() {
viz::FrameTimingDetails viz_breakdown;
// Optional TreesInViz - related timestamps should happen *before* other
// details
viz_breakdown.start_update_display_tree = AdvanceNowByUs(1);
viz_breakdown.start_prepare_to_draw = AdvanceNowByUs(2);
viz_breakdown.start_draw_layers = AdvanceNowByUs(3);
viz_breakdown.submit_compositor_frame = AdvanceNowByUs(4);
viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(5);
viz_breakdown.draw_start_timestamp = AdvanceNowByUs(6);
viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(7);
viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(8);
viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(9);
return viz_breakdown;
}
std::unique_ptr<EventMetrics> SetupEventMetrics(
std::unique_ptr<EventMetrics> metrics) {
if (metrics) {
AdvanceNowByUs(3);
metrics->SetDispatchStageTimestamp(
EventMetrics::DispatchStage::kRendererCompositorStarted);
AdvanceNowByUs(3);
metrics->SetDispatchStageTimestamp(
EventMetrics::DispatchStage::kRendererCompositorFinished);
}
return metrics;
}
// Sets up the dispatch durations of each EventMetrics according to
// stage_durations. Stages with a duration of -1 will be skipped.
std::unique_ptr<EventMetrics> SetupEventMetricsWithDispatchTimes(
std::unique_ptr<EventMetrics> metrics,
const std::vector<int>& stage_durations) {
if (metrics) {
int num_stages = stage_durations.size();
int max_num_stages =
static_cast<int>(EventMetrics::DispatchStage::kMaxValue) + 1;
CHECK(num_stages <= max_num_stages)
<< num_stages << " > " << max_num_stages;
// Start indexing from 2 because the 0th index held duration from
// kGenerated to kArrivedInRendererCompositor, which was already used in
// when the EventMetrics was created.
for (int i = 2; i < num_stages; i++) {
if (stage_durations[i] >= 0) {
AdvanceNowByUs(stage_durations[i]);
metrics->SetDispatchStageTimestamp(
EventMetrics::DispatchStage(i + 2));
}
}
}
return metrics;
}
std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) {
const base::TimeTicks event_time = AdvanceNowByUs(3);
const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByUs(2);
AdvanceNowByUs(3);
return SetupEventMetrics(EventMetrics::CreateForTesting(
type, event_time, arrived_in_browser_main_timestamp, &test_tick_clock_,
std::nullopt));
}
// Creates EventMetrics with elements in stage_durations representing each
// dispatch stage's desired duration respectively, with the 0th index
// representing the duration from kGenerated to kArrivedInRendererCompositor.
// stage_durations must have at least 1 element for the first required stage
// Use -1 for stages that want to be skipped.
std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetricsWithDispatchTimes(
bool is_inertial,
ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type,
const std::vector<int>& stage_durations) {
CHECK_GE(stage_durations.size(), 2u);
const base::TimeTicks event_time = AdvanceNowByUs(3);
// kGenerated -> kArrivedInBrowserMain
int begin_rwh_latency_us = stage_durations[0];
const base::TimeTicks arrived_in_browser_main_timestamp =
AdvanceNowByUs(begin_rwh_latency_us);
// kArrivedInBrowserMain -> kArrivedInRendererCompositor
AdvanceNowByUs(stage_durations[1]);
// Creates a kGestureScrollUpdate event.
return SetupEventMetricsWithDispatchTimes(
ScrollUpdateEventMetrics::CreateForTesting(
ui::EventType::kGestureScrollUpdate, ui::ScrollInputType::kWheel,
is_inertial, scroll_update_type, /*delta=*/10.0f, event_time,
arrived_in_browser_main_timestamp, &test_tick_clock_, std::nullopt),
stage_durations);
}
std::unique_ptr<EventMetrics> CreateScrollBeginMetrics(
ui::ScrollInputType input_type) {
const base::TimeTicks event_time = AdvanceNowByUs(3);
const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByUs(2);
AdvanceNowByUs(3);
return SetupEventMetrics(ScrollEventMetrics::CreateForTesting(
ui::EventType::kGestureScrollBegin, input_type,
/*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
&test_tick_clock_));
}
std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetrics(
ui::ScrollInputType input_type,
bool is_inertial,
ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) {
const base::TimeTicks event_time = AdvanceNowByUs(3);
const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByUs(2);
AdvanceNowByUs(3);
return SetupEventMetrics(ScrollUpdateEventMetrics::CreateForTesting(
ui::EventType::kGestureScrollUpdate, input_type, is_inertial,
scroll_update_type, /*delta=*/10.0f, event_time,
arrived_in_browser_main_timestamp, &test_tick_clock_, std::nullopt));
}
std::unique_ptr<EventMetrics> CreatePinchEventMetrics(
ui::EventType type,
ui::ScrollInputType input_type) {
const base::TimeTicks event_time = AdvanceNowByUs(3);
AdvanceNowByUs(3);
return SetupEventMetrics(PinchEventMetrics::CreateForTesting(
type, input_type, event_time, &test_tick_clock_));
}
std::vector<base::TimeTicks> GetEventTimestamps(
const EventMetrics::List& events_metrics) {
std::vector<base::TimeTicks> event_times;
event_times.reserve(events_metrics.size());
std::ranges::transform(events_metrics, std::back_inserter(event_times),
[](const auto& event_metrics) {
return event_metrics->GetDispatchStageTimestamp(
EventMetrics::DispatchStage::kGenerated);
});
return event_times;
}
std::unique_ptr<CompositorFrameReporter> CreatePipelineReporter() {
GlobalMetricsTrackers trackers{nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, &frame_sorter_};
auto reporter = std::make_unique<CompositorFrameReporter>(
ActiveTrackers(), viz::BeginFrameArgs(),
/*should_report_metrics=*/true,
CompositorFrameReporter::SmoothThread::kSmoothBoth,
FrameInfo::SmoothEffectDrivingThread::kUnknown,
/*layer_tree_host_id=*/1, trackers);
reporter->set_tick_clock(&test_tick_clock_);
return reporter;
}
std::unique_ptr<CompositorFrameReporter> CreateReporterWithMockSorter(
MockFrameSorter* mock_sorter_ptr,
const viz::BeginFrameArgs& args,
FrameInfo::SmoothThread smooth_thread =
FrameInfo::SmoothThread::kSmoothNone,
FrameInfo::SmoothEffectDrivingThread scrolling_thread =
FrameInfo::SmoothEffectDrivingThread::kUnknown) {
GlobalMetricsTrackers trackers{nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, mock_sorter_ptr};
auto reporter = std::make_unique<CompositorFrameReporter>(
ActiveTrackers(), args,
/*should_report_metrics=*/true, smooth_thread, scrolling_thread,
/*layer_tree_host_id=*/1, trackers);
reporter->set_tick_clock(&test_tick_clock_);
return reporter;
}
void IntToTimeDeltaVector(std::vector<base::TimeDelta>& timedelta_vector,
std::vector<int> int_vector) {
size_t vector_size = int_vector.size();
for (size_t i = 0; i < vector_size; i++) {
timedelta_vector[i] = base::Microseconds(int_vector[i]);
}
}
// Disable sub-sampling to deterministically record histograms under test.
base::MetricsSubSampler::ScopedAlwaysSampleForTesting no_subsampling_;
// This should be defined before |pipeline_reporter_| so it is created before
// and destroyed after that.
base::SimpleTestTickClock test_tick_clock_;
FrameSorter frame_sorter_;
std::unique_ptr<CompositorFrameReporter> pipeline_reporter_;
// Number of breakdown stages of the current PipelineReporter
const int kNumOfCompositorStages =
static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount) - 1;
const int kNumDispatchStages =
static_cast<int>(EventMetrics::DispatchStage::kMaxValue);
const base::TimeDelta kLatencyPredictionDeviationThreshold =
base::Milliseconds(8.33);
};
TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) {
base::HistogramTester histogram_tester;
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
Now());
EXPECT_EQ(3u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(3);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
EXPECT_EQ(4u, pipeline_reporter_->stage_history_size_for_testing());
pipeline_reporter_ = nullptr;
histogram_tester.ExpectTotalCount(
"CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.SendBeginMainFrameToCommit", 1);
histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 0);
histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
0);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
1);
}
TEST_F(CompositorFrameReporterTest, ReplacedByNewReporterReportingTest) {
base::HistogramTester histogram_tester;
pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
Now());
EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndCommitToActivation, Now());
EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(2);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter,
Now());
EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
pipeline_reporter_ = nullptr;
histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 0);
histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
0);
}
TEST_F(CompositorFrameReporterTest, SubmittedFrameReportingTest) {
base::HistogramTester histogram_tester;
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kActivation, Now());
EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
AdvanceNowByUs(2);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
pipeline_reporter_ = nullptr;
histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
histogram_tester.ExpectTotalCount("CompositorLatency2.TotalLatency", 1);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.DroppedFrame.Activation", 0);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.DroppedFrame.EndActivateToSubmitCompositorFrame", 0);
histogram_tester.ExpectTotalCount(
"CompositorLatency2.DroppedFrame.TotalLatency", 0);
histogram_tester.ExpectBucketCount("CompositorLatency2.Activation", 3, 1);
histogram_tester.ExpectBucketCount(
"CompositorLatency2.EndActivateToSubmitCompositorFrame", 2, 1);
histogram_tester.ExpectBucketCount("CompositorLatency2.TotalLatency", 5, 1);
}
// Tests that timestamps are converted to latency histograms correctly over the
// TreesInViz lifecycle.
TEST_F(CompositorFrameReporterTest, TreesInVizLifecycleTest) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kTreesInViz);
base::HistogramTester histogram_tester;
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kActivation, Now());
AdvanceNowByUs(3); // This should be the Activation delta
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitUpdateDisplayTree,
Now());
AdvanceNowByUs(1); // This should be the EndActivateToDrawLayers delta
pipeline_reporter_->SetTreesInVizBranchTime(Now());
AdvanceNowByUs(2); // This should be DrawLayersToSendUpdateDisplayTree delta
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitUpdateDisplayTreeToPresentationCompositorFrame,
Now());
viz::FrameTimingDetails viz_breakdown =
BuildVizBreakdownWithTreesInVizTimestamps();
pipeline_reporter_->SetVizBreakdown(viz_breakdown);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
viz_breakdown.presentation_feedback.timestamp);
EXPECT_EQ(3u, pipeline_reporter_->stage_history_size_for_testing());
pipeline_reporter_ = nullptr;
auto x = histogram_tester.GetAllHistogramsRecorded();
// Confirm TreesInViz expected latencies
struct {
const char* name;
const base::HistogramBase::Sample32 latency_ms;
} expected_latencies[] = {
{"CompositorLatency2.Activation",
static_cast<base::HistogramBase::Sample32>((3))},
{"CompositorLatency2.EndActivateToSubmitUpdateDisplayTree."
"EndActivateToDrawLayers",
static_cast<base::HistogramBase::Sample32>((1))},
{"CompositorLatency2.EndActivateToSubmitUpdateDisplayTree."
"DrawLayersToSubmitUpdateDisplayTree",
static_cast<base::HistogramBase::Sample32>((2))},
// Remaining values derived from BuildVizBreakdownWithTreesInVizTimestamps
{"CompositorLatency2."
"SubmitUpdateDisplayTreeToPresentationCompositorFrame."
"SendUpdateDisplayTreeToReceiveUpdateDisplayTree",
static_cast<base::HistogramBase::Sample32>((1))},
{"CompositorLatency2."
"SubmitUpdateDisplayTreeToPresentationCompositorFrame."
"ReceiveUpdateDisplayTreeToStartPrepareToDraw",
static_cast<base::HistogramBase::Sample32>((2))},
{"CompositorLatency2."
"SubmitUpdateDisplayTreeToPresentationCompositorFrame."
"StartPrepareToDrawToStartDrawLayers",
static_cast<base::HistogramBase::Sample32>((3))},
{"CompositorLatency2."
"SubmitUpdateDisplayTreeToPresentationCompositorFrame."
"StartDrawLayersToSubmitCompositorFrame",
static_cast<base::HistogramBase::Sample32>((4))},
// Total time of EndActivateToSubmitUpdateDisplayTree should be 1 + 2
{"CompositorLatency2.EndActivateToSubmitUpdateDisplayTree",
static_cast<base::HistogramBase::Sample32>((3))},
// Total time of SubmitUpdateDisplayTreeToPresentationCompositorFrame
// should be 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45
{"CompositorLatency2."
"SubmitUpdateDisplayTreeToPresentationCompositorFrame",
static_cast<base::HistogramBase::Sample32>((45))},
};
for (const auto& expected_latency : expected_latencies) {
histogram_tester.ExpectBucketCount(expected_latency.name,
expected_latency.latency_ms, 1);
}
}
// Tests that when a frame is presented to the user, total event latency metrics
// are reported properly.
TEST_F(CompositorFrameReporterTest,
EventLatencyTotalForPresentedFrameReported) {
base::HistogramTester histogram_tester;
std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
CreateEventMetrics(ui::EventType::kTouchPressed),
CreateEventMetrics(ui::EventType::kTouchMoved),
CreateEventMetrics(ui::EventType::kTouchMoved),
};
EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
EventMetrics::List events_metrics(
std::make_move_iterator(std::begin(event_metrics_ptrs)),
std::make_move_iterator(std::end(event_metrics_ptrs)));
std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
Now());
pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
const base::TimeTicks presentation_time = AdvanceNowByUs(3);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
presentation_time);
pipeline_reporter_ = nullptr;
struct {
const char* name;
const base::HistogramBase::Count32 count;
} expected_counts[] = {
{"EventLatency.TouchPressed.TotalLatency", 1},
{"EventLatency.TouchMoved.TotalLatency", 2},
{"EventLatency.TotalLatency", 3},
};
for (const auto& expected_count : expected_counts) {
histogram_tester.ExpectTotalCount(expected_count.name,
expected_count.count);
}
struct {
const char* name;
const base::HistogramBase::Sample32 latency_ms;
} expected_latencies[] = {
{"EventLatency.TouchPressed.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[0]).InMicroseconds())},
{"EventLatency.TouchMoved.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[1]).InMicroseconds())},
{"EventLatency.TouchMoved.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[2]).InMicroseconds())},
{"EventLatency.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[0]).InMicroseconds())},
{"EventLatency.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[1]).InMicroseconds())},
{"EventLatency.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[2]).InMicroseconds())},
};
for (const auto& expected_latency : expected_latencies) {
histogram_tester.ExpectBucketCount(expected_latency.name,
expected_latency.latency_ms, 1);
}
}
// Tests that when a frame is presented to the user, total scroll event latency
// metrics are reported properly.
TEST_F(CompositorFrameReporterTest,
EventLatencyScrollTotalForPresentedFrameReported) {
base::HistogramTester histogram_tester;
const bool kScrollIsInertial = true;
const bool kScrollIsNotInertial = false;
std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
CreateScrollBeginMetrics(ui::ScrollInputType::kWheel),
CreateScrollUpdateEventMetrics(
ui::ScrollInputType::kWheel, kScrollIsNotInertial,
ScrollUpdateEventMetrics::ScrollUpdateType::kStarted),
CreateScrollUpdateEventMetrics(
ui::ScrollInputType::kWheel, kScrollIsNotInertial,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued),
CreateScrollUpdateEventMetrics(
ui::ScrollInputType::kWheel, kScrollIsInertial,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued),
CreateScrollBeginMetrics(ui::ScrollInputType::kTouchscreen),
CreateScrollUpdateEventMetrics(
ui::ScrollInputType::kTouchscreen, kScrollIsNotInertial,
ScrollUpdateEventMetrics::ScrollUpdateType::kStarted),
CreateScrollUpdateEventMetrics(
ui::ScrollInputType::kTouchscreen, kScrollIsNotInertial,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued),
CreateScrollUpdateEventMetrics(
ui::ScrollInputType::kTouchscreen, kScrollIsInertial,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued),
};
EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
EventMetrics::List events_metrics(
std::make_move_iterator(std::begin(event_metrics_ptrs)),
std::make_move_iterator(std::end(event_metrics_ptrs)));
std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
Now());
pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
AdvanceNowByUs(3);
viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown();
pipeline_reporter_->SetVizBreakdown(viz_breakdown);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
viz_breakdown.presentation_feedback.timestamp);
pipeline_reporter_ = nullptr;
struct {
const char* name;
const base::HistogramBase::Count32 count;
} expected_counts[] = {
{"EventLatency.GestureScrollBegin.Wheel.TotalLatency2", 1},
{"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency2", 1},
{"EventLatency.GestureScrollUpdate.Wheel.TotalLatency2", 1},
{"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatency2", 1},
{"EventLatency.GestureScrollBegin.Touchscreen.TotalLatency2", 1},
{"EventLatency.FirstGestureScrollUpdate.Touchscreen.TotalLatency2", 1},
{"EventLatency.GestureScrollUpdate.Touchscreen.TotalLatency", 1},
{"EventLatency.GestureScrollUpdate.Touchscreen.TotalLatency2", 1},
{"EventLatency.InertialGestureScrollUpdate.Touchscreen.TotalLatency2", 1},
{"EventLatency.GestureScrollBegin.TotalLatency2", 2},
{"EventLatency.GestureScrollBegin.GenerationToBrowserMain", 2},
{"EventLatency.FirstGestureScrollUpdate.TotalLatency2", 2},
{"EventLatency.FirstGestureScrollUpdate.GenerationToBrowserMain", 2},
{"EventLatency.GestureScrollUpdate.TotalLatency2", 2},
{"EventLatency.GestureScrollUpdate.GenerationToBrowserMain", 2},
{"EventLatency.InertialGestureScrollUpdate.TotalLatency2", 2},
{"EventLatency.InertialGestureScrollUpdate.GenerationToBrowserMain", 2},
{"EventLatency.TotalLatency", 8},
};
for (const auto& expected_count : expected_counts) {
histogram_tester.ExpectTotalCount(expected_count.name,
expected_count.count);
}
const base::TimeTicks presentation_time =
viz_breakdown.presentation_feedback.timestamp;
struct {
const char* name;
const base::HistogramBase::Sample32 latency_ms;
} expected_latencies[] = {
{"EventLatency.GestureScrollBegin.Wheel.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[0]).InMicroseconds())},
{"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[1]).InMicroseconds())},
{"EventLatency.GestureScrollUpdate.Wheel.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[2]).InMicroseconds())},
{"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[3]).InMicroseconds())},
{"EventLatency.GestureScrollBegin.Touchscreen.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[4]).InMicroseconds())},
{"EventLatency.FirstGestureScrollUpdate.Touchscreen.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[5]).InMicroseconds())},
{"EventLatency.GestureScrollUpdate.Touchscreen.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[6]).InMicroseconds())},
{"EventLatency.GestureScrollUpdate.Touchscreen.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[6]).InMicroseconds())},
{"EventLatency.InertialGestureScrollUpdate.Touchscreen.TotalLatency2",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[7]).InMicroseconds())},
};
for (const auto& expected_latency : expected_latencies) {
histogram_tester.ExpectBucketCount(expected_latency.name,
expected_latency.latency_ms, 1);
}
}
// Tests that when a frame is presented to the user, total pinch event latency
// metrics are reported properly.
TEST_F(CompositorFrameReporterTest,
EventLatencyPinchTotalForPresentedFrameReported) {
base::HistogramTester histogram_tester;
std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
CreatePinchEventMetrics(ui::EventType::kGesturePinchBegin,
ui::ScrollInputType::kWheel),
CreatePinchEventMetrics(ui::EventType::kGesturePinchUpdate,
ui::ScrollInputType::kWheel),
CreatePinchEventMetrics(ui::EventType::kGesturePinchBegin,
ui::ScrollInputType::kTouchscreen),
CreatePinchEventMetrics(ui::EventType::kGesturePinchUpdate,
ui::ScrollInputType::kTouchscreen),
};
EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
EventMetrics::List events_metrics(
std::make_move_iterator(std::begin(event_metrics_ptrs)),
std::make_move_iterator(std::end(event_metrics_ptrs)));
std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
Now());
pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
AdvanceNowByUs(3);
viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown();
pipeline_reporter_->SetVizBreakdown(viz_breakdown);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
viz_breakdown.presentation_feedback.timestamp);
pipeline_reporter_ = nullptr;
struct {
const char* name;
const base::HistogramBase::Count32 count;
} expected_counts[] = {
{"EventLatency.GesturePinchBegin.Touchscreen.TotalLatency", 1},
{"EventLatency.GesturePinchUpdate.Touchscreen.TotalLatency", 1},
{"EventLatency.GesturePinchBegin.Touchpad.TotalLatency", 1},
{"EventLatency.GesturePinchUpdate.Touchpad.TotalLatency", 1},
{"EventLatency.TotalLatency", 4},
};
for (const auto& expected_count : expected_counts) {
histogram_tester.ExpectTotalCount(expected_count.name,
expected_count.count);
}
const base::TimeTicks presentation_time =
viz_breakdown.presentation_feedback.timestamp;
struct {
const char* name;
const base::HistogramBase::Sample32 latency_ms;
} expected_latencies[] = {
{"EventLatency.GesturePinchBegin.Touchpad.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[0]).InMicroseconds())},
{"EventLatency.GesturePinchUpdate.Touchpad.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[1]).InMicroseconds())},
{"EventLatency.GesturePinchBegin.Touchscreen.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[2]).InMicroseconds())},
{"EventLatency.GesturePinchUpdate.Touchscreen.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[3]).InMicroseconds())},
};
for (const auto& expected_latency : expected_latencies) {
histogram_tester.ExpectBucketCount(expected_latency.name,
expected_latency.latency_ms, 1);
}
}
// 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;
std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
CreateEventMetrics(ui::EventType::kTouchPressed),
CreateEventMetrics(ui::EventType::kTouchMoved),
CreateEventMetrics(ui::EventType::kTouchMoved),
};
EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
EventMetrics::List events_metrics(
std::make_move_iterator(std::begin(event_metrics_ptrs)),
std::make_move_iterator(std::end(event_metrics_ptrs)));
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
Now());
pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
AdvanceNowByUs(3);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
Now());
pipeline_reporter_ = nullptr;
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("EventLaterncy."),
IsEmpty());
}
// Tests that event metrics whose `EventMetrics::caused_frame_update()` returns
// false don't count towards EventLatency.* metrics.
TEST_F(CompositorFrameReporterTest,
EventLatencyTotalExcludesEventMetricsWhichDidNotCauseFrameUpdate) {
base::HistogramTester histogram_tester;
std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
CreateEventMetrics(ui::EventType::kTouchPressed),
CreateEventMetrics(ui::EventType::kTouchPressed),
CreateEventMetrics(ui::EventType::kTouchMoved),
CreateEventMetrics(ui::EventType::kTouchMoved),
CreateEventMetrics(ui::EventType::kTouchMoved),
};
event_metrics_ptrs[0]->set_caused_frame_update(false);
event_metrics_ptrs[4]->set_caused_frame_update(false);
EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
EventMetrics::List events_metrics(
std::make_move_iterator(std::begin(event_metrics_ptrs)),
std::make_move_iterator(std::end(event_metrics_ptrs)));
std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
Now());
AdvanceNowByUs(3);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
Now());
pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
const base::TimeTicks presentation_time = AdvanceNowByUs(3);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
presentation_time);
pipeline_reporter_ = nullptr;
struct {
const char* name;
const base::HistogramBase::Count32 count;
} expected_counts[] = {
{"EventLatency.TouchPressed.TotalLatency", 1},
{"EventLatency.TouchMoved.TotalLatency", 2},
{"EventLatency.TotalLatency", 3},
};
for (const auto& expected_count : expected_counts) {
histogram_tester.ExpectTotalCount(expected_count.name,
expected_count.count);
}
struct {
const char* name;
const base::HistogramBase::Sample32 latency_ms;
} expected_latencies[] = {
{"EventLatency.TouchPressed.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[1]).InMicroseconds())},
{"EventLatency.TouchMoved.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[2]).InMicroseconds())},
{"EventLatency.TouchMoved.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[3]).InMicroseconds())},
{"EventLatency.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[1]).InMicroseconds())},
{"EventLatency.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[2]).InMicroseconds())},
{"EventLatency.TotalLatency",
static_cast<base::HistogramBase::Sample32>(
(presentation_time - event_times[3]).InMicroseconds())},
};
for (const auto& expected_latency : expected_latencies) {
histogram_tester.ExpectBucketCount(expected_latency.name,
expected_latency.latency_ms, 1);
}
}
// Verifies that partial update dependent queues are working as expected when
// they reach their maximum capacity.
TEST_F(CompositorFrameReporterTest, PartialUpdateDependentQueues) {
// This constant should match the constant with the same name in
// compositor_frame_reporter.cc.
const size_t kMaxOwnedPartialUpdateDependents = 300u;
// The first three dependent reporters for the front of the queue.
auto deps = std::to_array<std::unique_ptr<CompositorFrameReporter>>({
CreatePipelineReporter(),
CreatePipelineReporter(),
CreatePipelineReporter(),
});
// Set `deps[0]` as a dependent of the main reporter and adopt it at the same
// time. This should enqueue it in both non-owned and owned dependents queues.
deps[0]->SetPartialUpdateDecider(pipeline_reporter_.get());
pipeline_reporter_->AdoptReporter(std::move(deps[0]));
DCHECK_EQ(1u,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
1u,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Set `deps[1]` as a dependent of the main reporter, but don't adopt it yet.
// This should enqueue it in non-owned dependents queue only.
deps[1]->SetPartialUpdateDecider(pipeline_reporter_.get());
DCHECK_EQ(2u,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
1u,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Set `deps[2]` as a dependent of the main reporter and adopt it at the same
// time. This should enqueue it in both non-owned and owned dependents queues.
deps[2]->SetPartialUpdateDecider(pipeline_reporter_.get());
pipeline_reporter_->AdoptReporter(std::move(deps[2]));
DCHECK_EQ(3u,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
2u,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Now adopt `deps[1]` to enqueue it in the owned dependents queue.
pipeline_reporter_->AdoptReporter(std::move(deps[1]));
DCHECK_EQ(3u,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
3u,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Fill the queues with more dependent reporters until the capacity is
// reached. After this, the queues should look like this (assuming n equals
// `kMaxOwnedPartialUpdateDependents`):
// Partial Update Dependents: [0, 1, 2, 3, 4, ..., n-1]
// Owned Partial Update Dependents: [0, 2, 1, 3, 4, ..., n-1]
while (
pipeline_reporter_->owned_partial_update_dependents_size_for_testing() <
kMaxOwnedPartialUpdateDependents) {
std::unique_ptr<CompositorFrameReporter> dependent =
CreatePipelineReporter();
dependent->SetPartialUpdateDecider(pipeline_reporter_.get());
pipeline_reporter_->AdoptReporter(std::move(dependent));
}
DCHECK_EQ(kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Enqueue a new dependent reporter. This should pop `deps[0]` from the front
// of the owned dependents queue and destroy it. Since the same one is in
// front of the non-owned dependents queue, it will be popped out of that
// queue, too. The queues will look like this:
// Partial Update Dependents: [1, 2, 3, 4, ..., n]
// Owned Partial Update Dependents: [2, 1, 3, 4, ..., n]
auto new_dep = CreatePipelineReporter();
new_dep->SetPartialUpdateDecider(pipeline_reporter_.get());
pipeline_reporter_->AdoptReporter(std::move(new_dep));
DCHECK_EQ(kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Enqueue another new dependent reporter. This should pop `deps[2]` from the
// front of the owned dependents queue and destroy it. It should be removed
// from the non-owned dependents queue as well.
// Partial Update Dependents: [2, 3, 4, ..., n+1]
// Owned Partial Update Dependents: [2, 3, 4, ..., n+1]
new_dep = CreatePipelineReporter();
new_dep->SetPartialUpdateDecider(pipeline_reporter_.get());
pipeline_reporter_->AdoptReporter(std::move(new_dep));
DCHECK_EQ(kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
// Enqueue yet another new dependent reporter. This should pop `deps[1]` from
// the front of the owned dependents queue and destroy it. Since the same one
// is in front of the non-owned dependents queue followed by `deps[2]` which
// was destroyed in the previous step, they will be popped out of that queue,
// too. The queues will look like this:
// Partial Update Dependents: [3, 4, ..., n+2]
// Owned Partial Update Dependents: [3, 4, ..., n+2]
new_dep = CreatePipelineReporter();
new_dep->SetPartialUpdateDecider(pipeline_reporter_.get());
pipeline_reporter_->AdoptReporter(std::move(new_dep));
DCHECK_EQ(kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->partial_update_dependents_size_for_testing());
DCHECK_EQ(
kMaxOwnedPartialUpdateDependents,
pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
}
TEST_F(CompositorFrameReporterTest, GenerateFrameInfo_PresentedAll) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(&mock_sorter, args);
reporter->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
auto begin_main_frame_start_time = AdvanceNowByUs(10);
reporter->StartStage(
CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
begin_main_frame_start_time);
std::unique_ptr<BeginMainFrameMetrics> blink_breakdown =
BuildBlinkBreakdown();
reporter->SetBlinkBreakdown(std::move(blink_breakdown),
begin_main_frame_start_time);
reporter->StartStage(CompositorFrameReporter::StageType::kCommit,
AdvanceNowByUs(10));
reporter->StartStage(
CompositorFrameReporter::StageType::kEndCommitToActivation,
AdvanceNowByUs(10));
reporter->StartStage(CompositorFrameReporter::StageType::kActivation,
AdvanceNowByUs(10));
reporter->StartStage(
CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
AdvanceNowByUs(10));
reporter->StartStage(CompositorFrameReporter::StageType::
kSubmitCompositorFrameToPresentationCompositorFrame,
AdvanceNowByUs(10));
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
AdvanceNowByUs(10));
reporter.reset();
EXPECT_EQ(captured_info.final_state,
FrameInfo::FrameFinalState::kPresentedAll);
EXPECT_EQ(captured_info.final_state_v4,
FrameInfo::FrameFinalState::kPresentedAll);
EXPECT_EQ(captured_info.main_thread_response,
FrameInfo::MainThreadResponse::kIncluded);
}
TEST_F(CompositorFrameReporterTest, GenerateFrameInfo_PresentedPartialNewMain) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(&mock_sorter, args);
reporter->set_is_accompanied_by_main_thread_update(true);
// Simulate partial update by having a decider (even if it's itself for
// simplicity here) This sets has_partial_update_ to true.
reporter->SetPartialUpdateDecider(reporter.get());
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
auto begin_main_frame_start_time = AdvanceNowByUs(10);
reporter->StartStage(
CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
begin_main_frame_start_time);
std::unique_ptr<BeginMainFrameMetrics> blink_breakdown =
BuildBlinkBreakdown();
reporter->SetBlinkBreakdown(std::move(blink_breakdown),
begin_main_frame_start_time);
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
AdvanceNowByUs(10));
reporter.reset();
EXPECT_EQ(captured_info.final_state,
FrameInfo::FrameFinalState::kPresentedPartialNewMain);
EXPECT_EQ(captured_info.final_state_v4,
FrameInfo::FrameFinalState::kPresentedPartialNewMain);
}
TEST_F(CompositorFrameReporterTest, GenerateFrameInfo_Dropped) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(&mock_sorter, args);
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
AdvanceNowByUs(10));
reporter.reset();
EXPECT_EQ(captured_info.final_state, FrameInfo::FrameFinalState::kDropped);
EXPECT_EQ(captured_info.final_state_v4, FrameInfo::FrameFinalState::kDropped);
}
TEST_F(CompositorFrameReporterTest, GenerateFrameInfo_DidNotProduce_NoDamage) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(&mock_sorter, args);
reporter->OnDidNotProduceFrame(FrameSkippedReason::kNoDamage);
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame,
AdvanceNowByUs(10));
reporter.reset();
EXPECT_EQ(captured_info.final_state,
FrameInfo::FrameFinalState::kNoUpdateDesired);
// V4 also considers this no update desired if main thread response is
// included.
EXPECT_EQ(captured_info.final_state_v4,
FrameInfo::FrameFinalState::kNoUpdateDesired);
EXPECT_EQ(captured_info.main_thread_response,
FrameInfo::MainThreadResponse::kIncluded);
}
TEST_F(CompositorFrameReporterTest,
GenerateFrameInfo_DidNotProduce_WaitingOnMain) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(
&mock_sorter, args, FrameInfo::SmoothThread::kSmoothCompositor);
reporter->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
auto begin_main_frame_start_time = AdvanceNowByUs(10);
reporter->StartStage(
CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
begin_main_frame_start_time);
std::unique_ptr<BeginMainFrameMetrics> blink_breakdown =
BuildBlinkBreakdown();
reporter->SetBlinkBreakdown(std::move(blink_breakdown),
begin_main_frame_start_time);
reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain);
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame,
AdvanceNowByUs(10));
reporter.reset();
// Since we never `SetPartialUpdateDecider` this is simulating the Compositor
// thread having no pending work. Which makes these `kNoUpdateDesired`.
EXPECT_EQ(captured_info.final_state,
FrameInfo::FrameFinalState::kNoUpdateDesired);
EXPECT_EQ(captured_info.final_state_v4,
FrameInfo::FrameFinalState::kNoUpdateDesired);
EXPECT_EQ(captured_info.smooth_thread,
FrameInfo::SmoothThread::kSmoothNone); // Adjusted
EXPECT_EQ(captured_info.main_thread_response,
FrameInfo::MainThreadResponse::kMissing);
}
TEST_F(CompositorFrameReporterTest,
GenerateFrameInfo_PresentedPartialWithoutWaiting) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(&mock_sorter, args);
reporter->SetPartialUpdateDecider(pipeline_reporter_.get());
reporter->set_will_throttle_main(false);
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
reporter->OnFinishImplFrame(Now(), /*waiting_for_main=*/false);
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
AdvanceNowByUs(10));
reporter.reset();
// Advance the Main thread reporter.
auto begin_main_frame_start_time = AdvanceNowByUs(10);
pipeline_reporter_->StartStage(
CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
begin_main_frame_start_time);
std::unique_ptr<BeginMainFrameMetrics> blink_breakdown =
BuildBlinkBreakdown();
pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown),
begin_main_frame_start_time);
pipeline_reporter_->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
AdvanceNowByUs(10));
pipeline_reporter_.reset();
EXPECT_EQ(captured_info.final_state,
FrameInfo::FrameFinalState::kPresentedPartialOldMain);
EXPECT_EQ(captured_info.final_state_v4,
FrameInfo::FrameFinalState::kPresentedPartialWithoutWaiting);
}
TEST_F(CompositorFrameReporterTest,
GenerateFrameInfo_MainThreadNoUpdate_CompositorDropped) {
MockFrameSorter mock_sorter;
viz::BeginFrameArgs args = viz::BeginFrameArgs();
args.frame_id = viz::BeginFrameId(1, 1);
auto reporter = CreateReporterWithMockSorter(&mock_sorter, args);
FrameInfo captured_info;
EXPECT_CALL(mock_sorter, AddFrameInfoToBuffer(_))
.WillOnce(SaveArg<0>(&captured_info));
EXPECT_CALL(mock_sorter, AddFrameResult(_, _)).Times(testing::AnyNumber());
reporter->StartStage(
CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
Now());
reporter->set_frame_skipped_reason_v4(FrameSkippedReason::kNoDamage);
reporter->TerminateFrame(
CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
AdvanceNowByUs(10));
reporter.reset();
EXPECT_EQ(captured_info.final_state, FrameInfo::FrameFinalState::kDropped);
EXPECT_EQ(captured_info.final_state_v4,
FrameInfo::FrameFinalState::kNoUpdateDesired);
}
} // namespace
} // namespace cc