blob: e6586c87ce22dee98c1faccdb5ccef8dd3f56d18 [file] [log] [blame]
// Copyright 2019 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/compositor_frame_reporter.h"
#include <string>
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/rolling_time_delta_history.h"
#include "cc/metrics/frame_sequence_tracker.h"
namespace cc {
namespace {
using StageType = CompositorFrameReporter::StageType;
using BlinkBreakdown = CompositorFrameReporter::BlinkBreakdown;
using VizBreakdown = CompositorFrameReporter::VizBreakdown;
constexpr int kMissedFrameReportTypeCount =
static_cast<int>(CompositorFrameReporter::MissedFrameReportTypes::
kMissedFrameReportTypeCount);
constexpr int kStageTypeCount = static_cast<int>(StageType::kStageTypeCount);
constexpr int kAllBreakdownCount =
static_cast<int>(VizBreakdown::kBreakdownCount) +
static_cast<int>(BlinkBreakdown::kBreakdownCount);
constexpr int kVizBreakdownInitialIndex = kStageTypeCount;
constexpr int kBlinkBreakdownInitialIndex =
kVizBreakdownInitialIndex + static_cast<int>(VizBreakdown::kBreakdownCount);
// For each possible FrameSequenceTrackerType there will be a UMA histogram
// plus one for general case.
constexpr int kFrameSequenceTrackerTypeCount =
static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
// Names for CompositorFrameReporter::StageType, which should be updated in case
// of changes to the enum.
constexpr const char* GetStageName(int stage_type_index) {
switch (stage_type_index) {
case static_cast<int>(StageType::kBeginImplFrameToSendBeginMainFrame):
return "BeginImplFrameToSendBeginMainFrame";
case static_cast<int>(StageType::kSendBeginMainFrameToCommit):
return "SendBeginMainFrameToCommit";
case static_cast<int>(StageType::kCommit):
return "Commit";
case static_cast<int>(StageType::kEndCommitToActivation):
return "EndCommitToActivation";
case static_cast<int>(StageType::kActivation):
return "Activation";
case static_cast<int>(StageType::kEndActivateToSubmitCompositorFrame):
return "EndActivateToSubmitCompositorFrame";
case static_cast<int>(
StageType::kSubmitCompositorFrameToPresentationCompositorFrame):
return "SubmitCompositorFrameToPresentationCompositorFrame";
case static_cast<int>(StageType::kTotalLatency):
return "TotalLatency";
case static_cast<int>(VizBreakdown::kSubmitToReceiveCompositorFrame) +
kVizBreakdownInitialIndex:
return "SubmitCompositorFrameToPresentationCompositorFrame."
"SubmitToReceiveCompositorFrame";
case static_cast<int>(VizBreakdown::kReceivedCompositorFrameToStartDraw) +
kVizBreakdownInitialIndex:
return "SubmitCompositorFrameToPresentationCompositorFrame."
"ReceivedCompositorFrameToStartDraw";
case static_cast<int>(VizBreakdown::kStartDrawToSwapEnd) +
kVizBreakdownInitialIndex:
return "SubmitCompositorFrameToPresentationCompositorFrame."
"StartDrawToSwapEnd";
case static_cast<int>(VizBreakdown::kSwapEndToPresentationCompositorFrame) +
kVizBreakdownInitialIndex:
return "SubmitCompositorFrameToPresentationCompositorFrame."
"SwapEndToPresentationCompositorFrame";
case static_cast<int>(BlinkBreakdown::kHandleInputEvents) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.HandleInputEvents";
case static_cast<int>(BlinkBreakdown::kAnimate) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.Animate";
case static_cast<int>(BlinkBreakdown::kStyleUpdate) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.StyleUpdate";
case static_cast<int>(BlinkBreakdown::kLayoutUpdate) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.LayoutUpdate";
case static_cast<int>(BlinkBreakdown::kPrepaint) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.Prepaint";
case static_cast<int>(BlinkBreakdown::kComposite) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.Composite";
case static_cast<int>(BlinkBreakdown::kPaint) + kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.Paint";
case static_cast<int>(BlinkBreakdown::kScrollingCoordinator) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.ScrollingCoordinator";
case static_cast<int>(BlinkBreakdown::kCompositeCommit) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.CompositeCommit";
case static_cast<int>(BlinkBreakdown::kUpdateLayers) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.UpdateLayers";
default:
return "";
}
}
// Names for CompositorFrameReporter::MissedFrameReportTypes, which should be
// updated in case of changes to the enum.
constexpr const char* kReportTypeNames[]{"", "MissedFrame.",
"MissedFrameLatencyIncrease."};
static_assert(sizeof(kReportTypeNames) / sizeof(kReportTypeNames[0]) ==
kMissedFrameReportTypeCount,
"Compositor latency report types has changed.");
// This value should be recalculate in case of changes to the number of values
// in CompositorFrameReporter::MissedFrameReportTypes or in
// CompositorFrameReporter::StageType
constexpr int kMaxHistogramIndex = kMissedFrameReportTypeCount *
kFrameSequenceTrackerTypeCount *
(kStageTypeCount + kAllBreakdownCount);
constexpr int kHistogramMin = 1;
constexpr int kHistogramMax = 350000;
constexpr int kHistogramBucketCount = 50;
std::string HistogramName(const int report_type_index,
const int frame_sequence_tracker_type_index,
const int stage_type_index) {
DCHECK_LE(frame_sequence_tracker_type_index,
FrameSequenceTrackerType::kMaxType);
const char* tracker_type_name =
FrameSequenceTracker::GetFrameSequenceTrackerTypeName(
frame_sequence_tracker_type_index);
DCHECK(tracker_type_name);
return base::StrCat({"CompositorLatency.",
kReportTypeNames[report_type_index], tracker_type_name,
*tracker_type_name ? "." : "",
GetStageName(stage_type_index)});
}
} // namespace
CompositorFrameReporter::CompositorFrameReporter(
const base::flat_set<FrameSequenceTrackerType>* active_trackers,
bool is_single_threaded)
: is_single_threaded_(is_single_threaded),
active_trackers_(active_trackers) {
TRACE_EVENT_ASYNC_BEGIN1("cc,benchmark", "PipelineReporter", this,
"is_single_threaded", is_single_threaded);
}
CompositorFrameReporter::~CompositorFrameReporter() {
TerminateReporter();
}
CompositorFrameReporter::StageData::StageData() = default;
CompositorFrameReporter::StageData::StageData(StageType stage_type,
base::TimeTicks start_time,
base::TimeTicks end_time)
: stage_type(stage_type), start_time(start_time), end_time(end_time) {}
CompositorFrameReporter::StageData::StageData(const StageData&) = default;
CompositorFrameReporter::StageData::~StageData() = default;
void CompositorFrameReporter::StartStage(
CompositorFrameReporter::StageType stage_type,
base::TimeTicks start_time) {
EndCurrentStage(start_time);
current_stage_.stage_type = stage_type;
current_stage_.start_time = start_time;
int stage_type_index = static_cast<int>(current_stage_.stage_type);
CHECK_LT(stage_type_index, static_cast<int>(StageType::kStageTypeCount));
CHECK_GE(stage_type_index, 0);
TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
"cc,benchmark", "PipelineReporter", this, GetStageName(stage_type_index),
start_time);
}
void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) {
if (current_stage_.start_time == base::TimeTicks())
return;
current_stage_.end_time = end_time;
stage_history_.push_back(current_stage_);
current_stage_.start_time = base::TimeTicks();
}
void CompositorFrameReporter::MissedSubmittedFrame() {
submitted_frame_missed_deadline_ = true;
}
void CompositorFrameReporter::TerminateFrame(
FrameTerminationStatus termination_status,
base::TimeTicks termination_time) {
frame_termination_status_ = termination_status;
frame_termination_time_ = termination_time;
EndCurrentStage(frame_termination_time_);
}
void CompositorFrameReporter::OnFinishImplFrame(base::TimeTicks timestamp) {
DCHECK(!did_finish_impl_frame_);
did_finish_impl_frame_ = true;
impl_frame_finish_time_ = timestamp;
}
void CompositorFrameReporter::OnAbortBeginMainFrame() {
did_abort_main_frame_ = false;
}
void CompositorFrameReporter::SetBlinkBreakdown(
std::unique_ptr<BeginMainFrameMetrics> blink_breakdown) {
DCHECK(blink_breakdown_.paint.is_zero());
if (blink_breakdown)
blink_breakdown_ = *blink_breakdown;
else
blink_breakdown_ = BeginMainFrameMetrics();
}
void CompositorFrameReporter::SetVizBreakdown(
const viz::FrameTimingDetails& viz_breakdown) {
DCHECK(viz_breakdown_.received_compositor_frame_timestamp.is_null());
viz_breakdown_ = viz_breakdown;
}
void CompositorFrameReporter::TerminateReporter() {
if (frame_termination_status_ != FrameTerminationStatus::kUnknown)
DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
bool report_latency = false;
const char* termination_status_str = nullptr;
switch (frame_termination_status_) {
case FrameTerminationStatus::kPresentedFrame:
report_latency = true;
termination_status_str = "presented_frame";
break;
case FrameTerminationStatus::kDidNotPresentFrame:
report_latency = true;
MissedSubmittedFrame();
termination_status_str = "did_not_present_frame";
break;
case FrameTerminationStatus::kMainFrameAborted:
termination_status_str = "main_frame_aborted";
break;
case FrameTerminationStatus::kReplacedByNewReporter:
report_latency = true;
MissedSubmittedFrame();
termination_status_str = "replaced_by_new_reporter_at_same_stage";
break;
case FrameTerminationStatus::kDidNotProduceFrame:
termination_status_str = "did_not_produce_frame";
break;
case FrameTerminationStatus::kUnknown:
termination_status_str = "terminated_before_ending";
break;
}
const char* submission_status_str =
submitted_frame_missed_deadline_ ? "missed_frame" : "non_missed_frame";
TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP2(
"cc,benchmark", "PipelineReporter", this, frame_termination_time_,
"termination_status", termination_status_str,
"compositor_frame_submission_status", submission_status_str);
// Only report histograms if the frame was presented.
if (report_latency) {
DCHECK(stage_history_.size());
stage_history_.emplace_back(StageType::kTotalLatency,
stage_history_.front().start_time,
stage_history_.back().end_time);
ReportStageHistograms(submitted_frame_missed_deadline_);
}
}
void CompositorFrameReporter::ReportStageHistograms(bool missed_frame) const {
CompositorFrameReporter::MissedFrameReportTypes report_type =
missed_frame
? CompositorFrameReporter::MissedFrameReportTypes::kMissedFrame
: CompositorFrameReporter::MissedFrameReportTypes::kNonMissedFrame;
for (const StageData& stage : stage_history_) {
ReportStageHistogramWithBreakdown(report_type, stage);
for (const auto& frame_sequence_tracker_type : *active_trackers_) {
// Report stage breakdowns.
ReportStageHistogramWithBreakdown(report_type, stage,
frame_sequence_tracker_type);
}
}
}
void CompositorFrameReporter::ReportStageHistogramWithBreakdown(
CompositorFrameReporter::MissedFrameReportTypes report_type,
const CompositorFrameReporter::StageData& stage,
FrameSequenceTrackerType frame_sequence_tracker_type) const {
base::TimeDelta stage_delta = stage.end_time - stage.start_time;
ReportHistogram(report_type, frame_sequence_tracker_type,
static_cast<int>(stage.stage_type), stage_delta);
switch (stage.stage_type) {
case StageType::kSendBeginMainFrameToCommit: {
ReportBlinkBreakdowns(report_type, frame_sequence_tracker_type);
break;
}
case StageType::kSubmitCompositorFrameToPresentationCompositorFrame: {
ReportVizBreakdowns(report_type, stage.start_time,
frame_sequence_tracker_type);
break;
}
default:
break;
}
}
void CompositorFrameReporter::ReportBlinkBreakdowns(
CompositorFrameReporter::MissedFrameReportTypes report_type,
FrameSequenceTrackerType frame_sequence_tracker_type) const {
std::vector<std::pair<BlinkBreakdown, base::TimeDelta>> breakdowns = {
{BlinkBreakdown::kHandleInputEvents,
blink_breakdown_.handle_input_events},
{BlinkBreakdown::kAnimate, blink_breakdown_.animate},
{BlinkBreakdown::kStyleUpdate, blink_breakdown_.style_update},
{BlinkBreakdown::kLayoutUpdate, blink_breakdown_.layout_update},
{BlinkBreakdown::kPrepaint, blink_breakdown_.prepaint},
{BlinkBreakdown::kComposite, blink_breakdown_.composite},
{BlinkBreakdown::kPaint, blink_breakdown_.paint},
{BlinkBreakdown::kScrollingCoordinator,
blink_breakdown_.scrolling_coordinator},
{BlinkBreakdown::kCompositeCommit, blink_breakdown_.composite_commit},
{BlinkBreakdown::kUpdateLayers, blink_breakdown_.update_layers}};
for (const auto& pair : breakdowns) {
ReportHistogram(report_type, frame_sequence_tracker_type,
kBlinkBreakdownInitialIndex + static_cast<int>(pair.first),
pair.second);
}
}
void CompositorFrameReporter::ReportVizBreakdowns(
CompositorFrameReporter::MissedFrameReportTypes report_type,
base::TimeTicks start_time,
FrameSequenceTrackerType frame_sequence_tracker_type) const {
// Check if viz_breakdown is set.
if (viz_breakdown_.received_compositor_frame_timestamp.is_null())
return;
base::TimeDelta submit_to_receive_compositor_frame_delta =
viz_breakdown_.received_compositor_frame_timestamp - start_time;
ReportHistogram(
report_type, frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kSubmitToReceiveCompositorFrame),
submit_to_receive_compositor_frame_delta);
if (viz_breakdown_.draw_start_timestamp.is_null())
return;
base::TimeDelta received_compositor_frame_to_start_draw_delta =
viz_breakdown_.draw_start_timestamp -
viz_breakdown_.received_compositor_frame_timestamp;
ReportHistogram(
report_type, frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kReceivedCompositorFrameToStartDraw),
received_compositor_frame_to_start_draw_delta);
if (viz_breakdown_.swap_timings.is_null())
return;
base::TimeDelta start_draw_to_swap_end_delta =
viz_breakdown_.swap_timings.swap_end -
viz_breakdown_.draw_start_timestamp;
ReportHistogram(report_type, frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kStartDrawToSwapEnd),
start_draw_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(
report_type, frame_sequence_tracker_type,
kVizBreakdownInitialIndex +
static_cast<int>(VizBreakdown::kSwapEndToPresentationCompositorFrame),
swap_end_to_presentation_compositor_frame_delta);
}
void CompositorFrameReporter::ReportHistogram(
CompositorFrameReporter::MissedFrameReportTypes report_type,
FrameSequenceTrackerType frame_sequence_tracker_type,
const int stage_type_index,
base::TimeDelta time_delta) const {
const int report_type_index = static_cast<int>(report_type);
const int frame_sequence_tracker_type_index =
static_cast<int>(frame_sequence_tracker_type);
const int histogram_index =
(stage_type_index * kFrameSequenceTrackerTypeCount +
frame_sequence_tracker_type_index) *
kMissedFrameReportTypeCount +
report_type_index;
CHECK_LT(stage_type_index, kStageTypeCount + kAllBreakdownCount);
CHECK_GE(stage_type_index, 0);
CHECK_LT(report_type_index, kMissedFrameReportTypeCount);
CHECK_GE(report_type_index, 0);
CHECK_LT(histogram_index, kMaxHistogramIndex);
CHECK_GE(histogram_index, 0);
STATIC_HISTOGRAM_POINTER_GROUP(
HistogramName(report_type_index, frame_sequence_tracker_type_index,
stage_type_index),
histogram_index, kMaxHistogramIndex,
AddTimeMicrosecondsGranularity(time_delta),
base::Histogram::FactoryGet(
HistogramName(report_type_index, frame_sequence_tracker_type_index,
stage_type_index),
kHistogramMin, kHistogramMax, kHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
} // namespace cc