Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / perf / metrics / smoothness.py
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
5 from metrics import Metric
6 from metrics import rendering_stats
7 from metrics import statistics
8 from telemetry.page import page_measurement
9 from telemetry.page.perf_tests_helper import FlattenList
10
11 TIMELINE_MARKER = 'Smoothness'
12
13
14 class MissingDisplayFrameRateError(page_measurement.MeasurementFailure):
15   def __init__(self, name):
16     super(MissingDisplayFrameRateError, self).__init__(
17         'Missing display frame rate metrics: ' + name)
18
19 class NotEnoughFramesError(page_measurement.MeasurementFailure):
20   def __init__(self):
21     super(NotEnoughFramesError, self).__init__(
22         'Page output less than two frames')
23
24
25 class NoSupportedActionError(page_measurement.MeasurementFailure):
26   def __init__(self):
27     super(NoSupportedActionError, self).__init__(
28         'None of the actions is supported by smoothness measurement')
29
30
31 def _GetSyntheticDelayCategoriesFromPage(page):
32   if not hasattr(page, 'synthetic_delays'):
33     return []
34   result = []
35   for delay, options in page.synthetic_delays.items():
36     options = '%f;%s' % (options.get('target_duration', 0),
37                          options.get('mode', 'static'))
38     result.append('DELAY(%s;%s)' % (delay, options))
39   return result
40
41
42 class SmoothnessMetric(Metric):
43   def __init__(self):
44     super(SmoothnessMetric, self).__init__()
45     self._stats = None
46     self._actions = []
47
48   def AddActionToIncludeInMetric(self, action):
49     self._actions.append(action)
50
51   def Start(self, page, tab):
52     custom_categories = ['webkit.console', 'benchmark']
53     custom_categories += _GetSyntheticDelayCategoriesFromPage(page)
54     tab.browser.StartTracing(','.join(custom_categories), 60)
55     tab.ExecuteJavaScript('console.time("' + TIMELINE_MARKER + '")')
56     if tab.browser.platform.IsRawDisplayFrameRateSupported():
57       tab.browser.platform.StartRawDisplayFrameRateMeasurement()
58
59   def Stop(self, page, tab):
60     if tab.browser.platform.IsRawDisplayFrameRateSupported():
61       tab.browser.platform.StopRawDisplayFrameRateMeasurement()
62     tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")')
63     timeline_model = tab.browser.StopTracing().AsTimelineModel()
64     timeline_ranges = [ action.GetActiveRangeOnTimeline(timeline_model)
65                         for action in self._actions ]
66
67     renderer_process = timeline_model.GetRendererProcessFromTab(tab)
68     self._stats = rendering_stats.RenderingStats(
69         renderer_process, timeline_model.browser_process, timeline_ranges)
70
71     if not self._stats.frame_times:
72       raise NotEnoughFramesError()
73
74   def SetStats(self, stats):
75     """ Pass in a RenderingStats object directly. For unittests that don't call
76         Start/Stop.
77     """
78     self._stats = stats
79
80   def AddResults(self, tab, results):
81     if self._stats.mouse_wheel_scroll_latency:
82       mean_mouse_wheel_scroll_latency = statistics.ArithmeticMean(
83           self._stats.mouse_wheel_scroll_latency,
84           len(self._stats.mouse_wheel_scroll_latency))
85       results.Add('mean_mouse_wheel_scroll_latency', 'ms',
86                   round(mean_mouse_wheel_scroll_latency, 3))
87
88     if self._stats.touch_scroll_latency:
89       mean_touch_scroll_latency = statistics.ArithmeticMean(
90           self._stats.touch_scroll_latency,
91           len(self._stats.touch_scroll_latency))
92       results.Add('mean_touch_scroll_latency', 'ms',
93                   round(mean_touch_scroll_latency, 3))
94
95     if self._stats.js_touch_scroll_latency:
96       mean_js_touch_scroll_latency = statistics.ArithmeticMean(
97           self._stats.js_touch_scroll_latency,
98           len(self._stats.js_touch_scroll_latency))
99       results.Add('mean_js_touch_scroll_latency', 'ms',
100                   round(mean_js_touch_scroll_latency, 3))
101
102     # List of raw frame times.
103     frame_times = FlattenList(self._stats.frame_times)
104     results.Add('frame_times', 'ms', frame_times)
105
106     # Arithmetic mean of frame times.
107     mean_frame_time = statistics.ArithmeticMean(frame_times,
108                                                 len(frame_times))
109     results.Add('mean_frame_time', 'ms', round(mean_frame_time, 3))
110
111     # Absolute discrepancy of frame time stamps.
112     jank = statistics.FrameDiscrepancy(self._stats.frame_timestamps)
113     results.Add('jank', '', round(jank, 4))
114
115     # Are we hitting 60 fps for 95 percent of all frames?
116     # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
117     percentile_95 = statistics.Percentile(frame_times, 95.0)
118     results.Add('mostly_smooth', 'score', 1.0 if percentile_95 < 19.0 else 0.0)
119
120     if tab.browser.platform.IsRawDisplayFrameRateSupported():
121       for r in tab.browser.platform.GetRawDisplayFrameRateMeasurements():
122         if r.value is None:
123           raise MissingDisplayFrameRateError(r.name)
124         results.Add(r.name, r.unit, r.value)