#include "cc/metrics/compositor_frame_reporting_controller.h"
#include <memory>
#include <utility>
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "cc/base/features.h"
#include "cc/metrics/compositor_frame_reporter.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
#include "cc/metrics/latency_ukm_reporter.h"
#include "cc/metrics/scroll_jank_dropped_frame_tracker.h"
#include "cc/metrics/scroll_jank_v4_processor.h"
#include "cc/scheduler/scheduler_state_machine.h"
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/tracing/public/cpp/perfetto/macros.h"
namespace cc {
using StageType = CompositorFrameReporter::StageType;
using FrameTerminationStatus = CompositorFrameReporter::FrameTerminationStatus;
CompositorFrameReportingController::CompositorFrameReportingController(
bool should_report_histograms,
bool should_report_ukm,
int layer_tree_host_id,
bool is_trees_in_viz_client)
: should_report_histograms_(should_report_histograms),
layer_tree_host_id_(layer_tree_host_id),
is_trees_in_viz_client_(is_trees_in_viz_client),
latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()),
predictor_jank_tracker_(std::make_unique<PredictorJankTracker>()),
scroll_jank_dropped_frame_tracker_(
std::make_unique<ScrollJankDroppedFrameTracker>()),
scroll_jank_ukm_reporter_(std::make_unique<ScrollJankUkmReporter>()),
scroll_jank_v4_processor_(std::make_unique<ScrollJankV4Processor>()) {
if (should_report_ukm) {
global_trackers_.latency_ukm_reporter = latency_ukm_reporter_.get();
global_trackers_.scroll_jank_ukm_reporter = scroll_jank_ukm_reporter_.get();
predictor_jank_tracker_->set_scroll_jank_ukm_reporter(
scroll_jank_ukm_reporter_.get());
scroll_jank_dropped_frame_tracker_->set_scroll_jank_ukm_reporter(
scroll_jank_ukm_reporter_.get());
}
global_trackers_.predictor_jank_tracker = predictor_jank_tracker_.get();
global_trackers_.scroll_jank_dropped_frame_tracker =
scroll_jank_dropped_frame_tracker_.get();
global_trackers_.scroll_jank_v4_processor = scroll_jank_v4_processor_.get();
}
CompositorFrameReportingController::~CompositorFrameReportingController() {
base::TimeTicks now = Now();
for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) {
if (reporters_[i]) {
reporters_[i]->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
now);
}
}
for (auto& pair : submitted_compositor_frames_) {
pair.reporter->TerminateFrame(FrameTerminationStatus::kDidNotPresentFrame,
Now());
}
predictor_jank_tracker_->set_scroll_jank_ukm_reporter(nullptr);
scroll_jank_dropped_frame_tracker_->set_scroll_jank_ukm_reporter(nullptr);
if (global_trackers_.frame_sorter) {
if (global_trackers_.frame_sequence_trackers) {
global_trackers_.frame_sorter->RemoveObserver(
global_trackers_.frame_sequence_trackers);
}
}
}
void CompositorFrameReportingController::SetVisible(bool visible) {
if (visible_ == visible) {
return;
}
visible_ = visible;
if (visible_) {
waiting_for_did_present_after_visible_ = true;
}
}
CompositorFrameReportingController::SubmittedCompositorFrame::
SubmittedCompositorFrame() = default;
CompositorFrameReportingController::SubmittedCompositorFrame::
SubmittedCompositorFrame(uint32_t frame_token,
std::unique_ptr<CompositorFrameReporter> reporter)
: frame_token(frame_token), reporter(std::move(reporter)) {}
CompositorFrameReportingController::SubmittedCompositorFrame::
~SubmittedCompositorFrame() = default;
CompositorFrameReportingController::SubmittedCompositorFrame::
SubmittedCompositorFrame(SubmittedCompositorFrame&& other) = default;
base::TimeTicks CompositorFrameReportingController::Now() const {
return tick_clock_->NowTicks();
}
bool CompositorFrameReportingController::HasReporterAt(
PipelineStage stage) const {
return !!reporters_[stage].get();
}
void CompositorFrameReportingController::ProcessSkippedFramesIfNecessary(
const viz::BeginFrameArgs& args) {
const auto& previous_frame = last_started_compositor_frame_.args;
if (previous_frame.IsValid() &&
previous_frame.frame_id.source_id == args.frame_id.source_id) {
CreateReportersForDroppedFrames(previous_frame, args);
}
FrameSequenceTrackerCollection* trackers =
global_trackers_.frame_sequence_trackers;
last_started_compositor_frame_.args = args;
if (trackers) {
last_started_compositor_frame_.scrolling_thread =
trackers->GetScrollingThread();
last_started_compositor_frame_.active_trackers =
trackers->GetActiveTrackers();
last_started_compositor_frame_.smooth_thread = trackers->GetSmoothThread();
}
}
void CompositorFrameReportingController::WillBeginImplFrame(
const viz::BeginFrameArgs& args,
bool will_throttle_main) {
ProcessSkippedFramesIfNecessary(args);
base::TimeTicks begin_time = Now();
if (reporters_[PipelineStage::kBeginImplFrame]) {
auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
DCHECK(reporter->did_finish_impl_frame());
if (reporter->did_not_produce_frame()) {
reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
reporter->did_not_produce_frame_time());
} else {
reporter->TerminateFrame(FrameTerminationStatus::kReplacedByNewReporter,
Now());
}
}
FrameSequenceTrackerCollection* trackers =
global_trackers_.frame_sequence_trackers;
ActiveTrackers active_trackers;
FrameInfo::SmoothEffectDrivingThread scrolling_thread =
FrameInfo::SmoothEffectDrivingThread::kUnknown;
FrameInfo::SmoothThread smooth_thread = FrameInfo::SmoothThread::kSmoothNone;
if (trackers) {
active_trackers = trackers->GetActiveTrackers();
scrolling_thread = trackers->GetScrollingThread();
smooth_thread = trackers->GetSmoothThread();
}
auto reporter = std::make_unique<CompositorFrameReporter>(
active_trackers, args, should_report_histograms_, smooth_thread,
scrolling_thread, layer_tree_host_id_, global_trackers_);
reporter->set_tick_clock(tick_clock_);
reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
begin_time);
reporter->set_want_new_tree(needs_raster_properties_animated_);
reporter->set_will_throttle_main(will_throttle_main);
reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter);
}
void CompositorFrameReportingController::WillBeginMainFrame(
const viz::BeginFrameArgs& args) {
if (reporters_[PipelineStage::kBeginImplFrame]) {
DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(),
reporters_[PipelineStage::kBeginImplFrame].get());
DCHECK_EQ(reporters_[PipelineStage::kBeginImplFrame]->frame_id(),
args.frame_id);
reporters_[PipelineStage::kBeginImplFrame]->StartStage(
StageType::kSendBeginMainFrameToCommit, Now());
AdvanceReporterStage(PipelineStage::kBeginImplFrame,
PipelineStage::kBeginMainFrame);
} else {
FrameSequenceTrackerCollection* trackers =
global_trackers_.frame_sequence_trackers;
ActiveTrackers active_trackers;
FrameInfo::SmoothEffectDrivingThread scrolling_thread =
FrameInfo::SmoothEffectDrivingThread::kUnknown;
FrameInfo::SmoothThread smooth_thread =
FrameInfo::SmoothThread::kSmoothNone;
if (args.frame_id == last_started_compositor_frame_.args.frame_id) {
scrolling_thread = last_started_compositor_frame_.scrolling_thread;
active_trackers = last_started_compositor_frame_.active_trackers;
smooth_thread = last_started_compositor_frame_.smooth_thread;
} else if (trackers) {
active_trackers = trackers->GetActiveTrackers();
scrolling_thread = trackers->GetScrollingThread();
smooth_thread = trackers->GetSmoothThread();
}
auto reporter = std::make_unique<CompositorFrameReporter>(
active_trackers, args, should_report_histograms_, smooth_thread,
scrolling_thread, layer_tree_host_id_, global_trackers_);
reporter->set_tick_clock(tick_clock_);
reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter);
}
}
void CompositorFrameReportingController::BeginMainFrameAborted(
const viz::BeginFrameId& id,
CommitEarlyOutReason reason) {
auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
DCHECK(reporter);
DCHECK_EQ(reporter->frame_id(), id);
reporter->OnAbortBeginMainFrame(Now());
if (reason == CommitEarlyOutReason::kFinishedNoUpdates) {
DidNotProduceFrame(id, FrameSkippedReason::kNoDamage);
}
}
void CompositorFrameReportingController::WillCommit() {
DCHECK(reporters_[PipelineStage::kReadyToCommit]);
reporters_[PipelineStage::kReadyToCommit]->StartStage(StageType::kCommit,
Now());
}
void CompositorFrameReportingController::DidCommit() {
DCHECK(reporters_[PipelineStage::kReadyToCommit]);
reporters_[PipelineStage::kReadyToCommit]->StartStage(
StageType::kEndCommitToActivation, Now());
AdvanceReporterStage(PipelineStage::kReadyToCommit, PipelineStage::kCommit);
}
void CompositorFrameReportingController::WillInvalidateOnImplSide() {
next_activate_has_invalidation_ = true;
}
void CompositorFrameReportingController::WillActivate() {
DCHECK(reporters_[PipelineStage::kCommit] || next_activate_has_invalidation_);
if (!reporters_[PipelineStage::kCommit])
return;
reporters_[PipelineStage::kCommit]->StartStage(StageType::kActivation, Now());
}
void CompositorFrameReportingController::DidActivate() {
DCHECK(reporters_[PipelineStage::kCommit] || next_activate_has_invalidation_);
next_activate_has_invalidation_ = false;
if (!reporters_[PipelineStage::kCommit])
return;
if (is_trees_in_viz_client_) {
reporters_[PipelineStage::kCommit]->StartStage(
StageType::kEndActivateToSubmitUpdateDisplayTree, Now());
} else {
reporters_[PipelineStage::kCommit]->StartStage(
StageType::kEndActivateToSubmitCompositorFrame, Now());
}
AdvanceReporterStage(PipelineStage::kCommit, PipelineStage::kActivate);
}
void CompositorFrameReportingController::DidSubmitCompositorFrame(
SubmitInfo& submit_info,
const viz::BeginFrameId& current_frame_id,
const viz::BeginFrameId& last_activated_frame_id) {
bool is_activated_frame_new =
(last_activated_frame_id != last_submitted_frame_id_);
uint64_t active_tree_staleness = current_frame_id.sequence_number -
last_activated_frame_id.sequence_number;
std::unique_ptr<CompositorFrameReporter> main_reporter;
std::unique_ptr<CompositorFrameReporter> impl_reporter;
if (is_activated_frame_new) {
DCHECK_EQ(reporters_[PipelineStage::kActivate]->frame_id(),
last_activated_frame_id);
main_reporter = std::move(reporters_[PipelineStage::kActivate]);
last_submitted_frame_id_ = last_activated_frame_id;
} else if (current_frame_id.source_id ==
viz::BeginFrameArgs::kManualSourceId &&
reporters_[PipelineStage::kActivate]) {
main_reporter = std::move(reporters_[PipelineStage::kActivate]);
last_submitted_frame_id_ = last_activated_frame_id;
} else {
DCHECK(!reporters_[PipelineStage::kActivate]);
}
if (CanSubmitImplFrame(current_frame_id)) {
auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
if (is_trees_in_viz_client_) {
reporter->StartStageUpdateDisplayTree(submit_info);
} else {
reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
reporter->impl_frame_finish_time());
}
AdvanceReporterStage(PipelineStage::kBeginImplFrame,
PipelineStage::kActivate);
impl_reporter = std::move(reporters_[PipelineStage::kActivate]);
CompositorFrameReporter* partial_update_decider =
GetOutstandingUpdatesFromMain(current_frame_id);
if (partial_update_decider)
impl_reporter->SetPartialUpdateDecider(partial_update_decider);
} else if (CanSubmitMainFrame(current_frame_id)) {
auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
if (is_trees_in_viz_client_) {
reporter->StartStageUpdateDisplayTree(submit_info);
} else {
reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
reporter->impl_frame_finish_time());
}
AdvanceReporterStage(PipelineStage::kBeginMainFrame,
PipelineStage::kActivate);
impl_reporter = std::move(reporters_[PipelineStage::kActivate]);
} else {
auto reporter = RestoreReporterAtBeginImpl(current_frame_id);
if (reporter) {
if (is_trees_in_viz_client_) {
reporter->StartStageUpdateDisplayTree(submit_info);
} else {
reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
reporter->impl_frame_finish_time());
}
impl_reporter = std::move(reporter);
}
}
#if DCHECK_IS_ON()
if (!submit_info.events_metrics.main_event_metrics.empty()) {
DCHECK(main_reporter);
}
if (impl_reporter) {
DCHECK_EQ(impl_reporter->frame_id(), current_frame_id);
if (main_reporter) {
DCHECK_NE(main_reporter->frame_id(), current_frame_id);
}
}
#endif
if (!impl_reporter &&
!submit_info.events_metrics.impl_event_metrics.empty()) {
DCHECK(main_reporter);
DCHECK_EQ(main_reporter->frame_id(), current_frame_id);
submit_info.events_metrics.main_event_metrics.reserve(
submit_info.events_metrics.main_event_metrics.size() +
submit_info.events_metrics.impl_event_metrics.size() +
submit_info.events_metrics.raster_event_metrics.size());
submit_info.events_metrics.main_event_metrics.insert(
submit_info.events_metrics.main_event_metrics.end(),
std::make_move_iterator(
submit_info.events_metrics.impl_event_metrics.begin()),
std::make_move_iterator(
submit_info.events_metrics.impl_event_metrics.end()));
submit_info.events_metrics.main_event_metrics.insert(
submit_info.events_metrics.main_event_metrics.end(),
std::make_move_iterator(
submit_info.events_metrics.raster_event_metrics.begin()),
std::make_move_iterator(
submit_info.events_metrics.raster_event_metrics.end()));
}
if (!impl_reporter && submit_info.normalized_invalidated_area) {
DCHECK(main_reporter);
DCHECK_EQ(main_reporter->frame_id(), current_frame_id);
main_reporter->set_normalized_invalidated_area(
submit_info.normalized_invalidated_area);
}
if (main_reporter) {
if (is_trees_in_viz_client_) {
main_reporter->StartStagePresentationCompositorFrame(submit_info);
} else {
main_reporter->StartStage(
StageType::kSubmitCompositorFrameToPresentationCompositorFrame,
submit_info.time);
}
main_reporter->AddEventsMetrics(
std::move(submit_info.events_metrics.main_event_metrics));
main_reporter->set_checkerboarded_needs_raster(
submit_info.checkerboarded_needs_raster);
main_reporter->set_checkerboarded_needs_record(
submit_info.checkerboarded_needs_record);
main_reporter->set_reporter_type_to_main();
main_reporter->set_top_controls_moved(submit_info.top_controls_moved);
submitted_compositor_frames_.emplace_back(submit_info.frame_token,
std::move(main_reporter));
}
if (impl_reporter) {
impl_reporter->EnableCompositorOnlyReporting();
if (is_trees_in_viz_client_) {
impl_reporter->StartStagePresentationCompositorFrame(submit_info);
} else {
impl_reporter->StartStage(
StageType::kSubmitCompositorFrameToPresentationCompositorFrame,
submit_info.time);
}
impl_reporter->AddEventsMetrics(
std::move(submit_info.events_metrics.impl_event_metrics));
impl_reporter->AddEventsMetrics(
std::move(submit_info.events_metrics.raster_event_metrics));
impl_reporter->set_checkerboarded_needs_raster(
submit_info.checkerboarded_needs_raster);
impl_reporter->set_checkerboarded_needs_record(
submit_info.checkerboarded_needs_record);
impl_reporter->set_is_accompanied_by_main_thread_update(
is_activated_frame_new);
impl_reporter->set_active_tree_staleness(active_tree_staleness);
impl_reporter->set_reporter_type_to_impl();
impl_reporter->set_top_controls_moved(submit_info.top_controls_moved);
impl_reporter->set_created_new_tree(submit_info.drawn_with_new_layer_tree);
impl_reporter->set_normalized_invalidated_area(
submit_info.normalized_invalidated_area);
impl_reporter->set_invalidate_raster_scroll(
submit_info.invalidate_raster_scroll);
submitted_compositor_frames_.emplace_back(submit_info.frame_token,
std::move(impl_reporter));
}
}
void CompositorFrameReportingController::DidNotProduceFrame(
const viz::BeginFrameId& id,
FrameSkippedReason skip_reason) {
for (auto& stage_reporter : reporters_) {
if (stage_reporter && stage_reporter->frame_id() == id) {
stage_reporter->OnDidNotProduceFrame(skip_reason);
if (skip_reason == FrameSkippedReason::kWaitingOnMain)
SetPartialUpdateDeciderWhenWaitingOnMain(stage_reporter);
break;
}
}
}
void CompositorFrameReportingController::
SetPartialUpdateDeciderWhenWaitingOnMain(
std::unique_ptr<CompositorFrameReporter>& stage_reporter) {
auto reporter = RestoreReporterAtBeginImpl(stage_reporter->frame_id());
if (reporter) {
reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain);
reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
Now());
stage_reporter->AdoptReporter(std::move(reporter));
} else {
CompositorFrameReporter* partial_update_decider =
GetOutstandingUpdatesFromMain(stage_reporter->frame_id());
if (partial_update_decider) {
stage_reporter->SetPartialUpdateDecider(partial_update_decider);
stage_reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain);
stage_reporter->TerminateFrame(
FrameTerminationStatus::kDidNotProduceFrame, Now());
partial_update_decider->AdoptReporter(std::move(stage_reporter));
}
}
}
void CompositorFrameReportingController::OnFinishImplFrame(
const viz::BeginFrameId& id,
bool waiting_for_main) {
for (auto& reporter : reporters_) {
if (reporter && reporter->frame_id() == id) {
reporter->OnFinishImplFrame(Now(), waiting_for_main);
return;
}
}
}
void CompositorFrameReportingController::MaybePassEventMetricsFromDroppedFrames(
CompositorFrameReporter& reporter,
uint32_t frame_token,
bool next_reporter_from_same_frame) {
if ((reporter.get_reporter_type() ==
CompositorFrameReporter::ReporterType::kMain &&
!next_reporter_from_same_frame) ||
(reporter.get_reporter_type() ==
CompositorFrameReporter::ReporterType::kImpl)) {
for (auto it = events_metrics_from_dropped_frames_.begin();
it != events_metrics_from_dropped_frames_.end() &&
frame_token > it->first;
it = events_metrics_from_dropped_frames_.erase(it)) {
reporter.AddEventsMetrics(std::move(it->second.main_event_metrics));
reporter.AddEventsMetrics(std::move(it->second.impl_event_metrics));
reporter.AddEventsMetrics(std::move(it->second.raster_event_metrics));
}
} else {
for (auto it = events_metrics_from_dropped_frames_.begin();
it != events_metrics_from_dropped_frames_.end() &&
frame_token > it->first;
it++) {
reporter.AddEventsMetrics(std::move(it->second.main_event_metrics));
}
}
}
void CompositorFrameReportingController::StoreEventMetricsFromDroppedFrames(
CompositorFrameReporter& reporter,
uint32_t frame_token) {
auto main_reporter_events_metrics = reporter.TakeMainBlockedEventsMetrics();
auto remaining_reporter_events_metrics = reporter.TakeEventsMetrics();
EventMetricsSet& frame_events_metrics =
events_metrics_from_dropped_frames_[frame_token];
switch (reporter.get_reporter_type()) {
case CompositorFrameReporter::ReporterType::kImpl:
frame_events_metrics.main_event_metrics.insert(
frame_events_metrics.main_event_metrics.end(),
std::make_move_iterator(main_reporter_events_metrics.begin()),
std::make_move_iterator(main_reporter_events_metrics.end()));
frame_events_metrics.impl_event_metrics.insert(
frame_events_metrics.impl_event_metrics.end(),
std::make_move_iterator(remaining_reporter_events_metrics.begin()),
std::make_move_iterator(remaining_reporter_events_metrics.end()));
break;
case CompositorFrameReporter::ReporterType::kMain:
frame_events_metrics.main_event_metrics.insert(
frame_events_metrics.main_event_metrics.end(),
std::make_move_iterator(main_reporter_events_metrics.begin()),
std::make_move_iterator(main_reporter_events_metrics.end()));
frame_events_metrics.main_event_metrics.insert(
frame_events_metrics.main_event_metrics.end(),
std::make_move_iterator(remaining_reporter_events_metrics.begin()),
std::make_move_iterator(remaining_reporter_events_metrics.end()));
break;
}
}
void CompositorFrameReportingController::DidPresentCompositorFrame(
uint32_t frame_token,
const viz::FrameTimingDetails& details) {
bool feedback_failed = details.presentation_feedback.failed();
for (auto submitted_frame = submitted_compositor_frames_.begin();
submitted_frame != submitted_compositor_frames_.end() &&
!viz::FrameTokenGT(submitted_frame->frame_token, frame_token);) {
bool is_earlier_frame = submitted_frame->frame_token != frame_token;
if (feedback_failed && is_earlier_frame) {
submitted_frame++;
continue;
}
auto termination_status = feedback_failed
? FrameTerminationStatus::kDidNotPresentFrame
: FrameTerminationStatus::kPresentedFrame;
if (is_earlier_frame)
termination_status = FrameTerminationStatus::kDidNotPresentFrame;
auto& reporter = submitted_frame->reporter;
reporter->SetVizBreakdown(details);
reporter->TerminateFrame(termination_status,
details.presentation_feedback.timestamp);
if (waiting_for_did_present_after_visible_) {
waiting_for_did_present_after_visible_ = false;
for (auto it = events_metrics_from_dropped_frames_.begin();
it != events_metrics_from_dropped_frames_.end() &&
submitted_frame->frame_token > it->first;
it = events_metrics_from_dropped_frames_.erase(it)) {
}
}
if (termination_status == FrameTerminationStatus::kPresentedFrame) {
CompositorFrameReporter* reporter_ptr = reporter.get();
if (CompositorFrameReporter* orig_reporter =
reporter->partial_update_decider()) {
orig_reporter->AdoptReporter(std::move(reporter));
}
auto next = submitted_frame + 1;
bool next_reporter_from_same_frame =
next != submitted_compositor_frames_.end() &&
submitted_frame->frame_token == next->frame_token;
MaybePassEventMetricsFromDroppedFrames(*reporter_ptr,
submitted_frame->frame_token,
next_reporter_from_same_frame);
reporter_ptr->DidSuccessfullyPresentFrame();
} else {
StoreEventMetricsFromDroppedFrames(*reporter,
submitted_frame->frame_token);
}
if (feedback_failed) {
submitted_frame = submitted_compositor_frames_.erase(submitted_frame);
} else {
DCHECK_EQ(submitted_frame->frame_token,
submitted_compositor_frames_.front().frame_token);
submitted_compositor_frames_.pop_front();
submitted_frame = submitted_compositor_frames_.begin();
}
}
}
void CompositorFrameReportingController::OnStoppedRequestingBeginFrames() {
auto now = Now();
for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) {
if (reporters_[i]) {
reporters_[i]->OnDidNotProduceFrame(FrameSkippedReason::kNoDamage);
reporters_[i]->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
now);
}
}
last_started_compositor_frame_ = {};
}
void CompositorFrameReportingController::NotifyReadyToCommit(
std::unique_ptr<BeginMainFrameMetrics> details) {
DCHECK(reporters_[PipelineStage::kBeginMainFrame]);
reporters_[PipelineStage::kBeginMainFrame]->SetBlinkBreakdown(
std::move(details), begin_main_frame_start_time_);
AdvanceReporterStage(PipelineStage::kBeginMainFrame,
PipelineStage::kReadyToCommit);
}
void CompositorFrameReportingController::AdvanceReporterStage(
PipelineStage start,
PipelineStage target) {
auto& reporter = reporters_[target];
if (reporter) {
auto termination_status = FrameTerminationStatus::kReplacedByNewReporter;
base::TimeTicks termination_time;
if (reporter->did_not_produce_frame()) {
termination_time = reporter->did_not_produce_frame_time();
termination_status = FrameTerminationStatus::kDidNotProduceFrame;
} else if (target == PipelineStage::kBeginMainFrame &&
reporter->did_abort_main_frame()) {
termination_time = reporter->main_frame_abort_time();
} else {
termination_time = Now();
}
reporter->TerminateFrame(termination_status, termination_time);
}
reporters_[target] = std::move(reporters_[start]);
}
bool CompositorFrameReportingController::CanSubmitImplFrame(
const viz::BeginFrameId& id) const {
#if DCHECK_IS_ON()
auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
if (reporter) {
DCHECK_EQ(reporter->frame_id(), id);
DCHECK(reporter->did_finish_impl_frame());
}
#endif
return reporters_[PipelineStage::kBeginImplFrame].get() != nullptr;
}
bool CompositorFrameReportingController::CanSubmitMainFrame(
const viz::BeginFrameId& id) const {
if (!reporters_[PipelineStage::kBeginMainFrame])
return false;
auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
return (reporter->frame_id() == id && reporter->did_finish_impl_frame() &&
reporter->did_abort_main_frame());
}
std::unique_ptr<CompositorFrameReporter>
CompositorFrameReportingController::RestoreReporterAtBeginImpl(
const viz::BeginFrameId& id) {
auto& main_reporter = reporters_[PipelineStage::kBeginMainFrame];
auto& ready_to_commit_reporter = reporters_[PipelineStage::kReadyToCommit];
auto& commit_reporter = reporters_[PipelineStage::kCommit];
if (main_reporter && main_reporter->frame_id() == id) {
DCHECK(!ready_to_commit_reporter ||
ready_to_commit_reporter->frame_id() != id);
DCHECK(!commit_reporter || commit_reporter->frame_id() != id);
return main_reporter->CopyReporterAtBeginImplStage();
}
if (ready_to_commit_reporter && ready_to_commit_reporter->frame_id() == id) {
DCHECK(!commit_reporter || commit_reporter->frame_id() != id);
return ready_to_commit_reporter->CopyReporterAtBeginImplStage();
}
if (commit_reporter && commit_reporter->frame_id() == id)
return commit_reporter->CopyReporterAtBeginImplStage();
return nullptr;
}
void CompositorFrameReportingController::InitializeUkmManager(
std::unique_ptr<ukm::UkmRecorder> recorder) {
latency_ukm_reporter_->InitializeUkmManager(std::move(recorder));
scroll_jank_ukm_reporter_->set_ukm_manager(
latency_ukm_reporter_->ukm_manager());
}
void CompositorFrameReportingController::SetSourceId(ukm::SourceId source_id) {
latency_ukm_reporter_->SetSourceId(source_id);
}
CompositorFrameReporter*
CompositorFrameReportingController::GetOutstandingUpdatesFromMain(
const viz::BeginFrameId& id) const {
{
const auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
if (reporter && reporter->frame_id() < id &&
!reporter->did_abort_main_frame()) {
return reporter.get();
}
}
{
const auto& reporter = reporters_[PipelineStage::kReadyToCommit];
if (reporter && reporter->frame_id() < id &&
!reporter->did_abort_main_frame()) {
return reporter.get();
}
}
{
const auto& reporter = reporters_[PipelineStage::kCommit];
if (reporter && reporter->frame_id() < id) {
DCHECK(!reporter->did_abort_main_frame());
return reporter.get();
}
}
return nullptr;
}
void CompositorFrameReportingController::CreateReportersForDroppedFrames(
const viz::BeginFrameArgs& old_args,
const viz::BeginFrameArgs& new_args) {
DCHECK_EQ(new_args.frame_id.source_id, old_args.frame_id.source_id);
DCHECK_GE(
new_args.frame_id.sequence_number - new_args.frames_throttled_since_last,
old_args.frame_id.sequence_number);
const uint32_t interval = new_args.frame_id.sequence_number -
old_args.frame_id.sequence_number -
new_args.frames_throttled_since_last;
const uint32_t kMaxFrameCount = 100;
if (interval > kMaxFrameCount) {
return;
}
std::optional<FrameSkippedReason> skipped_reason;
for (auto& stage_reporter : reporters_) {
if (stage_reporter && stage_reporter->frame_id() == old_args.frame_id) {
bool main_not_expected = !stage_reporter->will_throttle_main() &&
!stage_reporter->waiting_for_main();
bool waiting_on_main = stage_reporter->has_frame_skip_reason() &&
stage_reporter->frame_skip_reason() ==
FrameSkippedReason::kWaitingOnMain;
if (main_not_expected && waiting_on_main) {
skipped_reason = FrameSkippedReason::kNoDamage;
} else if (stage_reporter->has_frame_skip_reason()) {
skipped_reason = stage_reporter->frame_skip_reason();
}
break;
}
}
auto timestamp = old_args.frame_time + old_args.interval;
FrameSequenceTrackerCollection* trackers =
global_trackers_.frame_sequence_trackers;
ActiveTrackers active_trackers;
FrameInfo::SmoothEffectDrivingThread scrolling_thread =
FrameInfo::SmoothEffectDrivingThread::kUnknown;
FrameInfo::SmoothThread smooth_thread = FrameInfo::SmoothThread::kSmoothNone;
for (uint32_t i = 1; i < interval; ++i, timestamp += old_args.interval) {
auto args = viz::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, old_args.frame_id.source_id,
old_args.frame_id.sequence_number + i, timestamp,
timestamp + old_args.interval, old_args.interval,
viz::BeginFrameArgs::NORMAL);
devtools_instrumentation::DidBeginFrame(
layer_tree_host_id_, args.frame_time, args.frame_id.sequence_number);
if (trackers) {
active_trackers = trackers->GetActiveTrackers();
scrolling_thread = trackers->GetScrollThreadAtTime(timestamp);
smooth_thread = trackers->GetSmoothThreadAtTime(timestamp);
}
auto reporter = std::make_unique<CompositorFrameReporter>(
active_trackers, args, should_report_histograms_, smooth_thread,
scrolling_thread, layer_tree_host_id_, global_trackers_);
reporter->set_tick_clock(tick_clock_);
reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
timestamp);
reporter->TerminateFrame(FrameTerminationStatus::kDidNotPresentFrame,
args.deadline);
reporter->set_is_backfill(true);
reporter->set_frame_skipped_reason_v4(skipped_reason);
}
}
}