1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/metrics/compositor_frame_reporting_controller.h"
9 #include "base/trace_event/trace_event.h"
10 #include "base/trace_event/trace_id_helper.h"
11 #include "cc/metrics/compositor_frame_reporter.h"
12 #include "cc/metrics/dropped_frame_counter.h"
13 #include "cc/metrics/frame_sequence_tracker_collection.h"
14 #include "cc/metrics/latency_ukm_reporter.h"
15 #include "components/viz/common/frame_timing_details.h"
16 #include "components/viz/common/quads/compositor_frame_metadata.h"
17 #include "services/tracing/public/cpp/perfetto/macros.h"
21 using SmoothThread = CompositorFrameReporter::SmoothThread;
22 using StageType = CompositorFrameReporter::StageType;
23 using FrameTerminationStatus = CompositorFrameReporter::FrameTerminationStatus;
25 constexpr char kTraceCategory[] = "cc,benchmark";
26 constexpr int kNumOfCompositorStages =
27 static_cast<int>(StageType::kStageTypeCount) - 1;
28 constexpr int kNumDispatchStages =
29 static_cast<int>(EventMetrics::DispatchStage::kMaxValue);
30 constexpr base::TimeDelta kDefaultLatencyPredictionDeviationThreshold =
31 viz::BeginFrameArgs::DefaultInterval() / 2;
34 CompositorFrameReportingController::CompositorFrameReportingController(
35 bool should_report_histograms,
36 bool should_report_ukm,
37 int layer_tree_host_id)
38 : should_report_histograms_(should_report_histograms),
39 layer_tree_host_id_(layer_tree_host_id),
40 latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()),
41 previous_latency_predictions_main_(base::Microseconds(-1)),
42 previous_latency_predictions_impl_(base::Microseconds(-1)),
43 event_latency_predictions_(
44 CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages,
45 kNumOfCompositorStages)) {
46 if (should_report_ukm) {
47 // UKM metrics should be reported if and only if `latency_ukm_reporter` is
48 // set on `global_trackers_`.
49 global_trackers_.latency_ukm_reporter = latency_ukm_reporter_.get();
53 CompositorFrameReportingController::~CompositorFrameReportingController() {
54 base::TimeTicks now = Now();
55 for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) {
57 reporters_[i]->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
61 for (auto& pair : submitted_compositor_frames_) {
62 pair.reporter->TerminateFrame(FrameTerminationStatus::kDidNotPresentFrame,
67 CompositorFrameReportingController::SubmittedCompositorFrame::
68 SubmittedCompositorFrame() = default;
69 CompositorFrameReportingController::SubmittedCompositorFrame::
70 SubmittedCompositorFrame(uint32_t frame_token,
71 std::unique_ptr<CompositorFrameReporter> reporter)
72 : frame_token(frame_token), reporter(std::move(reporter)) {}
73 CompositorFrameReportingController::SubmittedCompositorFrame::
74 ~SubmittedCompositorFrame() = default;
76 CompositorFrameReportingController::SubmittedCompositorFrame::
77 SubmittedCompositorFrame(SubmittedCompositorFrame&& other) = default;
79 base::TimeTicks CompositorFrameReportingController::Now() const {
80 return tick_clock_->NowTicks();
83 bool CompositorFrameReportingController::HasReporterAt(
84 PipelineStage stage) const {
85 return !!reporters_[stage].get();
88 void CompositorFrameReportingController::ProcessSkippedFramesIfNecessary(
89 const viz::BeginFrameArgs& args) {
90 const auto& previous_frame = last_started_compositor_frame_.args;
91 if (previous_frame.IsValid() &&
92 previous_frame.frame_id.source_id == args.frame_id.source_id) {
93 CreateReportersForDroppedFrames(previous_frame, args);
96 last_started_compositor_frame_.args = args;
97 last_started_compositor_frame_.scrolling_thread = scrolling_thread_;
98 last_started_compositor_frame_.active_trackers = active_trackers_;
99 last_started_compositor_frame_.smooth_thread = GetSmoothThread();
102 void CompositorFrameReportingController::WillBeginImplFrame(
103 const viz::BeginFrameArgs& args) {
104 ProcessSkippedFramesIfNecessary(args);
105 ReportMultipleSwaps(args.frame_time, last_interval_);
106 last_interval_ = args.interval;
108 base::TimeTicks begin_time = Now();
109 if (reporters_[PipelineStage::kBeginImplFrame]) {
110 auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
111 DCHECK(reporter->did_finish_impl_frame());
112 // TODO(1144353): This is a speculative fix. This code should only be
113 // reached after the previous frame have been explicitly marked as 'did not
114 // produce frame', i.e. this code should have a DCHECK instead of a
116 // DCHECK(reporter->did_not_produce_frame()).
117 if (reporter->did_not_produce_frame()) {
118 reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
119 reporter->did_not_produce_frame_time());
121 reporter->TerminateFrame(FrameTerminationStatus::kReplacedByNewReporter,
125 auto reporter = std::make_unique<CompositorFrameReporter>(
126 active_trackers_, args, should_report_histograms_, GetSmoothThread(),
127 scrolling_thread_, layer_tree_host_id_, global_trackers_);
128 reporter->set_tick_clock(tick_clock_);
129 reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
131 reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter);
134 void CompositorFrameReportingController::WillBeginMainFrame(
135 const viz::BeginFrameArgs& args) {
136 if (reporters_[PipelineStage::kBeginImplFrame]) {
137 // We need to use .get() below because operator<< in std::unique_ptr is a
139 DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(),
140 reporters_[PipelineStage::kBeginImplFrame].get());
141 DCHECK_EQ(reporters_[PipelineStage::kBeginImplFrame]->frame_id(),
143 reporters_[PipelineStage::kBeginImplFrame]->StartStage(
144 StageType::kSendBeginMainFrameToCommit, Now());
145 AdvanceReporterStage(PipelineStage::kBeginImplFrame,
146 PipelineStage::kBeginMainFrame);
148 // In this case we have already submitted the ImplFrame, but we received
149 // beginMain frame before next BeginImplFrame (Not reached the ImplFrame
150 // deadline yet). So will start a new reporter at BeginMainFrame, and use
151 // the state(s) from the ImplFrame where necessary.
152 auto scrolling_thread = scrolling_thread_;
153 auto active_trackers = active_trackers_;
154 auto smooth_thread = GetSmoothThread();
155 if (args.frame_id == last_started_compositor_frame_.args.frame_id) {
156 // TODO(1277547): Instead of replacing all current information with the
157 // older information from when the impl-frame started, merge the two sets
158 // of information that makes sense.
159 scrolling_thread = last_started_compositor_frame_.scrolling_thread;
160 active_trackers = last_started_compositor_frame_.active_trackers;
161 smooth_thread = last_started_compositor_frame_.smooth_thread;
163 auto reporter = std::make_unique<CompositorFrameReporter>(
164 active_trackers, args, should_report_histograms_, smooth_thread,
165 scrolling_thread, layer_tree_host_id_, global_trackers_);
166 reporter->set_tick_clock(tick_clock_);
167 reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
168 reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter);
172 void CompositorFrameReportingController::BeginMainFrameAborted(
173 const viz::BeginFrameId& id,
174 CommitEarlyOutReason reason) {
175 auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
177 DCHECK_EQ(reporter->frame_id(), id);
178 reporter->OnAbortBeginMainFrame(Now());
180 if (reason == CommitEarlyOutReason::FINISHED_NO_UPDATES)
181 DidNotProduceFrame(id, FrameSkippedReason::kNoDamage);
184 void CompositorFrameReportingController::WillCommit() {
185 DCHECK(reporters_[PipelineStage::kReadyToCommit]);
186 reporters_[PipelineStage::kReadyToCommit]->StartStage(StageType::kCommit,
190 void CompositorFrameReportingController::DidCommit() {
191 DCHECK(reporters_[PipelineStage::kReadyToCommit]);
192 reporters_[PipelineStage::kReadyToCommit]->StartStage(
193 StageType::kEndCommitToActivation, Now());
194 AdvanceReporterStage(PipelineStage::kReadyToCommit, PipelineStage::kCommit);
197 void CompositorFrameReportingController::WillInvalidateOnImplSide() {
198 // Allows for activation without committing.
199 // TODO(alsan): Report latency of impl side invalidations.
200 next_activate_has_invalidation_ = true;
203 void CompositorFrameReportingController::WillActivate() {
204 DCHECK(reporters_[PipelineStage::kCommit] || next_activate_has_invalidation_);
205 if (!reporters_[PipelineStage::kCommit])
207 reporters_[PipelineStage::kCommit]->StartStage(StageType::kActivation, Now());
210 void CompositorFrameReportingController::DidActivate() {
211 DCHECK(reporters_[PipelineStage::kCommit] || next_activate_has_invalidation_);
212 next_activate_has_invalidation_ = false;
213 if (!reporters_[PipelineStage::kCommit])
215 reporters_[PipelineStage::kCommit]->StartStage(
216 StageType::kEndActivateToSubmitCompositorFrame, Now());
217 AdvanceReporterStage(PipelineStage::kCommit, PipelineStage::kActivate);
220 void CompositorFrameReportingController::DidSubmitCompositorFrame(
221 uint32_t frame_token,
222 base::TimeTicks submit_time,
223 const viz::BeginFrameId& current_frame_id,
224 const viz::BeginFrameId& last_activated_frame_id,
225 EventMetricsSet events_metrics,
226 bool has_missing_content) {
227 bool is_activated_frame_new =
228 (last_activated_frame_id != last_submitted_frame_id_);
230 // It is possible to submit a CompositorFrame containing outputs from two
231 // different begin-frames: an begin-main-frame that was blocked on the
232 // main-thread, and another one for the compositor thread.
233 std::unique_ptr<CompositorFrameReporter> main_reporter;
234 std::unique_ptr<CompositorFrameReporter> impl_reporter;
236 // If |is_activated_frame_new| is true, |main_reporter| is guaranteed to
237 // be set, and |impl_reporter| may or may not be set; otherwise,
238 // |impl_reporter| is guaranteed to be set, and |main_reporter| will not be
240 if (is_activated_frame_new) {
241 DCHECK_EQ(reporters_[PipelineStage::kActivate]->frame_id(),
242 last_activated_frame_id);
243 // The reporter in activate state can be submitted
244 main_reporter = std::move(reporters_[PipelineStage::kActivate]);
245 last_submitted_frame_id_ = last_activated_frame_id;
247 DCHECK(!reporters_[PipelineStage::kActivate]);
250 // |main_reporter| can be for a previous BeginFrameArgs (i.e. not for
251 // |current_frame_id|), in which case it is necessary to also report metrics
252 // for the reporter representing |current_frame_id|. Following are the
254 // 1) the main-thread did not request any updates (i.e. a 'begin main frame'
255 // was not issued). The reporter for |current_frame_id| should still be in
256 // the 'impl frame' stage.
257 // 2) the 'begin main frame' was issued, but the main-thread did not have any
258 // updates (i.e. the 'begin main frame' was aborted). The reporter for
259 // |current_frame_id| should be in the 'main frame' stage, and it will
260 // have been aborted.
261 // 3) main-thread is still processing 'begin main frame'. The reporter for
262 // |current_frame_id| should be in either the 'main frame' or 'commit'
264 if (CanSubmitImplFrame(current_frame_id)) {
265 auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
266 reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
267 reporter->impl_frame_finish_time());
268 AdvanceReporterStage(PipelineStage::kBeginImplFrame,
269 PipelineStage::kActivate);
270 impl_reporter = std::move(reporters_[PipelineStage::kActivate]);
271 CompositorFrameReporter* partial_update_decider =
272 GetOutstandingUpdatesFromMain(current_frame_id);
273 if (partial_update_decider)
274 impl_reporter->SetPartialUpdateDecider(partial_update_decider);
275 } else if (CanSubmitMainFrame(current_frame_id)) {
276 auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
277 reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
278 reporter->impl_frame_finish_time());
279 AdvanceReporterStage(PipelineStage::kBeginMainFrame,
280 PipelineStage::kActivate);
281 impl_reporter = std::move(reporters_[PipelineStage::kActivate]);
283 auto reporter = RestoreReporterAtBeginImpl(current_frame_id);
284 // The method will return nullptr if Impl reporter has been submitted
285 // prior to BeginMainFrame.
287 reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
288 reporter->impl_frame_finish_time());
289 impl_reporter = std::move(reporter);
294 if (!events_metrics.main_event_metrics.empty()) {
295 DCHECK(main_reporter);
299 DCHECK_EQ(impl_reporter->frame_id(), current_frame_id);
301 DCHECK_NE(main_reporter->frame_id(), current_frame_id);
306 // When |impl_reporter| does not exist, but there are still impl-side metrics,
307 // merge the main and impl metrics and pass the combined vector into
309 if (!impl_reporter && !events_metrics.impl_event_metrics.empty()) {
310 DCHECK(main_reporter);
311 // If there are impl events, there must be a reporter with
312 // |current_frame_id|.
313 DCHECK_EQ(main_reporter->frame_id(), current_frame_id);
314 events_metrics.main_event_metrics.reserve(
315 events_metrics.main_event_metrics.size() +
316 events_metrics.impl_event_metrics.size());
317 events_metrics.main_event_metrics.insert(
318 events_metrics.main_event_metrics.end(),
319 std::make_move_iterator(events_metrics.impl_event_metrics.begin()),
320 std::make_move_iterator(events_metrics.impl_event_metrics.end()));
324 main_reporter->StartStage(
325 StageType::kSubmitCompositorFrameToPresentationCompositorFrame,
327 main_reporter->AddEventsMetrics(
328 std::move(events_metrics.main_event_metrics));
329 main_reporter->set_has_missing_content(has_missing_content);
330 main_reporter->set_reporter_type_to_main();
331 submitted_compositor_frames_.emplace_back(frame_token,
332 std::move(main_reporter));
336 impl_reporter->EnableCompositorOnlyReporting();
337 impl_reporter->StartStage(
338 StageType::kSubmitCompositorFrameToPresentationCompositorFrame,
340 impl_reporter->AddEventsMetrics(
341 std::move(events_metrics.impl_event_metrics));
342 impl_reporter->set_has_missing_content(has_missing_content);
343 impl_reporter->set_is_accompanied_by_main_thread_update(
344 is_activated_frame_new);
345 impl_reporter->set_reporter_type_to_impl();
346 submitted_compositor_frames_.emplace_back(frame_token,
347 std::move(impl_reporter));
351 void CompositorFrameReportingController::DidNotProduceFrame(
352 const viz::BeginFrameId& id,
353 FrameSkippedReason skip_reason) {
354 for (auto& stage_reporter : reporters_) {
355 if (stage_reporter && stage_reporter->frame_id() == id) {
356 // The reporter will be flagged and terminated when replaced by another
357 // reporter. The reporter is not terminated immediately here because it
358 // can still end up producing a frame afterwards. For example, if the
359 // compositor does not have any updates, and the main-thread takes too
360 // long, then DidNotProduceFrame() is called for the reporter in the
361 // BeginMain stage, but the main-thread can make updates, which can be
362 // submitted with the next frame.
363 stage_reporter->OnDidNotProduceFrame(skip_reason);
364 if (skip_reason == FrameSkippedReason::kWaitingOnMain)
365 SetPartialUpdateDeciderWhenWaitingOnMain(stage_reporter);
372 void CompositorFrameReportingController::
373 SetPartialUpdateDeciderWhenWaitingOnMain(
374 std::unique_ptr<CompositorFrameReporter>& stage_reporter) {
375 // If the compositor has no updates, and the main-thread has not responded
376 // to the begin-main-frame yet, then depending on main thread having
377 // update or not this would be a NoFrameProduced or a DroppedFrame. To
378 // handle this case , keep the reporter for the main-thread, but recreate
379 // a reporter for the current frame and link it to the reporter it depends
381 auto reporter = RestoreReporterAtBeginImpl(stage_reporter->frame_id());
383 reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain);
384 reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
386 stage_reporter->AdoptReporter(std::move(reporter));
388 // The stage_reporter in this case was waiting for main, so needs to
389 // be adopted by the reporter which is waiting on Main thread's work
390 CompositorFrameReporter* partial_update_decider =
391 GetOutstandingUpdatesFromMain(stage_reporter->frame_id());
392 if (partial_update_decider) {
393 stage_reporter->SetPartialUpdateDecider(partial_update_decider);
394 stage_reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain);
395 stage_reporter->TerminateFrame(
396 FrameTerminationStatus::kDidNotProduceFrame, Now());
397 partial_update_decider->AdoptReporter(std::move(stage_reporter));
402 void CompositorFrameReportingController::TrackSwapTiming(
403 const viz::FrameTimingDetails& details) {
404 if (details.swap_timings.swap_start != base::TimeTicks()) {
405 if (latest_swap_times_.empty() ||
406 latest_swap_times_.back() < details.swap_timings.swap_start)
407 latest_swap_times_.push(details.swap_timings.swap_start);
410 // Making sure the queue would not keep growing in size.
411 DCHECK_LE(latest_swap_times_.size(), 10u);
414 void CompositorFrameReportingController::ReportMultipleSwaps(
415 base::TimeTicks begin_frame_time,
416 base::TimeDelta interval) {
417 while (!latest_swap_times_.empty() &&
418 latest_swap_times_.front() <= begin_frame_time - interval) {
419 latest_swap_times_.pop();
422 if (latest_swap_times_.empty())
425 if (latest_swap_times_.size() > 1) {
426 base::TimeDelta swap_delta =
427 latest_swap_times_.back() - latest_swap_times_.front();
429 if (swap_delta < interval) {
430 UMA_HISTOGRAM_PERCENTAGE("GPU.MultipleSwapsDelta",
431 swap_delta * 100.0 / interval);
433 const auto trace_track =
434 perfetto::Track(base::trace_event::GetNextGlobalTraceId());
435 TRACE_EVENT_BEGIN(kTraceCategory, "MultipleSwaps", trace_track,
436 latest_swap_times_.front());
437 TRACE_EVENT_END(kTraceCategory, trace_track, latest_swap_times_.back());
442 void CompositorFrameReportingController::OnFinishImplFrame(
443 const viz::BeginFrameId& id) {
444 for (auto& reporter : reporters_) {
445 if (reporter && reporter->frame_id() == id) {
446 reporter->OnFinishImplFrame(Now());
452 void CompositorFrameReportingController::DidPresentCompositorFrame(
453 uint32_t frame_token,
454 const viz::FrameTimingDetails& details) {
455 bool feedback_failed = details.presentation_feedback.failed();
457 if (!feedback_failed)
458 TrackSwapTiming(details);
460 for (auto submitted_frame = submitted_compositor_frames_.begin();
461 submitted_frame != submitted_compositor_frames_.end() &&
462 !viz::FrameTokenGT(submitted_frame->frame_token, frame_token);) {
463 bool is_earlier_frame = submitted_frame->frame_token != frame_token;
465 // If the presentation feedback is a failure, earlier frames should still be
466 // left in the queue as they still might end up being presented
467 // successfully. Skip to the next frame.
468 if (feedback_failed && is_earlier_frame) {
473 auto termination_status = feedback_failed
474 ? FrameTerminationStatus::kDidNotPresentFrame
475 : FrameTerminationStatus::kPresentedFrame;
477 // If this is an earlier frame, presentation feedback has been successful
478 // which means this earlier frame should be considered dropped.
479 if (is_earlier_frame)
480 termination_status = FrameTerminationStatus::kDidNotPresentFrame;
482 auto& reporter = submitted_frame->reporter;
483 reporter->SetVizBreakdown(details);
484 reporter->TerminateFrame(termination_status,
485 details.presentation_feedback.timestamp);
487 base::TimeDelta latency_prediction_deviation_threshold =
488 details.presentation_feedback.interval.is_zero()
489 ? kDefaultLatencyPredictionDeviationThreshold
490 : (details.presentation_feedback.interval) / 2;
491 switch (reporter->get_reporter_type()) {
492 case CompositorFrameReporter::ReporterType::kImpl:
493 reporter->CalculateCompositorLatencyPrediction(
494 previous_latency_predictions_impl_,
495 latency_prediction_deviation_threshold);
497 case CompositorFrameReporter::ReporterType::kMain:
498 reporter->CalculateCompositorLatencyPrediction(
499 previous_latency_predictions_main_,
500 latency_prediction_deviation_threshold);
504 if (termination_status == FrameTerminationStatus::kPresentedFrame) {
505 // If there are outstanding metrics from dropped frames older than this
506 // frame, this frame would be the first frame presented after those
507 // dropped frames. So, this frame is the one presenting updates from those
508 // frames to the user and should report metrics for them. Note that since
509 // reporters for submitted but dropped frames are terminated before any
510 // following frame being presented, all events metrics that should
511 // potentially be included in this presented frame are already in
512 // `events_metrics_from_dropped_frames_`.
513 for (auto it = events_metrics_from_dropped_frames_.begin();
514 it != events_metrics_from_dropped_frames_.end() &&
515 !(reporter->frame_id() < it->first);
516 it = events_metrics_from_dropped_frames_.erase(it)) {
517 reporter->AddEventsMetrics(std::move(it->second));
520 // TODO(crbug.com/1334827): Consider using a separate container to
521 // differentiate event predictions with and without a main dispatch stage.
522 reporter->CalculateEventLatencyPrediction(
523 event_latency_predictions_, latency_prediction_deviation_threshold);
525 // For presented frames, if `reporter` was cloned from another reporter,
526 // and the original reporter is still alive, then check whether the cloned
527 // reporter has a 'partial update decider'. It is still possible for the
528 // original reporter to terminate with 'no damage', and if that happens,
529 // then the cloned reporter's 'partial update' flag will need to be reset.
530 // To allow this to happen, keep the cloned reporter alive, and hand over
531 // its ownership to the original reporter, so that the cloned reporter
532 // stays alive until the original reporter is terminated, and the cloned
533 // reporter's 'partial update' flag can be unset if necessary. This is not
534 // necessary for frames with failed presentation as we can say for sure
535 // that they are dropped and nothing will change their fate.
536 if (CompositorFrameReporter* orig_reporter =
537 reporter->partial_update_decider()) {
538 orig_reporter->AdoptReporter(std::move(reporter));
541 // If the frame didn't end up being presented, keep its metrics around to
542 // be reported with the first following presented frame.
543 auto reporter_events_metrics = reporter->TakeEventsMetrics();
544 if (!reporter_events_metrics.empty()) {
545 auto& frame_events_metrics =
546 events_metrics_from_dropped_frames_[reporter->frame_id()];
547 frame_events_metrics.insert(
548 frame_events_metrics.end(),
549 std::make_move_iterator(reporter_events_metrics.begin()),
550 std::make_move_iterator(reporter_events_metrics.end()));
554 if (feedback_failed) {
555 // When feedback is for a failed presentation, `submitted_frame` is not
556 // necessarily in the front of the queue. We will reach here only once per
557 // did-present; so, we will have 1 operation of O(n) complexity (n is the
558 // number of previous frames).
559 submitted_frame = submitted_compositor_frames_.erase(submitted_frame);
561 // When feedback is for a successful presentation, `submitted_frame` is in
562 // the front of the queue; so, we will have n operations of O(1)
563 // complexity for a did-present (n is the number of previous frames).
564 // `pop_front()` function is used here to shrink the queue when necessary
565 // to avoid unnecessary memory usage over time.
566 DCHECK_EQ(submitted_frame->frame_token,
567 submitted_compositor_frames_.front().frame_token);
568 submitted_compositor_frames_.pop_front();
569 submitted_frame = submitted_compositor_frames_.begin();
574 void CompositorFrameReportingController::OnStoppedRequestingBeginFrames() {
575 // If the client stopped requesting begin-frames, that means the begin-frames
576 // currently being handled are no longer expected to produce any
577 // compositor-frames. So terminate the reporters.
579 for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) {
581 reporters_[i]->OnDidNotProduceFrame(FrameSkippedReason::kNoDamage);
582 reporters_[i]->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
586 last_started_compositor_frame_ = {};
589 void CompositorFrameReportingController::NotifyReadyToCommit(
590 std::unique_ptr<BeginMainFrameMetrics> details) {
591 DCHECK(reporters_[PipelineStage::kBeginMainFrame]);
592 reporters_[PipelineStage::kBeginMainFrame]->SetBlinkBreakdown(
593 std::move(details), begin_main_frame_start_time_);
594 AdvanceReporterStage(PipelineStage::kBeginMainFrame,
595 PipelineStage::kReadyToCommit);
598 void CompositorFrameReportingController::AddActiveTracker(
599 FrameSequenceTrackerType type) {
600 active_trackers_.set(static_cast<size_t>(type));
603 void CompositorFrameReportingController::RemoveActiveTracker(
604 FrameSequenceTrackerType type) {
605 active_trackers_.reset(static_cast<size_t>(type));
606 if (global_trackers_.dropped_frame_counter)
607 global_trackers_.dropped_frame_counter->ReportFrames();
610 void CompositorFrameReportingController::SetScrollingThread(
611 FrameInfo::SmoothEffectDrivingThread thread) {
612 scrolling_thread_ = thread;
615 void CompositorFrameReportingController::SetThreadAffectsSmoothness(
616 FrameInfo::SmoothEffectDrivingThread thread_type,
617 bool affects_smoothness) {
618 auto current_smooth_thread = GetSmoothThread();
620 if (thread_type == FrameInfo::SmoothEffectDrivingThread::kCompositor) {
621 is_compositor_thread_driving_smoothness_ = affects_smoothness;
623 DCHECK_EQ(thread_type, FrameInfo::SmoothEffectDrivingThread::kMain);
624 is_main_thread_driving_smoothness_ = affects_smoothness;
627 // keep the history for the last 3 seconds.
628 if (!smooth_thread_history_.empty()) {
629 auto expired_smooth_thread =
630 smooth_thread_history_.lower_bound(Now() - base::Seconds(3))--;
631 smooth_thread_history_.erase(smooth_thread_history_.begin(),
632 expired_smooth_thread);
635 // Only trackes the history if there is a change in smooth_thread_
636 if (current_smooth_thread != GetSmoothThread()) {
637 smooth_thread_history_.insert(std::make_pair(Now(), current_smooth_thread));
641 void CompositorFrameReportingController::AdvanceReporterStage(
643 PipelineStage target) {
644 auto& reporter = reporters_[target];
646 auto termination_status = FrameTerminationStatus::kReplacedByNewReporter;
647 base::TimeTicks termination_time;
648 if (reporter->did_not_produce_frame()) {
649 termination_time = reporter->did_not_produce_frame_time();
650 termination_status = FrameTerminationStatus::kDidNotProduceFrame;
651 } else if (target == PipelineStage::kBeginMainFrame &&
652 reporter->did_abort_main_frame()) {
653 termination_time = reporter->main_frame_abort_time();
655 termination_time = Now();
657 reporter->TerminateFrame(termination_status, termination_time);
659 reporters_[target] = std::move(reporters_[start]);
662 bool CompositorFrameReportingController::CanSubmitImplFrame(
663 const viz::BeginFrameId& id) const {
665 auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
667 DCHECK_EQ(reporter->frame_id(), id);
668 DCHECK(reporter->did_finish_impl_frame());
671 return reporters_[PipelineStage::kBeginImplFrame].get() != nullptr;
674 bool CompositorFrameReportingController::CanSubmitMainFrame(
675 const viz::BeginFrameId& id) const {
676 if (!reporters_[PipelineStage::kBeginMainFrame])
678 auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
679 return (reporter->frame_id() == id && reporter->did_finish_impl_frame() &&
680 reporter->did_abort_main_frame());
683 std::unique_ptr<CompositorFrameReporter>
684 CompositorFrameReportingController::RestoreReporterAtBeginImpl(
685 const viz::BeginFrameId& id) {
686 auto& main_reporter = reporters_[PipelineStage::kBeginMainFrame];
687 auto& ready_to_commit_reporter = reporters_[PipelineStage::kReadyToCommit];
688 auto& commit_reporter = reporters_[PipelineStage::kCommit];
689 if (main_reporter && main_reporter->frame_id() == id) {
690 DCHECK(!ready_to_commit_reporter ||
691 ready_to_commit_reporter->frame_id() != id);
692 DCHECK(!commit_reporter || commit_reporter->frame_id() != id);
693 return main_reporter->CopyReporterAtBeginImplStage();
695 if (ready_to_commit_reporter && ready_to_commit_reporter->frame_id() == id) {
696 DCHECK(!commit_reporter || commit_reporter->frame_id() != id);
697 return ready_to_commit_reporter->CopyReporterAtBeginImplStage();
699 if (commit_reporter && commit_reporter->frame_id() == id)
700 return commit_reporter->CopyReporterAtBeginImplStage();
704 void CompositorFrameReportingController::SetUkmManager(UkmManager* manager) {
705 latency_ukm_reporter_->set_ukm_manager(manager);
708 CompositorFrameReporter::SmoothThread
709 CompositorFrameReportingController::GetSmoothThread() const {
710 if (is_main_thread_driving_smoothness_) {
711 return is_compositor_thread_driving_smoothness_ ? SmoothThread::kSmoothBoth
712 : SmoothThread::kSmoothMain;
715 return is_compositor_thread_driving_smoothness_
716 ? SmoothThread::kSmoothCompositor
717 : SmoothThread::kSmoothNone;
720 CompositorFrameReporter::SmoothThread
721 CompositorFrameReportingController::GetSmoothThreadAtTime(
722 base::TimeTicks timestamp) const {
723 if (smooth_thread_history_.lower_bound(timestamp) ==
724 smooth_thread_history_.end())
725 return GetSmoothThread();
726 return smooth_thread_history_.lower_bound(timestamp)->second;
729 CompositorFrameReporter*
730 CompositorFrameReportingController::GetOutstandingUpdatesFromMain(
731 const viz::BeginFrameId& id) const {
732 // Any unterminated reporter in the 'main frame', or 'commit' stages, then
733 // that indicates some pending updates from the main thread.
735 const auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
736 if (reporter && reporter->frame_id() < id &&
737 !reporter->did_abort_main_frame()) {
738 return reporter.get();
742 const auto& reporter = reporters_[PipelineStage::kReadyToCommit];
743 if (reporter && reporter->frame_id() < id &&
744 !reporter->did_abort_main_frame()) {
745 return reporter.get();
749 const auto& reporter = reporters_[PipelineStage::kCommit];
750 if (reporter && reporter->frame_id() < id) {
751 DCHECK(!reporter->did_abort_main_frame());
752 return reporter.get();
758 void CompositorFrameReportingController::CreateReportersForDroppedFrames(
759 const viz::BeginFrameArgs& old_args,
760 const viz::BeginFrameArgs& new_args) const {
761 DCHECK_EQ(new_args.frame_id.source_id, old_args.frame_id.source_id);
763 new_args.frame_id.sequence_number - new_args.frames_throttled_since_last,
764 old_args.frame_id.sequence_number);
765 const uint32_t interval = new_args.frame_id.sequence_number -
766 old_args.frame_id.sequence_number -
767 new_args.frames_throttled_since_last;
769 // Up to 100 frames will be reported (100 closest frames to new_args).
770 const uint32_t kMaxFrameCount = 100;
772 // If there are more than 100 frames skipped, ignore them
773 if (interval > kMaxFrameCount)
776 auto timestamp = old_args.frame_time + old_args.interval;
777 for (uint32_t i = 1; i < interval; ++i, timestamp += old_args.interval) {
778 auto args = viz::BeginFrameArgs::Create(
779 BEGINFRAME_FROM_HERE, old_args.frame_id.source_id,
780 old_args.frame_id.sequence_number + i, timestamp,
781 timestamp + old_args.interval, old_args.interval,
782 viz::BeginFrameArgs::NORMAL);
783 devtools_instrumentation::DidBeginFrame(
784 layer_tree_host_id_, args.frame_time, args.frame_id.sequence_number);
785 // ThreadType::kUnknown is used here for scrolling thread, because the
786 // frames reported here could have a scroll interaction active at their
787 // start time, but they were skipped and history of scrolling thread might
788 // change in the diff of start time and report time.
789 auto reporter = std::make_unique<CompositorFrameReporter>(
790 active_trackers_, args, should_report_histograms_,
791 GetSmoothThreadAtTime(timestamp),
792 FrameInfo::SmoothEffectDrivingThread::kUnknown, layer_tree_host_id_,
794 reporter->set_tick_clock(tick_clock_);
795 reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
797 reporter->TerminateFrame(FrameTerminationStatus::kDidNotPresentFrame,
799 reporter->set_is_backfill(true);
803 void CompositorFrameReportingController::AddSortedFrame(
804 const viz::BeginFrameArgs& args,
805 const FrameInfo& frame_info) {
806 if (global_trackers_.frame_sequence_trackers) {
807 global_trackers_.frame_sequence_trackers->AddSortedFrame(args, frame_info);
811 void CompositorFrameReportingController::SetDroppedFrameCounter(
812 DroppedFrameCounter* counter) {
813 global_trackers_.dropped_frame_counter = counter;
815 counter->SetSortedFrameCallback(
816 base::BindRepeating(&CompositorFrameReportingController::AddSortedFrame,
817 base::Unretained(this)));