Add MissedDeadlineFrames to track frames that miss the vsync deadline
This change adds a MissedDeadlineFrames UMA that tracks frames that
miss the vsync deadline during scroll/animations.
Bug: 1092938
Change-Id: I044985583193d94c90485f1308be5645d553af7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2343642
Commit-Queue: Matthew Crabill <[email protected]>
Reviewed-by: Sadrul Chowdhury <[email protected]>
Reviewed-by: Xida Chen <[email protected]>
Reviewed-by: David Bokan <[email protected]>
Reviewed-by: Behdad Bakhshinategh <[email protected]>
Reviewed-by: Stephen Chenney <[email protected]>
Cr-Commit-Position: refs/heads/master@{#807596}
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc
index 67d182b..01c2a5f 100644
--- a/cc/metrics/frame_sequence_metrics.cc
+++ b/cc/metrics/frame_sequence_metrics.cc
@@ -51,6 +51,13 @@
FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
}
+std::string GetMissedDeadlineHistogramName(FrameSequenceTrackerType type,
+ const char* thread_name) {
+ return base::StrCat(
+ {"Graphics.Smoothness.PercentMissedDeadlineFrames.", thread_name, ".",
+ FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
+}
+
std::string GetFrameSequenceLengthHistogramName(FrameSequenceTrackerType type) {
return base::StrCat(
{"Graphics.Smoothness.FrameSequenceLength.",
@@ -206,6 +213,10 @@
DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
DCHECK_LE(aggregated_throughput_.frames_produced,
aggregated_throughput_.frames_expected);
+ DCHECK_LE(impl_throughput_.frames_ontime, impl_throughput_.frames_expected);
+ DCHECK_LE(main_throughput_.frames_ontime, main_throughput_.frames_expected);
+ DCHECK_LE(aggregated_throughput_.frames_ontime,
+ aggregated_throughput_.frames_expected);
// Terminates |trace_data_| for all types of FrameSequenceTracker.
trace_data_.Terminate();
@@ -221,50 +232,94 @@
return;
}
+ const bool main_report = ThroughputData::CanReportHistogram(
+ this, ThreadType::kMain, main_throughput_);
+ const bool compositor_report = ThroughputData::CanReportHistogram(
+ this, ThreadType::kCompositor, impl_throughput_);
+
+ base::Optional<int> impl_throughput_percent_dropped;
+ base::Optional<int> impl_throughput_percent_missed;
+ base::Optional<int> main_throughput_percent_dropped;
+ base::Optional<int> main_throughput_percent_missed;
+
// Report the throughput metrics.
- base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram(
- this, ThreadType::kCompositor,
- GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor, type_),
- impl_throughput_);
- base::Optional<int> main_throughput_percent = ThroughputData::ReportHistogram(
- this, ThreadType::kMain,
- GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
- main_throughput_);
+ if (compositor_report) {
+ impl_throughput_percent_dropped =
+ ThroughputData::ReportDroppedFramePercentHistogram(
+ this, ThreadType::kCompositor,
+ GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor,
+ type_),
+ impl_throughput_);
+ impl_throughput_percent_missed =
+ ThroughputData::ReportMissedDeadlineFramePercentHistogram(
+ this, ThreadType::kCompositor,
+ GetIndexForMetric(FrameSequenceMetrics::ThreadType::kCompositor,
+ type_),
+ impl_throughput_);
+ }
+ if (main_report) {
+ main_throughput_percent_dropped =
+ ThroughputData::ReportDroppedFramePercentHistogram(
+ this, ThreadType::kMain,
+ GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
+ main_throughput_);
+ main_throughput_percent_missed =
+ ThroughputData::ReportMissedDeadlineFramePercentHistogram(
+ this, ThreadType::kMain,
+ GetIndexForMetric(FrameSequenceMetrics::ThreadType::kMain, type_),
+ main_throughput_);
+ }
// Report for the 'scrolling thread' for the scrolling interactions.
if (scrolling_thread_ != ThreadType::kUnknown) {
- base::Optional<int> scrolling_thread_throughput;
+ base::Optional<int> scrolling_thread_throughput_dropped;
+ base::Optional<int> scrolling_thread_throughput_missed;
switch (scrolling_thread_) {
case ThreadType::kCompositor:
- scrolling_thread_throughput = impl_throughput_percent;
+ scrolling_thread_throughput_dropped = impl_throughput_percent_dropped;
+ scrolling_thread_throughput_missed = impl_throughput_percent_missed;
break;
case ThreadType::kMain:
- scrolling_thread_throughput = main_throughput_percent;
+ scrolling_thread_throughput_dropped = main_throughput_percent_dropped;
+ scrolling_thread_throughput_missed = main_throughput_percent_missed;
break;
case ThreadType::kUnknown:
NOTREACHED();
break;
}
- if (scrolling_thread_throughput.has_value()) {
- // It's OK to use the UMA histogram in the following code while still
- // using |GetThroughputHistogramName()| to get the name of the metric,
- // since the input-params to the function never change at runtime.
+ // It's OK to use the UMA histogram in the following code while still
+ // using |GetThroughputHistogramName()| to get the name of the metric,
+ // since the input-params to the function never change at runtime.
+ if (scrolling_thread_throughput_dropped.has_value() &&
+ scrolling_thread_throughput_missed.has_value()) {
if (type_ == FrameSequenceTrackerType::kWheelScroll) {
UMA_HISTOGRAM_PERCENTAGE(
GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll,
"ScrollingThread"),
- scrolling_thread_throughput.value());
+ scrolling_thread_throughput_dropped.value());
+ UMA_HISTOGRAM_PERCENTAGE(
+ GetMissedDeadlineHistogramName(
+ FrameSequenceTrackerType::kWheelScroll, "ScrollingThread"),
+ scrolling_thread_throughput_missed.value());
} else if (type_ == FrameSequenceTrackerType::kTouchScroll) {
UMA_HISTOGRAM_PERCENTAGE(
GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll,
"ScrollingThread"),
- scrolling_thread_throughput.value());
+ scrolling_thread_throughput_dropped.value());
+ UMA_HISTOGRAM_PERCENTAGE(
+ GetMissedDeadlineHistogramName(
+ FrameSequenceTrackerType::kTouchScroll, "ScrollingThread"),
+ scrolling_thread_throughput_missed.value());
} else {
DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll);
UMA_HISTOGRAM_PERCENTAGE(
GetThroughputHistogramName(
FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
- scrolling_thread_throughput.value());
+ scrolling_thread_throughput_dropped.value());
+ UMA_HISTOGRAM_PERCENTAGE(
+ GetMissedDeadlineHistogramName(
+ FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
+ scrolling_thread_throughput_missed.value());
}
}
}
@@ -315,10 +370,9 @@
jank_reporter_->AddPresentedFrame(presentation_time, frame_interval);
}
-base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
+bool FrameSequenceMetrics::ThroughputData::CanReportHistogram(
FrameSequenceMetrics* metrics,
ThreadType thread_type,
- int metric_index,
const ThroughputData& data) {
const auto sequence_type = metrics->type();
DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
@@ -326,7 +380,26 @@
// All video frames are compositor thread only.
if (sequence_type == FrameSequenceTrackerType::kVideo &&
thread_type == ThreadType::kMain)
- return base::nullopt;
+ return false;
+
+ if (data.frames_expected < kMinFramesForThroughputMetric)
+ return false;
+
+ const bool is_animation =
+ ShouldReportForAnimation(sequence_type, thread_type);
+
+ return is_animation || IsInteractionType(sequence_type) ||
+ sequence_type == FrameSequenceTrackerType::kVideo;
+}
+
+int FrameSequenceMetrics::ThroughputData::ReportDroppedFramePercentHistogram(
+ FrameSequenceMetrics* metrics,
+ ThreadType thread_type,
+ int metric_index,
+ const ThroughputData& data) {
+ const auto sequence_type = metrics->type();
+ DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
+ DCHECK(CanReportHistogram(metrics, thread_type, data));
if (metrics->GetEffectiveThread() == thread_type) {
STATIC_HISTOGRAM_POINTER_GROUP(
@@ -339,9 +412,6 @@
base::HistogramBase::kUmaTargetedHistogramFlag));
}
- if (data.frames_expected < kMinFramesForThroughputMetric)
- return base::nullopt;
-
// Throughput means the percent of frames that was expected to show on the
// screen but didn't. In other words, the lower the throughput is, the
// smoother user experience.
@@ -393,15 +463,9 @@
}
}
- if (!is_animation && !IsInteractionType(sequence_type) &&
- sequence_type != FrameSequenceTrackerType::kVideo) {
- return base::nullopt;
- }
-
- const char* thread_name =
- thread_type == ThreadType::kCompositor
- ? "CompositorThread"
- : thread_type == ThreadType::kMain ? "MainThread" : "SlowerThread";
+ const char* thread_name = thread_type == ThreadType::kCompositor
+ ? "CompositorThread"
+ : "MainThread";
STATIC_HISTOGRAM_POINTER_GROUP(
GetThroughputHistogramName(sequence_type, thread_name), metric_index,
kMaximumHistogramIndex, Add(percent),
@@ -411,6 +475,66 @@
return percent;
}
+int FrameSequenceMetrics::ThroughputData::
+ ReportMissedDeadlineFramePercentHistogram(FrameSequenceMetrics* metrics,
+ ThreadType thread_type,
+ int metric_index,
+ const ThroughputData& data) {
+ const auto sequence_type = metrics->type();
+ DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
+
+ // Throughput means the percent of frames that was expected to show on the
+ // screen but didn't. In other words, the lower the throughput is, the
+ // smoother user experience.
+ const int percent = data.MissedDeadlineFramePercent();
+
+ const bool is_animation =
+ ShouldReportForAnimation(sequence_type, thread_type);
+ const bool is_interaction = ShouldReportForInteraction(metrics, thread_type);
+
+ if (is_animation) {
+ TRACE_EVENT_INSTANT2(
+ "cc,benchmark", "PercentMissedDeadlineFrames.AllAnimations",
+ TRACE_EVENT_SCOPE_THREAD, "frames_expected", data.frames_expected,
+ "frames_ontime", data.frames_ontime);
+
+ UMA_HISTOGRAM_PERCENTAGE(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.AllAnimations",
+ percent);
+ }
+
+ if (is_interaction) {
+ TRACE_EVENT_INSTANT2(
+ "cc,benchmark", "PercentMissedDeadlineFrames.AllInteractions",
+ TRACE_EVENT_SCOPE_THREAD, "frames_expected", data.frames_expected,
+ "frames_ontime", data.frames_ontime);
+ UMA_HISTOGRAM_PERCENTAGE(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.AllInteractions",
+ percent);
+ }
+
+ if (is_animation || is_interaction) {
+ TRACE_EVENT_INSTANT2(
+ "cc,benchmark", "PercentMissedDeadlineFrames.AllSequences",
+ TRACE_EVENT_SCOPE_THREAD, "frames_expected", data.frames_expected,
+ "frames_ontime", data.frames_ontime);
+ UMA_HISTOGRAM_PERCENTAGE(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.AllSequences",
+ percent);
+ }
+
+ const char* thread_name = thread_type == ThreadType::kCompositor
+ ? "CompositorThread"
+ : "MainThread";
+ STATIC_HISTOGRAM_POINTER_GROUP(
+ GetMissedDeadlineHistogramName(sequence_type, thread_name), metric_index,
+ kMaximumHistogramIndex, Add(percent),
+ base::LinearHistogram::FactoryGet(
+ GetMissedDeadlineHistogramName(sequence_type, thread_name), 1, 100,
+ 101, base::HistogramBase::kUmaTargetedHistogramFlag));
+ return percent;
+}
+
std::unique_ptr<base::trace_event::TracedValue>
FrameSequenceMetrics::ThroughputData::ToTracedValue(
const ThroughputData& impl,
@@ -420,9 +544,11 @@
if (effective_thread == ThreadType::kMain) {
dict->SetInteger("main-frames-produced", main.frames_produced);
dict->SetInteger("main-frames-expected", main.frames_expected);
+ dict->SetInteger("main-frames-ontime", main.frames_ontime);
} else {
dict->SetInteger("impl-frames-produced", impl.frames_produced);
dict->SetInteger("impl-frames-expected", impl.frames_expected);
+ dict->SetInteger("impl-frames-ontime", impl.frames_ontime);
}
return dict;
}
diff --git a/cc/metrics/frame_sequence_metrics.h b/cc/metrics/frame_sequence_metrics.h
index 17a8285..3725d1d 100644
--- a/cc/metrics/frame_sequence_metrics.h
+++ b/cc/metrics/frame_sequence_metrics.h
@@ -49,16 +49,27 @@
const ThroughputData& main,
ThreadType effective_thred);
- // Returns the throughput in percent, a return value of base::nullopt
- // indicates that no throughput metric is reported.
- static base::Optional<int> ReportHistogram(FrameSequenceMetrics* metrics,
- ThreadType thread_type,
- int metric_index,
- const ThroughputData& data);
+ static bool CanReportHistogram(FrameSequenceMetrics* metrics,
+ ThreadType thread_type,
+ const ThroughputData& data);
+
+ // Returns the dropped throughput in percent
+ static int ReportDroppedFramePercentHistogram(FrameSequenceMetrics* metrics,
+ ThreadType thread_type,
+ int metric_index,
+ const ThroughputData& data);
+
+ // Returns the missed deadline throughput in percent
+ static int ReportMissedDeadlineFramePercentHistogram(
+ FrameSequenceMetrics* metrics,
+ ThreadType thread_type,
+ int metric_index,
+ const ThroughputData& data);
void Merge(const ThroughputData& data) {
frames_expected += data.frames_expected;
frames_produced += data.frames_produced;
+ frames_ontime += data.frames_ontime;
#if DCHECK_IS_ON()
frames_processed += data.frames_processed;
frames_received += data.frames_received;
@@ -72,6 +83,13 @@
static_cast<double>(frames_expected));
}
+ int MissedDeadlineFramePercent() const {
+ if (frames_produced == 0)
+ return 0;
+ return std::ceil(100 * (frames_produced - frames_ontime) /
+ static_cast<double>(frames_produced));
+ }
+
// Tracks the number of frames that were expected to be shown during this
// frame-sequence.
uint32_t frames_expected = 0;
@@ -80,6 +98,10 @@
// during this frame-sequence.
uint32_t frames_produced = 0;
+ // Tracks the number of frames that were actually presented to the user
+ // that didn't miss the vsync deadline during this frame-sequence.
+ uint32_t frames_ontime = 0;
+
#if DCHECK_IS_ON()
// Tracks the number of frames that is either submitted or reported as no
// damage.
diff --git a/cc/metrics/frame_sequence_metrics_unittest.cc b/cc/metrics/frame_sequence_metrics_unittest.cc
index 6f514d1..9189f571 100644
--- a/cc/metrics/frame_sequence_metrics_unittest.cc
+++ b/cc/metrics/frame_sequence_metrics_unittest.cc
@@ -19,12 +19,15 @@
nullptr);
first.impl_throughput().frames_expected = 200u;
first.impl_throughput().frames_produced = 190u;
+ first.impl_throughput().frames_ontime = 120u;
first.aggregated_throughput().frames_expected = 170u;
first.aggregated_throughput().frames_produced = 150u;
+ first.aggregated_throughput().frames_ontime = 100u;
first.ReportMetrics();
EXPECT_EQ(first.aggregated_throughput().frames_expected, 0u);
EXPECT_EQ(first.aggregated_throughput().frames_produced, 0u);
+ EXPECT_EQ(first.aggregated_throughput().frames_ontime, 0u);
}
TEST(FrameSequenceMetricsTest, MergeMetrics) {
@@ -33,6 +36,7 @@
FrameSequenceMetrics first(FrameSequenceTrackerType::kTouchScroll, nullptr);
first.impl_throughput().frames_expected = 20;
first.impl_throughput().frames_produced = 10;
+ first.impl_throughput().frames_ontime = 5;
EXPECT_FALSE(first.HasEnoughDataForReporting());
// Create a second metric with too few frames to report any metrics.
@@ -40,6 +44,7 @@
FrameSequenceTrackerType::kTouchScroll, nullptr);
second->impl_throughput().frames_expected = 90;
second->impl_throughput().frames_produced = 60;
+ second->impl_throughput().frames_ontime = 50;
EXPECT_FALSE(second->HasEnoughDataForReporting());
// Merge the two metrics. The result should have enough frames to report
@@ -54,12 +59,14 @@
first.SetScrollingThread(FrameSequenceMetrics::ThreadType::kCompositor);
first.impl_throughput().frames_expected = 20;
first.impl_throughput().frames_produced = 10;
+ first.impl_throughput().frames_ontime = 10;
auto second = std::make_unique<FrameSequenceMetrics>(
FrameSequenceTrackerType::kTouchScroll, nullptr);
second->SetScrollingThread(FrameSequenceMetrics::ThreadType::kMain);
second->main_throughput().frames_expected = 50;
second->main_throughput().frames_produced = 10;
+ second->main_throughput().frames_ontime = 10;
ASSERT_DEATH(first.Merge(std::move(second)), "");
}
@@ -71,8 +78,10 @@
FrameSequenceMetrics first(FrameSequenceTrackerType::kVideo, nullptr);
first.impl_throughput().frames_expected = 120;
first.impl_throughput().frames_produced = 80;
+ first.impl_throughput().frames_ontime = 80;
first.main_throughput().frames_expected = 0;
first.main_throughput().frames_produced = 0;
+ first.main_throughput().frames_ontime = 0;
first.ReportMetrics();
histograms.ExpectTotalCount("Graphics.Smoothness.FrameSequenceLength.Video",
1u);
@@ -86,8 +95,10 @@
FrameSequenceMetrics first(FrameSequenceTrackerType::kTouchScroll, nullptr);
first.impl_throughput().frames_expected = 120;
first.impl_throughput().frames_produced = 80;
+ first.impl_throughput().frames_ontime = 60;
first.main_throughput().frames_expected = 20;
first.main_throughput().frames_produced = 10;
+ first.main_throughput().frames_ontime = 5;
EXPECT_TRUE(first.HasEnoughDataForReporting());
first.ReportMetrics();
@@ -98,6 +109,13 @@
1u);
histograms.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 0u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.CompositorThread."
+ "TouchScroll",
+ 1u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.MainThread.TouchScroll",
+ 0u);
// There should still be data left over for the main-thread.
EXPECT_TRUE(first.HasDataLeftForReporting());
@@ -106,7 +124,9 @@
FrameSequenceTrackerType::kTouchScroll, nullptr);
second->impl_throughput().frames_expected = 110;
second->impl_throughput().frames_produced = 100;
+ second->impl_throughput().frames_ontime = 80;
second->main_throughput().frames_expected = 90;
+ second->main_throughput().frames_ontime = 70;
first.Merge(std::move(second));
EXPECT_TRUE(first.HasEnoughDataForReporting());
first.ReportMetrics();
@@ -115,6 +135,13 @@
2u);
histograms.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 1u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.CompositorThread."
+ "TouchScroll",
+ 2u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.MainThread.TouchScroll",
+ 1u);
// All the metrics have now been reported. No data should be left over.
EXPECT_FALSE(first.HasDataLeftForReporting());
}
@@ -128,8 +155,10 @@
nullptr);
first.impl_throughput().frames_expected = 120;
first.impl_throughput().frames_produced = 80;
+ first.impl_throughput().frames_ontime = 70;
first.main_throughput().frames_expected = 120;
first.main_throughput().frames_produced = 80;
+ first.main_throughput().frames_ontime = 70;
EXPECT_TRUE(first.HasEnoughDataForReporting());
first.ReportMetrics();
@@ -146,18 +175,34 @@
"Graphics.Smoothness.PercentDroppedFrames.SlowerThread."
"CompositorAnimation",
0u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.CompositorThread."
+ "CompositorAnimation",
+ 1u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.MainThread."
+ "CompositorAnimation",
+ 0u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.SlowerThread."
+ "CompositorAnimation",
+ 0u);
// Not reported, but the data should be reset.
EXPECT_EQ(first.impl_throughput().frames_expected, 0u);
EXPECT_EQ(first.impl_throughput().frames_produced, 0u);
+ EXPECT_EQ(first.impl_throughput().frames_ontime, 0u);
EXPECT_EQ(first.main_throughput().frames_expected, 0u);
EXPECT_EQ(first.main_throughput().frames_produced, 0u);
+ EXPECT_EQ(first.main_throughput().frames_ontime, 0u);
FrameSequenceMetrics second(FrameSequenceTrackerType::kRAF, nullptr);
second.impl_throughput().frames_expected = 120;
second.impl_throughput().frames_produced = 80;
+ second.impl_throughput().frames_ontime = 70;
second.main_throughput().frames_expected = 120;
second.main_throughput().frames_produced = 80;
+ second.main_throughput().frames_ontime = 70;
EXPECT_TRUE(second.HasEnoughDataForReporting());
second.ReportMetrics();
@@ -169,6 +214,13 @@
"Graphics.Smoothness.PercentDroppedFrames.MainThread.RAF", 1u);
histograms.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.SlowerThread.RAF", 0u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.CompositorThread.RAF",
+ 0u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.MainThread.RAF", 1u);
+ histograms.ExpectTotalCount(
+ "Graphics.Smoothness.PercentMissedDeadlineFrames.SlowerThread.RAF", 0u);
}
TEST(FrameSequenceMetricsTest, ScrollingThreadMetricsReportedForInteractions) {
@@ -177,8 +229,10 @@
FrameSequenceTrackerType::kTouchScroll, nullptr);
metrics->impl_throughput().frames_expected = 100;
metrics->impl_throughput().frames_produced = 80;
+ metrics->impl_throughput().frames_ontime = 70;
metrics->main_throughput().frames_expected = 100;
metrics->main_throughput().frames_produced = 60;
+ metrics->main_throughput().frames_ontime = 50;
return metrics;
};
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index 56019f9..9e851e3d 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -361,6 +361,9 @@
DCHECK_GT(impl_throughput().frames_expected,
impl_throughput().frames_produced)
<< TRACKER_DCHECK_MSG;
+ DCHECK_GE(impl_throughput().frames_produced,
+ impl_throughput().frames_ontime)
+ << TRACKER_DCHECK_MSG;
--impl_throughput().frames_expected;
#if DCHECK_IS_ON()
++impl_throughput().frames_processed;
@@ -437,9 +440,28 @@
uint32_t impl_frames_produced = 0;
uint32_t main_frames_produced = 0;
+ uint32_t impl_frames_ontime = 0;
+ uint32_t main_frames_ontime = 0;
+
+ const auto& vsync_interval =
+ (feedback.interval.is_zero() ? viz::BeginFrameArgs::DefaultInterval()
+ : feedback.interval) *
+ 1.5;
+ DCHECK(!vsync_interval.is_zero()) << TRACKER_DCHECK_MSG;
+ base::TimeTicks safe_deadline_for_frame =
+ last_frame_presentation_timestamp_ + vsync_interval;
const bool was_presented = !feedback.failed();
if (was_presented && submitted_frame_since_last_presentation) {
+ if (!last_frame_presentation_timestamp_.is_null() &&
+ (safe_deadline_for_frame < feedback.timestamp)) {
+ DCHECK_LE(impl_throughput().frames_ontime,
+ impl_throughput().frames_produced)
+ << TRACKER_DCHECK_MSG;
+ ++impl_throughput().frames_ontime;
+ ++impl_frames_ontime;
+ }
+
DCHECK_LT(impl_throughput().frames_produced,
impl_throughput().frames_expected)
<< TRACKER_DCHECK_MSG;
@@ -474,6 +496,16 @@
metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kMain,
feedback.timestamp, feedback.interval);
}
+ if (main_frames_.size() < size_before_erase) {
+ if (!last_frame_presentation_timestamp_.is_null() &&
+ (safe_deadline_for_frame < feedback.timestamp)) {
+ DCHECK_LE(main_throughput().frames_ontime,
+ main_throughput().frames_produced)
+ << TRACKER_DCHECK_MSG;
+ ++main_throughput().frames_ontime;
+ ++main_frames_ontime;
+ }
+ }
if (impl_frames_produced > 0) {
// If there is no main frame presented, then we need to see whether or not
@@ -492,6 +524,7 @@
// frames produced so that we can apply that to aggregated throughput
// if the main frame reports no-damage later on.
impl_frames_produced_while_expecting_main_ += impl_frames_produced;
+ impl_frames_ontime_while_expecting_main_ += impl_frames_ontime;
} else {
// TODO(https://crbug.com/1066455): Determine why this DCHECK is
// causing PageLoadMetricsBrowserTests to flake, and re-enable.
@@ -499,10 +532,14 @@
// << TRACKER_DCHECK_MSG;
aggregated_throughput().frames_produced += impl_frames_produced;
impl_frames_produced_while_expecting_main_ = 0;
+ aggregated_throughput().frames_ontime += impl_frames_ontime;
+ impl_frames_ontime_while_expecting_main_ = 0;
}
} else {
aggregated_throughput().frames_produced += main_frames_produced;
impl_frames_produced_while_expecting_main_ = 0;
+ aggregated_throughput().frames_ontime += main_frames_ontime;
+ impl_frames_ontime_while_expecting_main_ = 0;
while (!expecting_main_when_submit_impl_.empty() &&
!viz::FrameTokenGT(expecting_main_when_submit_impl_.front(),
frame_token)) {
@@ -511,6 +548,8 @@
}
}
+ last_frame_presentation_timestamp_ = feedback.timestamp;
+
if (checkerboarding_.last_frame_had_checkerboarding) {
DCHECK(!checkerboarding_.last_frame_timestamp.is_null())
<< TRACKER_DCHECK_MSG;
@@ -608,6 +647,8 @@
DCHECK_GT(main_throughput().frames_expected,
main_throughput().frames_produced)
<< TRACKER_DCHECK_MSG;
+ DCHECK_GE(main_throughput().frames_produced, main_throughput().frames_ontime)
+ << TRACKER_DCHECK_MSG;
last_no_main_damage_sequence_ = args.frame_id.sequence_number;
--main_throughput().frames_expected;
// Compute the number of actually expected compositor frames during this main
@@ -634,6 +675,9 @@
aggregated_throughput().frames_produced +=
impl_frames_produced_while_expecting_main_;
impl_frames_produced_while_expecting_main_ = 0;
+ aggregated_throughput().frames_ontime +=
+ impl_frames_ontime_while_expecting_main_;
+ impl_frames_ontime_while_expecting_main_ = 0;
expecting_main_when_submit_impl_.clear();
}
diff --git a/cc/metrics/frame_sequence_tracker.h b/cc/metrics/frame_sequence_tracker.h
index 5d1d92e..3c59aea 100644
--- a/cc/metrics/frame_sequence_tracker.h
+++ b/cc/metrics/frame_sequence_tracker.h
@@ -214,6 +214,9 @@
// scheduled to report histogram.
base::TimeTicks first_frame_timestamp_;
+ // Tracks the presentation timestamp of the previous frame.
+ base::TimeTicks last_frame_presentation_timestamp_;
+
// Keeps track of whether the impl-frame being processed did not have any
// damage from the compositor (i.e. 'impl damage').
bool frame_had_no_compositor_damage_ = false;
@@ -244,6 +247,7 @@
// presented because if that main frame ends up with no-damage, then we should
// count the impl frames that were produced in the meantime.
uint32_t impl_frames_produced_while_expecting_main_ = 0;
+ uint32_t impl_frames_ontime_while_expecting_main_ = 0;
// Each entry is a frame token, inserted at ReportSubmitFrame.
base::circular_deque<uint32_t> expecting_main_when_submit_impl_;