| 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 |