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/frame_sequence_tracker.h"
12 #include "base/bind.h"
13 #include "base/containers/contains.h"
14 #include "base/containers/cxx20_erase.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/notreached.h"
19 #include "base/trace_event/trace_event.h"
20 #include "base/trace_event/traced_value.h"
21 #include "components/viz/common/frame_sinks/begin_frame_args.h"
22 #include "components/viz/common/quads/compositor_frame_metadata.h"
23 #include "ui/gfx/presentation_feedback.h"
25 // This macro is used with DCHECK to provide addition debug info.
27 #define TRACKER_TRACE_STREAM frame_sequence_trace_
28 #define TRACKER_DCHECK_MSG \
29 " in " << GetFrameSequenceTrackerTypeName(this->type()) \
30 << " tracker: " << frame_sequence_trace_.str() << " (" \
31 << frame_sequence_trace_.str().size() << ")";
33 #define TRACKER_TRACE_STREAM EAT_STREAM_PARAMETERS
34 #define TRACKER_DCHECK_MSG ""
41 constexpr char kTraceCategory[] =
42 "cc,benchmark," TRACE_DISABLED_BY_DEFAULT("devtools.timeline.frame");
46 using ThreadType = FrameInfo::SmoothEffectDrivingThread;
48 // In the |TRACKER_TRACE_STREAM|, we mod the numbers such as frame sequence
49 // number, or frame token, such that the debug string is not too long.
50 constexpr int kDebugStrMod = 1000;
52 const char* FrameSequenceTracker::GetFrameSequenceTrackerTypeName(
53 FrameSequenceTrackerType type) {
55 case FrameSequenceTrackerType::kCompositorAnimation:
56 return "CompositorAnimation";
57 case FrameSequenceTrackerType::kMainThreadAnimation:
58 return "MainThreadAnimation";
59 case FrameSequenceTrackerType::kPinchZoom:
61 case FrameSequenceTrackerType::kRAF:
63 case FrameSequenceTrackerType::kTouchScroll:
65 case FrameSequenceTrackerType::kVideo:
67 case FrameSequenceTrackerType::kWheelScroll:
69 case FrameSequenceTrackerType::kScrollbarScroll:
70 return "ScrollbarScroll";
71 case FrameSequenceTrackerType::kCustom:
73 case FrameSequenceTrackerType::kCanvasAnimation:
74 return "CanvasAnimation";
75 case FrameSequenceTrackerType::kJSAnimation:
77 case FrameSequenceTrackerType::kSETMainThreadAnimation:
78 return "SETMainThreadAnimation";
79 case FrameSequenceTrackerType::kSETCompositorAnimation:
80 return "SETCompositorAnimation";
81 case FrameSequenceTrackerType::kMaxType:
86 FrameSequenceTracker::FrameSequenceTracker(
87 FrameSequenceTrackerType type,
88 ThroughputUkmReporter* throughput_ukm_reporter)
89 : custom_sequence_id_(-1),
91 std::make_unique<FrameSequenceMetrics>(type,
92 throughput_ukm_reporter)) {
93 DCHECK_LT(type, FrameSequenceTrackerType::kMaxType);
94 DCHECK(type != FrameSequenceTrackerType::kCustom);
95 // TODO(crbug.com/1158439): remove the trace event once the validation is
97 TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
98 kTraceCategory, "TrackerValidation", TRACE_ID_LOCAL(this),
99 base::TimeTicks::Now(), "name", GetFrameSequenceTrackerTypeName(type));
102 FrameSequenceTracker::FrameSequenceTracker(
103 int custom_sequence_id,
104 FrameSequenceMetrics::CustomReporter custom_reporter)
105 : custom_sequence_id_(custom_sequence_id),
106 metrics_(std::make_unique<FrameSequenceMetrics>(
107 FrameSequenceTrackerType::kCustom,
108 /*ukm_reporter=*/nullptr)) {
109 DCHECK_GT(custom_sequence_id_, 0);
110 metrics_->SetCustomReporter(std::move(custom_reporter));
113 FrameSequenceTracker::~FrameSequenceTracker() {
114 TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP2(
115 kTraceCategory, "TrackerValidation", TRACE_ID_LOCAL(this),
116 base::TimeTicks::Now(), "aborted_main", aborted_main_frame_,
117 "no_damage_main", no_damage_draw_main_frames_);
121 void FrameSequenceTracker::ScheduleTerminate() {
122 // If the last frame has ended and there is no frame awaiting presentation,
123 // then it is ready to terminate.
124 if (!is_inside_frame_ && last_submitted_frame_ == 0)
125 termination_status_ = TerminationStatus::kReadyForTermination;
127 termination_status_ = TerminationStatus::kScheduledForTermination;
130 void FrameSequenceTracker::ReportBeginImplFrame(
131 const viz::BeginFrameArgs& args) {
132 if (termination_status_ != TerminationStatus::kActive)
135 if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id))
138 TRACKER_TRACE_STREAM << "b(" << args.frame_id.sequence_number % kDebugStrMod
141 DCHECK(!is_inside_frame_) << TRACKER_DCHECK_MSG;
142 is_inside_frame_ = true;
144 if (args.type == viz::BeginFrameArgs::NORMAL)
145 impl_frames_.insert(args.frame_id);
148 DCHECK_EQ(last_started_impl_sequence_, 0u) << TRACKER_DCHECK_MSG;
149 last_started_impl_sequence_ = args.frame_id.sequence_number;
150 if (reset_all_state_) {
151 begin_impl_frame_data_ = {};
152 begin_main_frame_data_ = {};
153 reset_all_state_ = false;
156 DCHECK(!frame_had_no_compositor_damage_) << TRACKER_DCHECK_MSG;
157 DCHECK(!compositor_frame_submitted_) << TRACKER_DCHECK_MSG;
159 UpdateTrackedFrameData(&begin_impl_frame_data_, args.frame_id.source_id,
160 args.frame_id.sequence_number,
161 args.frames_throttled_since_last);
162 impl_throughput().frames_expected +=
163 begin_impl_frame_data_.previous_sequence_delta;
165 ++impl_throughput().frames_received;
168 if (first_frame_timestamp_.is_null())
169 first_frame_timestamp_ = args.frame_time;
172 void FrameSequenceTracker::ReportBeginMainFrame(
173 const viz::BeginFrameArgs& args) {
174 if (termination_status_ != TerminationStatus::kActive)
177 if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id))
180 TRACKER_TRACE_STREAM << "B("
181 << begin_main_frame_data_.previous_sequence %
183 << "," << args.frame_id.sequence_number % kDebugStrMod
186 if (first_received_main_sequence_ &&
187 first_received_main_sequence_ > args.frame_id.sequence_number) {
191 if (!first_received_main_sequence_ &&
192 ShouldIgnoreSequence(args.frame_id.sequence_number)) {
197 if (args.type == viz::BeginFrameArgs::NORMAL) {
198 DCHECK(impl_frames_.contains(args.frame_id)) << TRACKER_DCHECK_MSG;
202 last_processed_main_sequence_latency_ = 0;
203 pending_main_sequences_.push_back(args.frame_id.sequence_number);
205 UpdateTrackedFrameData(&begin_main_frame_data_, args.frame_id.source_id,
206 args.frame_id.sequence_number,
207 args.frames_throttled_since_last);
208 if (!first_received_main_sequence_ ||
209 first_received_main_sequence_ <= last_no_main_damage_sequence_) {
210 first_received_main_sequence_ = args.frame_id.sequence_number;
212 main_throughput().frames_expected +=
213 begin_main_frame_data_.previous_sequence_delta;
214 previous_begin_main_sequence_ = current_begin_main_sequence_;
215 current_begin_main_sequence_ = args.frame_id.sequence_number;
218 void FrameSequenceTracker::ReportMainFrameProcessed(
219 const viz::BeginFrameArgs& args) {
220 if (termination_status_ != TerminationStatus::kActive)
223 if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id))
226 TRACKER_TRACE_STREAM << "E(" << args.frame_id.sequence_number % kDebugStrMod
229 const bool previous_main_frame_submitted_or_no_damage =
230 previous_begin_main_sequence_ != 0 &&
231 (last_submitted_main_sequence_ == previous_begin_main_sequence_ ||
232 last_no_main_damage_sequence_ == previous_begin_main_sequence_);
233 if (last_processed_main_sequence_ != 0 &&
234 !had_impl_frame_submitted_between_commits_ &&
235 !previous_main_frame_submitted_or_no_damage) {
236 DCHECK_GE(main_throughput().frames_expected,
237 begin_main_frame_data_.previous_sequence_delta)
238 << TRACKER_DCHECK_MSG;
239 main_throughput().frames_expected -=
240 begin_main_frame_data_.previous_sequence_delta;
241 last_no_main_damage_sequence_ = previous_begin_main_sequence_;
243 had_impl_frame_submitted_between_commits_ = false;
245 if (first_received_main_sequence_ &&
246 args.frame_id.sequence_number >= first_received_main_sequence_) {
247 DCHECK_EQ(last_processed_main_sequence_latency_, 0u) << TRACKER_DCHECK_MSG;
248 last_processed_main_sequence_ = args.frame_id.sequence_number;
249 last_processed_main_sequence_latency_ =
250 std::max(last_started_impl_sequence_, last_processed_impl_sequence_) -
251 args.frame_id.sequence_number;
255 void FrameSequenceTracker::ReportSubmitFrame(
256 uint32_t frame_token,
257 bool has_missing_content,
258 const viz::BeginFrameAck& ack,
259 const viz::BeginFrameArgs& origin_args) {
260 DCHECK_NE(termination_status_, TerminationStatus::kReadyForTermination);
262 // TODO(crbug.com/1072482): find a proper way to terminate a tracker.
263 // Right now, we define a magical number |frames_to_terminate_tracker| = 3,
264 // which means that if this frame_token is more than 3 frames compared with
265 // the last submitted frame, then we assume that the last submitted frame is
266 // not going to be presented, and thus terminate this tracker.
267 const uint32_t frames_to_terminate_tracker = 3;
268 if (termination_status_ == TerminationStatus::kScheduledForTermination &&
269 last_submitted_frame_ != 0 &&
270 viz::FrameTokenGT(frame_token,
271 last_submitted_frame_ + frames_to_terminate_tracker)) {
272 termination_status_ = TerminationStatus::kReadyForTermination;
276 if (ShouldIgnoreBeginFrameSource(ack.frame_id.source_id) ||
277 ShouldIgnoreSequence(ack.frame_id.sequence_number)) {
278 ignored_frame_tokens_.insert(frame_token);
283 DCHECK(is_inside_frame_) << TRACKER_DCHECK_MSG;
284 DCHECK_LT(impl_throughput().frames_processed,
285 impl_throughput().frames_received)
286 << TRACKER_DCHECK_MSG;
287 ++impl_throughput().frames_processed;
290 last_processed_impl_sequence_ = ack.frame_id.sequence_number;
291 if (first_submitted_frame_ == 0)
292 first_submitted_frame_ = frame_token;
293 last_submitted_frame_ = frame_token;
294 compositor_frame_submitted_ = true;
296 TRACKER_TRACE_STREAM << "s(" << frame_token % kDebugStrMod << ")";
297 had_impl_frame_submitted_between_commits_ = true;
298 metrics()->NotifySubmitForJankReporter(
299 FrameInfo::SmoothEffectDrivingThread::kCompositor, frame_token,
300 ack.frame_id.sequence_number);
302 const bool main_changes_after_sequence_started =
303 first_received_main_sequence_ &&
304 origin_args.frame_id.sequence_number >= first_received_main_sequence_;
305 const bool main_changes_include_new_changes =
306 last_submitted_main_sequence_ == 0 ||
307 origin_args.frame_id.sequence_number > last_submitted_main_sequence_;
308 const bool main_change_had_no_damage =
309 last_no_main_damage_sequence_ != 0 &&
310 origin_args.frame_id.sequence_number == last_no_main_damage_sequence_;
311 const bool origin_args_is_valid = origin_args.frame_id.sequence_number <=
312 begin_main_frame_data_.previous_sequence;
314 if (!ShouldIgnoreBeginFrameSource(origin_args.frame_id.source_id) &&
315 origin_args_is_valid) {
316 if (main_changes_after_sequence_started &&
317 main_changes_include_new_changes && !main_change_had_no_damage) {
318 submitted_frame_had_new_main_content_ = true;
319 TRACKER_TRACE_STREAM << "S("
320 << origin_args.frame_id.sequence_number %
323 metrics()->NotifySubmitForJankReporter(
324 FrameInfo::SmoothEffectDrivingThread::kMain, frame_token,
325 origin_args.frame_id.sequence_number);
327 last_submitted_main_sequence_ = origin_args.frame_id.sequence_number;
328 main_frames_.push_back(frame_token);
329 DCHECK_GE(main_throughput().frames_expected, main_frames_.size())
330 << TRACKER_DCHECK_MSG;
334 if (has_missing_content) {
335 checkerboarding_.frames.push_back(frame_token);
339 void FrameSequenceTracker::ReportFrameEnd(
340 const viz::BeginFrameArgs& args,
341 const viz::BeginFrameArgs& main_args) {
342 DCHECK_NE(termination_status_, TerminationStatus::kReadyForTermination);
344 if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id))
347 // We only update the `pending_main_sequences` when the frame has successfully
348 // submitted, or when we determine that it has no damage. See
349 // ReportMainFrameCausedNoDamage. We do not do this in
350 // NotifyMainFrameProcessed, as that can occur during Commit, and we may yet
351 // determine at Draw that there was no damage.
352 while (!pending_main_sequences_.empty() &&
353 pending_main_sequences_.front() <=
354 main_args.frame_id.sequence_number) {
355 pending_main_sequences_.pop_front();
357 TRACKER_TRACE_STREAM << "e(" << args.frame_id.sequence_number % kDebugStrMod
359 << main_args.frame_id.sequence_number % kDebugStrMod
362 bool should_ignore_sequence =
363 ShouldIgnoreSequence(args.frame_id.sequence_number);
364 if (reset_all_state_) {
365 begin_impl_frame_data_ = {};
366 begin_main_frame_data_ = {};
367 reset_all_state_ = false;
370 if (should_ignore_sequence) {
371 is_inside_frame_ = false;
375 if (compositor_frame_submitted_ && submitted_frame_had_new_main_content_ &&
376 last_processed_main_sequence_latency_) {
377 // If a compositor frame was submitted with new content from the
378 // main-thread, then make sure the latency gets accounted for.
379 main_throughput().frames_expected += last_processed_main_sequence_latency_;
382 // It is possible that the compositor claims there was no damage from the
383 // compositor, but before the frame ends, it submits a compositor frame (e.g.
384 // with some damage from main). In such cases, the compositor is still
385 // responsible for processing the update, and therefore the 'no damage' claim
387 if (frame_had_no_compositor_damage_ && !compositor_frame_submitted_) {
388 DCHECK_GT(impl_throughput().frames_expected, 0u) << TRACKER_DCHECK_MSG;
389 DCHECK_GT(impl_throughput().frames_expected,
390 impl_throughput().frames_produced)
391 << TRACKER_DCHECK_MSG;
392 DCHECK_GE(impl_throughput().frames_produced,
393 impl_throughput().frames_ontime)
394 << TRACKER_DCHECK_MSG;
395 --impl_throughput().frames_expected;
396 metrics()->NotifyNoUpdateForJankReporter(
397 FrameInfo::SmoothEffectDrivingThread::kCompositor,
398 args.frame_id.sequence_number, args.interval);
400 ++impl_throughput().frames_processed;
401 // If these two are the same, it means that each impl frame is either
402 // no-damage or submitted. That's expected, so we don't need those in the
404 if (impl_throughput().frames_processed == impl_throughput().frames_received)
405 ignored_trace_char_count_ = frame_sequence_trace_.str().size();
407 NOTREACHED() << TRACKER_DCHECK_MSG;
409 begin_impl_frame_data_.previous_sequence = 0;
411 // last_submitted_frame_ == 0 means the last impl frame has been presented.
412 if (termination_status_ == TerminationStatus::kScheduledForTermination &&
413 last_submitted_frame_ == 0)
414 termination_status_ = TerminationStatus::kReadyForTermination;
416 frame_had_no_compositor_damage_ = false;
417 compositor_frame_submitted_ = false;
418 submitted_frame_had_new_main_content_ = false;
419 last_processed_main_sequence_latency_ = 0;
421 DCHECK(is_inside_frame_) << TRACKER_DCHECK_MSG;
422 is_inside_frame_ = false;
424 DCHECK_EQ(last_started_impl_sequence_, last_processed_impl_sequence_)
425 << TRACKER_DCHECK_MSG;
426 last_started_impl_sequence_ = 0;
429 void FrameSequenceTracker::ReportFramePresented(
430 uint32_t frame_token,
431 const gfx::PresentationFeedback& feedback) {
432 // TODO(xidachen): We should early exit if |last_submitted_frame_| = 0, as it
433 // means that we are presenting the same frame_token again.
434 const bool submitted_frame_since_last_presentation = !!last_submitted_frame_;
435 // !viz::FrameTokenGT(a, b) is equivalent to b >= a.
436 const bool frame_token_acks_last_frame =
437 !viz::FrameTokenGT(last_submitted_frame_, frame_token);
439 // Even if the presentation timestamp is null, we set last_submitted_frame_ to
440 // 0 such that the tracker can be terminated.
441 if (last_submitted_frame_ && frame_token_acks_last_frame)
442 last_submitted_frame_ = 0;
443 // Update termination status if this is scheduled for termination, and it is
444 // not waiting for any frames, or it has received the presentation-feedback
445 // for the latest frame it is tracking.
447 // We should always wait for an impl frame to end, that is, ReportFrameEnd.
448 if (termination_status_ == TerminationStatus::kScheduledForTermination &&
449 last_submitted_frame_ == 0 && !is_inside_frame_) {
450 termination_status_ = TerminationStatus::kReadyForTermination;
453 if (first_submitted_frame_ == 0 ||
454 viz::FrameTokenGT(first_submitted_frame_, frame_token)) {
455 // We are getting presentation feedback for frames that were submitted
456 // before this sequence started. So ignore these.
460 TRACKER_TRACE_STREAM << "P(" << frame_token % kDebugStrMod << ")";
462 base::EraseIf(ignored_frame_tokens_, [frame_token](const uint32_t& token) {
463 return viz::FrameTokenGT(frame_token, token);
465 if (ignored_frame_tokens_.contains(frame_token))
468 const auto vsync_interval =
469 (feedback.interval.is_zero() ? viz::BeginFrameArgs::DefaultInterval()
470 : feedback.interval);
471 DCHECK(!vsync_interval.is_zero()) << TRACKER_DCHECK_MSG;
472 base::TimeTicks safe_deadline_for_frame =
473 last_frame_presentation_timestamp_ + vsync_interval * 1.5;
475 const bool was_presented = !feedback.failed();
476 if (was_presented && submitted_frame_since_last_presentation) {
477 if (!last_frame_presentation_timestamp_.is_null() &&
478 (safe_deadline_for_frame < feedback.timestamp)) {
479 DCHECK_LE(impl_throughput().frames_ontime,
480 impl_throughput().frames_produced)
481 << TRACKER_DCHECK_MSG;
482 ++impl_throughput().frames_ontime;
485 DCHECK_LT(impl_throughput().frames_produced,
486 impl_throughput().frames_expected)
487 << TRACKER_DCHECK_MSG;
488 ++impl_throughput().frames_produced;
489 if (metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
490 metrics()->AdvanceTrace(feedback.timestamp);
493 metrics()->ComputeJank(FrameInfo::SmoothEffectDrivingThread::kCompositor,
494 frame_token, feedback.timestamp, vsync_interval);
498 // This presentation includes the visual update from all main frame tokens
500 const unsigned size_before_erase = main_frames_.size();
501 while (!main_frames_.empty() &&
502 !viz::FrameTokenGT(main_frames_.front(), frame_token)) {
503 main_frames_.pop_front();
505 if (main_frames_.size() < size_before_erase) {
506 DCHECK_LT(main_throughput().frames_produced,
507 main_throughput().frames_expected)
508 << TRACKER_DCHECK_MSG;
509 ++main_throughput().frames_produced;
510 if (metrics()->GetEffectiveThread() == ThreadType::kMain) {
511 metrics()->AdvanceTrace(feedback.timestamp);
514 metrics()->ComputeJank(FrameInfo::SmoothEffectDrivingThread::kMain,
515 frame_token, feedback.timestamp, vsync_interval);
517 if (main_frames_.size() < size_before_erase) {
518 if (!last_frame_presentation_timestamp_.is_null() &&
519 (safe_deadline_for_frame < feedback.timestamp)) {
520 DCHECK_LE(main_throughput().frames_ontime,
521 main_throughput().frames_produced)
522 << TRACKER_DCHECK_MSG;
523 ++main_throughput().frames_ontime;
526 last_frame_presentation_timestamp_ = feedback.timestamp;
528 if (checkerboarding_.last_frame_had_checkerboarding) {
529 DCHECK(!checkerboarding_.last_frame_timestamp.is_null())
530 << TRACKER_DCHECK_MSG;
531 DCHECK(!feedback.timestamp.is_null()) << TRACKER_DCHECK_MSG;
533 // |feedback.timestamp| is the timestamp when the latest frame was
534 // presented. |checkerboarding_.last_frame_timestamp| is the timestamp
535 // when the previous frame (which had checkerboarding) was presented. Use
536 // |feedback.interval| to compute the number of vsyncs that have passed
537 // between the two frames (since that is how many times the user saw that
538 // checkerboarded frame).
539 base::TimeDelta difference =
540 feedback.timestamp - checkerboarding_.last_frame_timestamp;
541 const auto& interval = feedback.interval.is_zero()
542 ? viz::BeginFrameArgs::DefaultInterval()
544 DCHECK(!interval.is_zero()) << TRACKER_DCHECK_MSG;
545 constexpr base::TimeDelta kEpsilon = base::Milliseconds(1);
546 int64_t frames = (difference + kEpsilon).IntDiv(interval);
547 metrics_->add_checkerboarded_frames(frames);
550 const bool frame_had_checkerboarding =
551 base::Contains(checkerboarding_.frames, frame_token);
552 checkerboarding_.last_frame_had_checkerboarding = frame_had_checkerboarding;
553 checkerboarding_.last_frame_timestamp = feedback.timestamp;
556 while (!checkerboarding_.frames.empty() &&
557 !viz::FrameTokenGT(checkerboarding_.frames.front(), frame_token)) {
558 checkerboarding_.frames.pop_front();
562 void FrameSequenceTracker::ReportImplFrameCausedNoDamage(
563 const viz::BeginFrameAck& ack) {
564 DCHECK_NE(termination_status_, TerminationStatus::kReadyForTermination);
566 if (ShouldIgnoreBeginFrameSource(ack.frame_id.source_id))
569 TRACKER_TRACE_STREAM << "n(" << ack.frame_id.sequence_number % kDebugStrMod
572 // This tracker would be scheduled to terminate, and this frame doesn't belong
574 if (ShouldIgnoreSequence(ack.frame_id.sequence_number))
577 last_processed_impl_sequence_ = ack.frame_id.sequence_number;
578 // If there is no damage for this frame (and no frame is submitted), then the
579 // impl-sequence needs to be reset. However, this should be done after the
580 // processing the frame is complete (i.e. in ReportFrameEnd()), so that other
581 // notifications (e.g. 'no main damage' etc.) can be handled correctly.
582 DCHECK_EQ(begin_impl_frame_data_.previous_sequence,
583 ack.frame_id.sequence_number);
584 frame_had_no_compositor_damage_ = true;
587 void FrameSequenceTracker::ReportMainFrameCausedNoDamage(
588 const viz::BeginFrameArgs& args,
590 if (termination_status_ != TerminationStatus::kActive)
593 if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id))
596 TRACKER_TRACE_STREAM << "N("
597 << begin_main_frame_data_.previous_sequence %
599 << "," << args.frame_id.sequence_number % kDebugStrMod
602 if (!first_received_main_sequence_ ||
603 first_received_main_sequence_ > args.frame_id.sequence_number) {
607 if (last_no_main_damage_sequence_ == args.frame_id.sequence_number)
610 auto initial_pending_size = pending_main_sequences_.size();
611 while (!pending_main_sequences_.empty() &&
612 pending_main_sequences_.front() <= args.frame_id.sequence_number) {
613 pending_main_sequences_.pop_front();
615 // If we didn't remove any `pending_main_sequences`, then we have previously
616 // submitted a CompositorFrame with damage for `args.frame_id.sequence_number`
617 // and the sequence is being re-used on a subsequent Impl frame. Which just
618 // happens to have no damage.
620 // This can occur when there is a Compositor Animation that is offscreen, and
621 // when we are awaiting the next BeginMainFrame to be Committed and Activated.
623 // We do not change the `main_throughput` expectations when the sequence is
625 if (pending_main_sequences_.size() == initial_pending_size)
629 ++aborted_main_frame_;
631 ++no_damage_draw_main_frames_;
633 DCHECK_GT(main_throughput().frames_expected, 0u) << TRACKER_DCHECK_MSG;
634 DCHECK_GT(main_throughput().frames_expected,
635 main_throughput().frames_produced)
636 << TRACKER_DCHECK_MSG;
637 DCHECK_GE(main_throughput().frames_produced, main_throughput().frames_ontime)
638 << TRACKER_DCHECK_MSG;
639 last_no_main_damage_sequence_ = args.frame_id.sequence_number;
640 --main_throughput().frames_expected;
641 metrics()->NotifyNoUpdateForJankReporter(
642 FrameInfo::SmoothEffectDrivingThread::kMain,
643 args.frame_id.sequence_number, args.interval);
645 DCHECK_GE(main_throughput().frames_expected, main_frames_.size())
646 << TRACKER_DCHECK_MSG;
648 // Could be 0 if there were a pause frame production.
649 if (begin_main_frame_data_.previous_sequence != 0) {
650 DCHECK_GE(begin_main_frame_data_.previous_sequence,
651 args.frame_id.sequence_number)
652 << TRACKER_DCHECK_MSG;
654 begin_main_frame_data_.previous_sequence = 0;
657 void FrameSequenceTracker::PauseFrameProduction() {
658 // The states need to be reset, so that the tracker ignores the vsyncs until
659 // the next received begin-frame. However, defer doing that until the frame
660 // ends (or a new frame starts), so that in case a frame is in-progress,
661 // subsequent notifications for that frame can be handled correctly.
662 TRACKER_TRACE_STREAM << 'R';
663 reset_all_state_ = true;
666 void FrameSequenceTracker::UpdateTrackedFrameData(
667 TrackedFrameData* frame_data,
669 uint64_t sequence_number,
670 uint64_t throttled_frame_count) {
671 if (frame_data->previous_sequence &&
672 frame_data->previous_source == source_id) {
673 uint32_t current_latency =
674 sequence_number - frame_data->previous_sequence - throttled_frame_count;
675 DCHECK_GT(current_latency, 0u) << TRACKER_DCHECK_MSG;
676 frame_data->previous_sequence_delta = current_latency;
678 frame_data->previous_sequence_delta = 1;
680 frame_data->previous_source = source_id;
681 frame_data->previous_sequence = sequence_number;
684 bool FrameSequenceTracker::ShouldIgnoreBeginFrameSource(
685 uint64_t source_id) const {
686 if (begin_impl_frame_data_.previous_source == 0)
687 return source_id == viz::BeginFrameArgs::kManualSourceId;
688 return source_id != begin_impl_frame_data_.previous_source;
691 // This check handles two cases:
692 // 1. When there is a call to ReportBeginMainFrame, or ReportSubmitFrame, or
693 // ReportFramePresented, there must be a ReportBeginImplFrame for that sequence.
694 // Otherwise, the begin_impl_frame_data_.previous_sequence would be 0.
695 // 2. A tracker is scheduled to terminate, then any new request to handle a new
696 // impl frame whose sequence_number > begin_impl_frame_data_.previous_sequence
697 // should be ignored.
698 // Note that sequence_number < begin_impl_frame_data_.previous_sequence cannot
700 bool FrameSequenceTracker::ShouldIgnoreSequence(
701 uint64_t sequence_number) const {
702 return sequence_number != begin_impl_frame_data_.previous_sequence;
705 bool FrameSequenceTracker::ShouldReportMetricsNow(
706 const viz::BeginFrameArgs& args) const {
707 return metrics_->HasEnoughDataForReporting() &&
708 !first_frame_timestamp_.is_null() &&
709 args.frame_time - first_frame_timestamp_ >= time_delta_to_report_;
712 std::unique_ptr<FrameSequenceMetrics> FrameSequenceTracker::TakeMetrics() {
714 DCHECK_EQ(impl_throughput().frames_received,
715 impl_throughput().frames_processed)
716 << frame_sequence_trace_.str().substr(ignored_trace_char_count_);
718 return std::move(metrics_);
721 void FrameSequenceTracker::CleanUp() {
723 metrics_->ReportLeftoverData();
726 void FrameSequenceTracker::AddSortedFrame(const viz::BeginFrameArgs& args,
727 const FrameInfo& frame_info) {
729 metrics_->AddSortedFrame(args, frame_info);
732 FrameSequenceTracker::CheckerboardingData::CheckerboardingData() = default;
733 FrameSequenceTracker::CheckerboardingData::~CheckerboardingData() = default;