| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import time | 5 import time |
| 6 | 6 |
| 7 from metrics import smoothness | 7 from metrics import smoothness |
| 8 from metrics.rendering_stats import RenderingStats |
| 8 from telemetry.page import page_measurement | 9 from telemetry.page import page_measurement |
| 9 | 10 |
| 10 class StatsCollector(object): | |
| 11 def __init__(self, timeline): | |
| 12 """ | |
| 13 Utility class for collecting rendering stats from timeline model. | |
| 14 | |
| 15 timeline -- The timeline model | |
| 16 """ | |
| 17 self.timeline = timeline | |
| 18 self.total_best_rasterize_time = 0 | |
| 19 self.total_best_record_time = 0 | |
| 20 self.total_pixels_rasterized = 0 | |
| 21 self.total_pixels_recorded = 0 | |
| 22 self.trigger_event = self.FindTriggerEvent() | |
| 23 self.renderer_process = self.trigger_event.start_thread.parent | |
| 24 | |
| 25 def FindTriggerEvent(self): | |
| 26 events = [s for | |
| 27 s in self.timeline.GetAllEventsOfName( | |
| 28 'measureNextFrame') | |
| 29 if s.parent_slice == None] | |
| 30 if len(events) != 1: | |
| 31 raise LookupError, 'no measureNextFrame event found' | |
| 32 return events[0] | |
| 33 | |
| 34 def FindFrameNumber(self, trigger_time): | |
| 35 start_event = None | |
| 36 for event in self.renderer_process.IterAllSlicesOfName( | |
| 37 "LayerTreeHost::UpdateLayers"): | |
| 38 if event.start > trigger_time: | |
| 39 if start_event == None: | |
| 40 start_event = event | |
| 41 elif event.start < start_event.start: | |
| 42 start_event = event | |
| 43 if start_event is None: | |
| 44 raise LookupError, \ | |
| 45 'no LayterTreeHost::UpdateLayers after measureNextFrame found' | |
| 46 return start_event.args["source_frame_number"] | |
| 47 | |
| 48 def GatherRasterizeStats(self, frame_number): | |
| 49 for event in self.renderer_process.IterAllSlicesOfName( | |
| 50 "RasterWorkerPoolTaskImpl::RunRasterOnThread"): | |
| 51 if event.args["data"]["source_frame_number"] == frame_number and \ | |
| 52 event.args['data']['resolution'] == 'HIGH_RESOLUTION': | |
| 53 for raster_loop_event in event.GetAllSubSlicesOfName("RasterLoop"): | |
| 54 best_rasterize_time = float("inf") | |
| 55 for raster_event in raster_loop_event.GetAllSubSlicesOfName( | |
| 56 "Picture::Raster"): | |
| 57 if "num_pixels_rasterized" in raster_event.args: | |
| 58 best_rasterize_time = min(best_rasterize_time, | |
| 59 raster_event.duration) | |
| 60 self.total_pixels_rasterized += \ | |
| 61 raster_event.args["num_pixels_rasterized"] | |
| 62 if best_rasterize_time == float('inf'): | |
| 63 best_rasterize_time = 0 | |
| 64 self.total_best_rasterize_time += best_rasterize_time | |
| 65 | |
| 66 def GatherRecordStats(self, frame_number): | |
| 67 for event in self.renderer_process.IterAllSlicesOfName( | |
| 68 "PictureLayer::Update"): | |
| 69 if event.args["source_frame_number"] == frame_number: | |
| 70 for record_loop_event in event.GetAllSubSlicesOfName("RecordLoop"): | |
| 71 best_record_time = float('inf') | |
| 72 for record_event in record_loop_event.GetAllSubSlicesOfName( | |
| 73 "Picture::Record"): | |
| 74 best_record_time = min(best_record_time, record_event.duration) | |
| 75 self.total_pixels_recorded += ( | |
| 76 record_event.args["data"]["width"] * | |
| 77 record_event.args["data"]["height"]) | |
| 78 if best_record_time == float('inf'): | |
| 79 best_record_time = 0 | |
| 80 self.total_best_record_time += best_record_time | |
| 81 | |
| 82 def GatherRenderingStats(self): | |
| 83 trigger_time = self.trigger_event.start | |
| 84 frame_number = self.FindFrameNumber(trigger_time) | |
| 85 self.GatherRasterizeStats(frame_number) | |
| 86 self.GatherRecordStats(frame_number) | |
| 87 | |
| 88 def DivideIfPossibleOrZero(numerator, denominator): | |
| 89 if denominator == 0: | |
| 90 return 0 | |
| 91 return numerator / denominator | |
| 92 | |
| 93 class RasterizeAndRecord(page_measurement.PageMeasurement): | 11 class RasterizeAndRecord(page_measurement.PageMeasurement): |
| 94 def __init__(self): | 12 def __init__(self): |
| 95 super(RasterizeAndRecord, self).__init__('', True) | 13 super(RasterizeAndRecord, self).__init__('', True) |
| 96 self._metrics = None | 14 self._metrics = None |
| 97 | 15 |
| 98 def AddCommandLineOptions(self, parser): | 16 def AddCommandLineOptions(self, parser): |
| 99 parser.add_option('--report-all-results', dest='report_all_results', | |
| 100 action='store_true', | |
| 101 help='Reports all data collected') | |
| 102 parser.add_option('--raster-record-repeat', dest='raster_record_repeat', | 17 parser.add_option('--raster-record-repeat', dest='raster_record_repeat', |
| 103 default=20, | 18 default=20, |
| 104 help='Repetitions in raster and record loops.' + | 19 help='Repetitions in raster and record loops.' + |
| 105 'Higher values reduce variance, but can cause' + | 20 'Higher values reduce variance, but can cause' + |
| 106 'instability (timeouts, event buffer overflows, etc.).') | 21 'instability (timeouts, event buffer overflows, etc.).') |
| 107 parser.add_option('--start-wait-time', dest='start_wait_time', | 22 parser.add_option('--start-wait-time', dest='start_wait_time', |
| 108 default=2, | 23 default=2, |
| 109 help='Wait time before the benchmark is started ' + | 24 help='Wait time before the benchmark is started ' + |
| 110 '(must be long enought to load all content)') | 25 '(must be long enought to load all content)') |
| 111 parser.add_option('--stop-wait-time', dest='stop_wait_time', | 26 parser.add_option('--stop-wait-time', dest='stop_wait_time', |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 # first frame requested has more variance in the number of pixels | 58 # first frame requested has more variance in the number of pixels |
| 144 # rasterized. | 59 # rasterized. |
| 145 tab.ExecuteJavaScript(""" | 60 tab.ExecuteJavaScript(""" |
| 146 window.__rafFired = false; | 61 window.__rafFired = false; |
| 147 window.webkitRequestAnimationFrame(function() { | 62 window.webkitRequestAnimationFrame(function() { |
| 148 chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers(); | 63 chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers(); |
| 149 window.__rafFired = true; | 64 window.__rafFired = true; |
| 150 }); | 65 }); |
| 151 """) | 66 """) |
| 152 | 67 |
| 68 time.sleep(float(self.options.stop_wait_time)) |
| 153 tab.browser.StartTracing('webkit.console,benchmark', 60) | 69 tab.browser.StartTracing('webkit.console,benchmark', 60) |
| 154 self._metrics.Start() | 70 self._metrics.Start() |
| 155 | 71 |
| 156 tab.ExecuteJavaScript(""" | 72 tab.ExecuteJavaScript(""" |
| 157 window.__rafFired = false; | 73 window.__rafFired = false; |
| 158 window.webkitRequestAnimationFrame(function() { | 74 window.webkitRequestAnimationFrame(function() { |
| 75 chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers(); |
| 159 console.time("measureNextFrame"); | 76 console.time("measureNextFrame"); |
| 160 chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers(); | |
| 161 window.__rafFired = true; | 77 window.__rafFired = true; |
| 162 }); | 78 }); |
| 163 """) | 79 """) |
| 164 # Wait until the frame was drawn. | 80 # Wait until the frame was drawn. |
| 165 # Needs to be adjusted for every device and for different | 81 # Needs to be adjusted for every device and for different |
| 166 # raster_record_repeat counts. | 82 # raster_record_repeat counts. |
| 167 # TODO(ernstm): replace by call-back. | 83 # TODO(ernstm): replace by call-back. |
| 168 time.sleep(float(self.options.stop_wait_time)) | 84 time.sleep(float(self.options.stop_wait_time)) |
| 169 tab.ExecuteJavaScript('console.timeEnd("measureNextFrame")') | 85 tab.ExecuteJavaScript('console.timeEnd("measureNextFrame")') |
| 170 | 86 |
| 171 self._metrics.Stop() | 87 self._metrics.Stop() |
| 88 rendering_stats_deltas = self._metrics.deltas |
| 172 timeline = tab.browser.StopTracing().AsTimelineModel() | 89 timeline = tab.browser.StopTracing().AsTimelineModel() |
| 90 timeline_marker = smoothness.FindTimelineMarker(timeline, |
| 91 'measureNextFrame') |
| 92 benchmark_stats = RenderingStats(timeline_marker, |
| 93 timeline_marker, |
| 94 rendering_stats_deltas, |
| 95 self._metrics.is_using_gpu_benchmarking) |
| 173 | 96 |
| 174 collector = StatsCollector(timeline) | 97 results.Add('rasterize_time', 'ms', |
| 175 collector.GatherRenderingStats() | 98 max(benchmark_stats.rasterize_time) * 1000.0) |
| 176 | 99 results.Add('record_time', 'ms', |
| 177 rendering_stats = self._metrics.end_values | 100 max(benchmark_stats.record_time) * 1000.0) |
| 178 | 101 results.Add('rasterized_pixels', 'pixels', |
| 179 results.Add('best_rasterize_time', 'seconds', | 102 max(benchmark_stats.rasterized_pixel_count)) |
| 180 collector.total_best_rasterize_time, | 103 results.Add('recorded_pixels', 'pixels', |
| 181 data_type='unimportant') | 104 max(benchmark_stats.recorded_pixel_count)) |
| 182 results.Add('best_record_time', 'seconds', | |
| 183 collector.total_best_record_time, | |
| 184 data_type='unimportant') | |
| 185 results.Add('total_pixels_rasterized', 'pixels', | |
| 186 collector.total_pixels_rasterized, | |
| 187 data_type='unimportant') | |
| 188 results.Add('total_pixels_recorded', 'pixels', | |
| 189 collector.total_pixels_recorded, | |
| 190 data_type='unimportant') | |
| 191 | |
| 192 if self.options.report_all_results: | |
| 193 for k, v in rendering_stats.iteritems(): | |
| 194 results.Add(k, '', v) | |
| OLD | NEW |