blob: 880058efca92f21eba40b423626af62111001254 [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 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);
// 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* kStageNames[] = {
[static_cast<int>(StageType::kBeginImplFrameToSendBeginMainFrame)] =
"BeginImplFrameToSendBeginMainFrame",
[static_cast<int>(StageType::kSendBeginMainFrameToCommit)] =
"SendBeginMainFrameToCommit",
[static_cast<int>(StageType::kCommit)] = "Commit",
[static_cast<int>(StageType::kEndCommitToActivation)] =
"EndCommitToActivation",
[static_cast<int>(StageType::kActivation)] = "Activation",
[static_cast<int>(StageType::kEndActivateToSubmitCompositorFrame)] =
"EndActivateToSubmitCompositorFrame",
[static_cast<int>(
StageType::kSubmitCompositorFrameToPresentationCompositorFrame)] =
"SubmitCompositorFrameToPresentationCompositorFrame",
[static_cast<int>(StageType::kTotalLatency)] = "TotalLatency",
[static_cast<int>(VizBreakdown::kSubmitToReceiveCompositorFrame) +
kStageTypeCount] =
"SubmitCompositorFrameToPresentationCompositorFrame."
"SubmitToReceiveCompositorFrame",
[static_cast<int>(VizBreakdown::kReceivedCompositorFrameToStartDraw) +
kStageTypeCount] =
"SubmitCompositorFrameToPresentationCompositorFrame."
"ReceivedCompositorFrameToStartDraw",
[static_cast<int>(VizBreakdown::kStartDrawToSwapEnd) + kStageTypeCount] =
"SubmitCompositorFrameToPresentationCompositorFrame.StartDrawToSwapEnd",
[static_cast<int>(VizBreakdown::kSwapEndToPresentationCompositorFrame) +
kStageTypeCount] =
"SubmitCompositorFrameToPresentationCompositorFrame."
"SwapEndToPresentationCompositorFrame"};
static_assert(sizeof(kStageNames) / sizeof(kStageNames[0]) ==
kStageTypeCount + kAllBreakdownCount,
"Compositor latency stages has changed.");
// 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::
kFrameSequenceTrackerTypeNames[frame_sequence_tracker_type_index];
DCHECK(tracker_type_name);
return base::StrCat({"CompositorLatency.",
kReportTypeNames[report_type_index], tracker_type_name,
*tracker_type_name ? "." : "",
kStageNames[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,
TRACE_STR_COPY(kStageNames[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::SetVizBreakdown(
const viz::FrameTimingDetails& viz_breakdown) {
DCHECK(current_stage_.viz_breakdown.received_compositor_frame_timestamp
.is_null());
current_stage_.viz_breakdown = viz_breakdown;
}
void CompositorFrameReporter::TerminateReporter() {
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:
termination_status_str = "did_not_present_frame";
break;
case FrameTerminationStatus::kMainFrameAborted:
termination_status_str = "main_frame_aborted";
break;
case FrameTerminationStatus::kReplacedByNewReporter:
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:
NOTREACHED();
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", TRACE_STR_COPY(termination_status_str),
"compositor_frame_submission_status",
TRACE_STR_COPY(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, FrameSequenceTrackerType::kMaxType, stage);
for (const auto& frame_sequence_tracker_type : *active_trackers_) {
// Report stage breakdowns.
ReportStageHistogramWithBreakdown(report_type,
frame_sequence_tracker_type, stage);
}
}
}
void CompositorFrameReporter::ReportStageHistogramWithBreakdown(
CompositorFrameReporter::MissedFrameReportTypes report_type,
FrameSequenceTrackerType frame_sequence_tracker_type,
CompositorFrameReporter::StageData stage) 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::kSubmitCompositorFrameToPresentationCompositorFrame: {
ReportVizBreakdown(report_type, frame_sequence_tracker_type, stage);
break;
}
default:
break;
}
}
void CompositorFrameReporter::ReportVizBreakdown(
CompositorFrameReporter::MissedFrameReportTypes report_type,
FrameSequenceTrackerType frame_sequence_tracker_type,
CompositorFrameReporter::StageData stage) const {
// Check if viz_breakdown is set.
if (stage.viz_breakdown.received_compositor_frame_timestamp.is_null())
return;
int index_origin = static_cast<int>(StageType::kStageTypeCount);
base::TimeDelta submit_to_receive_compositor_frame_delta =
stage.viz_breakdown.received_compositor_frame_timestamp -
stage.start_time;
ReportHistogram(report_type, frame_sequence_tracker_type, index_origin,
submit_to_receive_compositor_frame_delta);
if (stage.viz_breakdown.draw_start_timestamp.is_null())
return;
base::TimeDelta received_compositor_frame_to_start_draw_delta =
stage.viz_breakdown.draw_start_timestamp -
stage.viz_breakdown.received_compositor_frame_timestamp;
ReportHistogram(report_type, frame_sequence_tracker_type, index_origin + 1,
received_compositor_frame_to_start_draw_delta);
if (stage.viz_breakdown.swap_timings.is_null())
return;
base::TimeDelta start_draw_to_swap_end_delta =
stage.viz_breakdown.swap_timings.swap_end -
stage.viz_breakdown.draw_start_timestamp;
ReportHistogram(report_type, frame_sequence_tracker_type, index_origin + 2,
start_draw_to_swap_end_delta);
base::TimeDelta swap_end_to_presentation_compositor_frame_delta =
stage.end_time - stage.viz_breakdown.swap_timings.swap_end;
ReportHistogram(report_type, frame_sequence_tracker_type, index_origin + 3,
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