OLD | NEW |
| (Empty) |
1 # Copyright (c) 2012 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 from perf_tools import smoothness_metrics | |
5 from telemetry.core import util | |
6 from telemetry.page import page_measurement | |
7 | |
8 class DidNotScrollException(page_measurement.MeasurementFailure): | |
9 def __init__(self): | |
10 super(DidNotScrollException, self).__init__('Page did not scroll') | |
11 | |
12 class MissingDisplayFrameRate(page_measurement.MeasurementFailure): | |
13 def __init__(self): | |
14 super(MissingDisplayFrameRate, self).__init__( | |
15 'Missing display frame rate metrics') | |
16 | |
17 def DivideIfPossibleOrZero(numerator, denominator): | |
18 if denominator == 0: | |
19 return 0 | |
20 return numerator / denominator | |
21 | |
22 def CalcScrollResults(rendering_stats_deltas, results): | |
23 num_frames_sent_to_screen = rendering_stats_deltas['numFramesSentToScreen'] | |
24 | |
25 mean_frame_time_seconds = ( | |
26 rendering_stats_deltas['totalTimeInSeconds'] / | |
27 float(num_frames_sent_to_screen)) | |
28 | |
29 dropped_percent = ( | |
30 rendering_stats_deltas['droppedFrameCount'] / | |
31 float(num_frames_sent_to_screen)) | |
32 | |
33 num_impl_thread_scrolls = rendering_stats_deltas.get( | |
34 'numImplThreadScrolls', 0) | |
35 num_main_thread_scrolls = rendering_stats_deltas.get( | |
36 'numMainThreadScrolls', 0) | |
37 | |
38 percent_impl_scrolled = DivideIfPossibleOrZero( | |
39 float(num_impl_thread_scrolls), | |
40 num_impl_thread_scrolls + num_main_thread_scrolls) | |
41 | |
42 num_layers = ( | |
43 rendering_stats_deltas.get('numLayersDrawn', 0) / | |
44 float(num_frames_sent_to_screen)) | |
45 | |
46 num_missing_tiles = ( | |
47 rendering_stats_deltas.get('numMissingTiles', 0) / | |
48 float(num_frames_sent_to_screen)) | |
49 | |
50 results.Add('mean_frame_time', 'ms', round(mean_frame_time_seconds * 1000, 3)) | |
51 results.Add('dropped_percent', '%', round(dropped_percent * 100, 1), | |
52 data_type='unimportant') | |
53 results.Add('percent_impl_scrolled', '%', | |
54 round(percent_impl_scrolled * 100, 1), | |
55 data_type='unimportant') | |
56 results.Add('average_num_layers_drawn', '', round(num_layers, 1), | |
57 data_type='unimportant') | |
58 results.Add('average_num_missing_tiles', '', round(num_missing_tiles, 1), | |
59 data_type='unimportant') | |
60 | |
61 def CalcTextureUploadResults(rendering_stats_deltas, results): | |
62 if (('totalCommitCount' not in rendering_stats_deltas) | |
63 or rendering_stats_deltas['totalCommitCount'] == 0) : | |
64 averageCommitTimeMs = 0 | |
65 else : | |
66 averageCommitTimeMs = ( | |
67 1000 * rendering_stats_deltas['totalCommitTimeInSeconds'] / | |
68 rendering_stats_deltas['totalCommitCount']) | |
69 | |
70 results.Add('texture_upload_count', 'count', | |
71 rendering_stats_deltas.get('textureUploadCount', 0)) | |
72 results.Add('total_texture_upload_time', 'seconds', | |
73 rendering_stats_deltas.get('totalTextureUploadTimeInSeconds', 0)) | |
74 results.Add('average_commit_time', 'ms', averageCommitTimeMs, | |
75 data_type='unimportant') | |
76 | |
77 def CalcFirstPaintTimeResults(results, tab): | |
78 if tab.browser.is_content_shell: | |
79 results.Add('first_paint', 'ms', 'unsupported') | |
80 return | |
81 | |
82 tab.ExecuteJavaScript(""" | |
83 window.__rafFired = false; | |
84 window.webkitRequestAnimationFrame(function() { | |
85 window.__rafFired = true; | |
86 }); | |
87 """) | |
88 util.WaitFor(lambda: tab.EvaluateJavaScript('window.__rafFired'), 60) | |
89 | |
90 first_paint_secs = tab.EvaluateJavaScript( | |
91 'window.chrome.loadTimes().firstPaintTime - ' + | |
92 'window.chrome.loadTimes().startLoadTime') | |
93 | |
94 results.Add('first_paint', 'ms', round(first_paint_secs * 1000, 1)) | |
95 | |
96 def CalcImageDecodingResults(rendering_stats_deltas, results): | |
97 totalDeferredImageDecodeCount = rendering_stats_deltas.get( | |
98 'totalDeferredImageDecodeCount', 0) | |
99 totalDeferredImageCacheHitCount = rendering_stats_deltas.get( | |
100 'totalDeferredImageCacheHitCount', 0) | |
101 totalImageGatheringCount = rendering_stats_deltas.get( | |
102 'totalImageGatheringCount', 0) | |
103 totalDeferredImageDecodeTimeInSeconds = rendering_stats_deltas.get( | |
104 'totalDeferredImageDecodeTimeInSeconds', 0) | |
105 totalImageGatheringTimeInSeconds = rendering_stats_deltas.get( | |
106 'totalImageGatheringTimeInSeconds', 0) | |
107 | |
108 averageImageGatheringTime = DivideIfPossibleOrZero( | |
109 (totalImageGatheringTimeInSeconds * 1000), totalImageGatheringCount) | |
110 | |
111 results.Add('total_deferred_image_decode_count', 'count', | |
112 totalDeferredImageDecodeCount, | |
113 data_type='unimportant') | |
114 results.Add('total_image_cache_hit_count', 'count', | |
115 totalDeferredImageCacheHitCount, | |
116 data_type='unimportant') | |
117 results.Add('average_image_gathering_time', 'ms', averageImageGatheringTime, | |
118 data_type='unimportant') | |
119 results.Add('total_deferred_image_decoding_time', 'seconds', | |
120 totalDeferredImageDecodeTimeInSeconds, | |
121 data_type='unimportant') | |
122 | |
123 def CalcAnalysisResults(rendering_stats_deltas, results): | |
124 totalTilesAnalyzed = rendering_stats_deltas.get( | |
125 'totalTilesAnalyzed', 0) | |
126 solidColorTilesAnalyzed = rendering_stats_deltas.get( | |
127 'solidColorTilesAnalyzed', 0) | |
128 totalTileAnalysisTimeInSeconds = rendering_stats_deltas.get( | |
129 'totalTileAnalysisTimeInSeconds', 0) | |
130 | |
131 averageAnalysisTimeMS = \ | |
132 1000 * DivideIfPossibleOrZero(totalTileAnalysisTimeInSeconds, | |
133 totalTilesAnalyzed) | |
134 | |
135 results.Add('total_tiles_analyzed', 'count', | |
136 totalTilesAnalyzed, | |
137 data_type='unimportant') | |
138 results.Add('solid_color_tiles_analyzed', 'count', | |
139 solidColorTilesAnalyzed, | |
140 data_type='unimportant') | |
141 results.Add('average_tile_analysis_time', 'ms', | |
142 averageAnalysisTimeMS, | |
143 data_type='unimportant') | |
144 | |
145 def CalcLatencyResults(rendering_stats_deltas, results): | |
146 inputEventCount = rendering_stats_deltas.get( | |
147 'inputEventCount', 0) | |
148 totalInputLatencyInSeconds = rendering_stats_deltas.get( | |
149 'totalInputLatency', 0) | |
150 | |
151 averageLatency = DivideIfPossibleOrZero( | |
152 (totalInputLatencyInSeconds * 1000), inputEventCount) | |
153 | |
154 results.Add('average_latency', 'ms', averageLatency, | |
155 data_type='unimportant') | |
156 | |
157 | |
158 class SmoothnessMeasurement(page_measurement.PageMeasurement): | |
159 def __init__(self): | |
160 super(SmoothnessMeasurement, self).__init__('smoothness') | |
161 self.force_enable_threaded_compositing = False | |
162 self.use_gpu_benchmarking_extension = True | |
163 self._metrics = None | |
164 | |
165 def AddCommandLineOptions(self, parser): | |
166 parser.add_option('--report-all-results', dest='report_all_results', | |
167 action='store_true', | |
168 help='Reports all data collected, not just FPS') | |
169 | |
170 def CustomizeBrowserOptions(self, options): | |
171 if self.use_gpu_benchmarking_extension: | |
172 options.extra_browser_args.append('--enable-gpu-benchmarking') | |
173 if self.force_enable_threaded_compositing: | |
174 options.extra_browser_args.append('--enable-threaded-compositing') | |
175 | |
176 def CanRunForPage(self, page): | |
177 return hasattr(page, 'smoothness') | |
178 | |
179 def WillRunAction(self, page, tab, action): | |
180 if tab.browser.platform.IsRawDisplayFrameRateSupported(): | |
181 tab.browser.platform.StartRawDisplayFrameRateMeasurement() | |
182 self._metrics = smoothness_metrics.SmoothnessMetrics(tab) | |
183 if action.CanBeBound(): | |
184 self._metrics.BindToAction(action) | |
185 else: | |
186 self._metrics.Start() | |
187 | |
188 def DidRunAction(self, page, tab, action): | |
189 if tab.browser.platform.IsRawDisplayFrameRateSupported(): | |
190 tab.browser.platform.StopRawDisplayFrameRateMeasurement() | |
191 if not action.CanBeBound(): | |
192 self._metrics.Stop() | |
193 | |
194 def MeasurePage(self, page, tab, results): | |
195 rendering_stats_deltas = self._metrics.deltas | |
196 | |
197 if not (rendering_stats_deltas['numFramesSentToScreen'] > 0): | |
198 raise DidNotScrollException() | |
199 | |
200 load_timings = tab.EvaluateJavaScript("window.performance.timing") | |
201 load_time_seconds = ( | |
202 float(load_timings['loadEventStart']) - | |
203 load_timings['navigationStart']) / 1000 | |
204 dom_content_loaded_time_seconds = ( | |
205 float(load_timings['domContentLoadedEventStart']) - | |
206 load_timings['navigationStart']) / 1000 | |
207 results.Add('load_time', 'seconds', load_time_seconds) | |
208 results.Add('dom_content_loaded_time', 'seconds', | |
209 dom_content_loaded_time_seconds) | |
210 | |
211 CalcFirstPaintTimeResults(results, tab) | |
212 CalcScrollResults(rendering_stats_deltas, results) | |
213 CalcTextureUploadResults(rendering_stats_deltas, results) | |
214 CalcImageDecodingResults(rendering_stats_deltas, results) | |
215 CalcAnalysisResults(rendering_stats_deltas, results) | |
216 CalcLatencyResults(rendering_stats_deltas, results) | |
217 | |
218 if self.options.report_all_results: | |
219 for k, v in rendering_stats_deltas.iteritems(): | |
220 results.Add(k, '', v) | |
221 | |
222 if tab.browser.platform.IsRawDisplayFrameRateSupported(): | |
223 for r in tab.browser.platform.GetRawDisplayFrameRateMeasurements(): | |
224 if not r.value: | |
225 raise MissingDisplayFrameRate() | |
226 results.Add(r.name, r.unit, r.value) | |
OLD | NEW |