• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from metrics import Metric
6from metrics import rendering_stats
7from metrics import statistics
8from telemetry.core.timeline.model import MarkerMismatchError
9from telemetry.core.timeline.model import MarkerOverlapError
10from telemetry.page import page_measurement
11
12TIMELINE_MARKER = 'Smoothness'
13
14
15class NotEnoughFramesError(page_measurement.MeasurementFailure):
16  def __init__(self):
17    super(NotEnoughFramesError, self).__init__(
18        'Page output less than two frames')
19
20
21class NoSupportedActionError(page_measurement.MeasurementFailure):
22  def __init__(self):
23    super(NoSupportedActionError, self).__init__(
24        'None of the actions is supported by smoothness measurement')
25
26
27class SmoothnessMetric(Metric):
28  def __init__(self):
29    super(SmoothnessMetric, self).__init__()
30    self._stats = None
31    self._timeline_marker_names = []
32
33  def AddTimelineMarkerNameToIncludeInMetric(self, timeline_marker_name):
34    self._timeline_marker_names.append(timeline_marker_name)
35
36  def Start(self, page, tab):
37    tab.browser.StartTracing('webkit.console,benchmark', 60)
38    tab.ExecuteJavaScript('console.time("' + TIMELINE_MARKER + '")')
39
40  def Stop(self, page, tab):
41    tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")')
42    timeline_model = tab.browser.StopTracing().AsTimelineModel()
43    try:
44      timeline_markers = timeline_model.FindTimelineMarkers(
45          self._timeline_marker_names)
46    except MarkerMismatchError as e:
47      raise page_measurement.MeasurementFailure(str(e))
48    except MarkerOverlapError as e:
49      raise page_measurement.MeasurementFailure(str(e))
50
51    renderer_process = timeline_model.GetRendererProcessFromTab(tab)
52    self._stats = rendering_stats.RenderingStats(
53        renderer_process, timeline_markers)
54
55    if not self._stats.frame_times:
56      raise NotEnoughFramesError()
57
58  def SetStats(self, stats):
59    """ Pass in a RenderingStats object directly. For unittests that don't call
60        Start/Stop.
61    """
62    self._stats = stats
63
64  def AddResults(self, tab, results):
65    # List of raw frame times.
66    results.Add('frame_times', 'ms', self._stats.frame_times)
67
68    # Arithmetic mean of frame times.
69    mean_frame_time = statistics.ArithmeticMean(self._stats.frame_times,
70                                                len(self._stats.frame_times))
71    results.Add('mean_frame_time', 'ms', round(mean_frame_time, 3))
72
73    # Absolute discrepancy of frame time stamps.
74    jank = statistics.FrameDiscrepancy(self._stats.frame_timestamps)
75    results.Add('jank', '', round(jank, 4))
76
77    # Are we hitting 60 fps for 95 percent of all frames?
78    # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
79    percentile_95 = statistics.Percentile(self._stats.frame_times, 95.0)
80    results.Add('mostly_smooth', '', 1.0 if percentile_95 < 19.0 else 0.0)
81