def MeasurePage(self, page, tab, results): # Exit if threaded forced compositing is not enabled. if (not self._compositing_features_enabled): logging.warning('Warning: compositing feature status unknown or not '+ 'forced and threaded. Skipping measurement.') sys.exit(0) # Rasterize only what's visible. tab.ExecuteJavaScript( 'chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent();') # Wait until the page has loaded and come to a somewhat steady state. # Needs to be adjusted for every device (~2 seconds for workstation). time.sleep(float(self.options.start_wait_time)) # Render one frame before we start gathering a trace. On some pages, the # first frame requested has more variance in the number of pixels # rasterized. tab.ExecuteJavaScript( 'window.__rafFired = false;' 'window.webkitRequestAnimationFrame(function() {' 'chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers();' 'window.__rafFired = true;' '});') time.sleep(float(self.options.stop_wait_time)) tab.browser.StartTracing('webkit.console,benchmark', 60) tab.ExecuteJavaScript( 'window.__rafFired = false;' 'window.webkitRequestAnimationFrame(function() {' 'chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers();' 'console.time("' + TIMELINE_MARKER + '");' 'window.__rafFired = true;' '});') # Wait until the frame was drawn. # Needs to be adjusted for every device and for different # raster_record_repeat counts. # TODO(ernstm): replace by call-back. time.sleep(float(self.options.stop_wait_time)) tab.ExecuteJavaScript( 'console.timeEnd("' + TIMELINE_MARKER + '")') timeline = tab.browser.StopTracing().AsTimelineModel() try: timeline_markers = timeline.FindTimelineMarkers(TIMELINE_MARKER) except (MarkerMismatchError, MarkerOverlapError) as e: raise page_measurement.MeasurementFailure(str(e)) renderer_process = timeline.GetRendererProcessFromTab(tab) stats = rendering_stats.RenderingStats(renderer_process, timeline_markers) results.Add('rasterize_time', 'ms', max(stats.rasterize_time)) results.Add('record_time', 'ms', max(stats.record_time)) results.Add('rasterized_pixels', 'pixels', max(stats.rasterized_pixel_count)) results.Add('recorded_pixels', 'pixels', max(stats.recorded_pixel_count))
def AddResults(self, model, renderer_thread, interaction_record, results): renderer_process = renderer_thread.parent time_bounds = bounds.Bounds() time_bounds.AddValue(interaction_record.start) time_bounds.AddValue(interaction_record.end) stats = rendering_stats.RenderingStats(renderer_process, model.browser_process, [time_bounds]) if stats.mouse_wheel_scroll_latency: mean_mouse_wheel_scroll_latency = statistics.ArithmeticMean( stats.mouse_wheel_scroll_latency) mouse_wheel_scroll_latency_discrepancy = statistics.DurationsDiscrepancy( stats.mouse_wheel_scroll_latency) results.Add('mean_mouse_wheel_scroll_latency', 'ms', round(mean_mouse_wheel_scroll_latency, 3)) results.Add('mouse_wheel_scroll_latency_discrepancy', '', round(mouse_wheel_scroll_latency_discrepancy, 4)) if stats.touch_scroll_latency: mean_touch_scroll_latency = statistics.ArithmeticMean( stats.touch_scroll_latency) touch_scroll_latency_discrepancy = statistics.DurationsDiscrepancy( stats.touch_scroll_latency) results.Add('mean_touch_scroll_latency', 'ms', round(mean_touch_scroll_latency, 3)) results.Add('touch_scroll_latency_discrepancy', '', round(touch_scroll_latency_discrepancy, 4)) if stats.js_touch_scroll_latency: mean_js_touch_scroll_latency = statistics.ArithmeticMean( stats.js_touch_scroll_latency) js_touch_scroll_latency_discrepancy = statistics.DurationsDiscrepancy( stats.js_touch_scroll_latency) results.Add('mean_js_touch_scroll_latency', 'ms', round(mean_js_touch_scroll_latency, 3)) results.Add('js_touch_scroll_latency_discrepancy', '', round(js_touch_scroll_latency_discrepancy, 4)) # List of raw frame times. frame_times = FlattenList(stats.frame_times) results.Add('frame_times', 'ms', frame_times) # Arithmetic mean of frame times. mean_frame_time = statistics.ArithmeticMean(frame_times) results.Add('mean_frame_time', 'ms', round(mean_frame_time, 3)) # Absolute discrepancy of frame time stamps. frame_discrepancy = statistics.TimestampsDiscrepancy( stats.frame_timestamps) results.Add('jank', 'ms', round(frame_discrepancy, 4)) # Are we hitting 60 fps for 95 percent of all frames? # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0. percentile_95 = statistics.Percentile(frame_times, 95.0) results.Add('mostly_smooth', 'score', 1.0 if percentile_95 < 19.0 else 0.0)
def Stop(self, page, tab): if tab.browser.platform.IsRawDisplayFrameRateSupported(): tab.browser.platform.StopRawDisplayFrameRateMeasurement() tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")') timeline_model = tab.browser.StopTracing().AsTimelineModel() timeline_ranges = [ action.GetActiveRangeOnTimeline(timeline_model) for action in self._actions ] renderer_process = timeline_model.GetRendererProcessFromTab(tab) self._stats = rendering_stats.RenderingStats( renderer_process, timeline_model.browser_process, timeline_ranges) if not self._stats.frame_times: raise NotEnoughFramesError()
def Stop(self, page, tab): tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")') timeline_model = tab.browser.StopTracing().AsTimelineModel() try: timeline_markers = timeline_model.FindTimelineMarkers( self._timeline_marker_names) except MarkerMismatchError as e: raise page_measurement.MeasurementFailure(str(e)) except MarkerOverlapError as e: raise page_measurement.MeasurementFailure(str(e)) renderer_process = timeline_model.GetRendererProcessFromTab(tab) self._stats = rendering_stats.RenderingStats(renderer_process, timeline_markers) if not self._stats.frame_times: raise NotEnoughFramesError()
def Stop(self, page, tab): tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")') timeline_model = tab.browser.StopTracing().AsTimelineModel() render_process_marker = timeline_model.FindTimelineMarkers(TIMELINE_MARKER) timeline_marker_labels = GetTimelineMarkerLabelsFromAction( self._compound_action) try: timeline_markers = timeline_model.FindTimelineMarkers( timeline_marker_labels) except MarkerMismatchError: # TODO(ernstm): re-raise exception as MeasurementFailure when the # reference build was updated. timeline_markers = render_process_marker except MarkerOverlapError as e: raise page_measurement.MeasurementFailure(str(e)) self._stats = rendering_stats.RenderingStats( render_process_marker, timeline_markers) if not self._stats.frame_times: raise NotEnoughFramesError()
def testCalcResultsTraceEvents(self): # Make the test repeatable by seeding the random number generator # (which is used by the mock timer) with a constant number. random.seed(1234567) mock_timer = MockTimer() trace_events = [] total_time_seconds = 0.0 num_frames_sent = 0.0 first_frame = True previous_frame_time = None # This list represents time differences between frames in milliseconds. expected_frame_times = [] # Append start trace events for the timeline marker and gesture marker, # with some amount of time in between them. trace_events.append({ 'name': RENDERER_PROCESS_MARKER, 'tts': mock_timer.microseconds, 'args': {}, 'pid': 20978, 'ts': mock_timer.microseconds, 'cat': 'webkit', 'tid': 11, 'ph': 'S', # Phase: start. 'id': '0x12345' }) mock_timer.Advance() trace_events.append({ 'name': SYNTHETIC_GESTURE_MARKER, 'tts': mock_timer.microseconds, 'args': {}, 'pid': 20978, 'ts': mock_timer.microseconds, 'cat': 'webkit', 'tid': 11, 'ph': 'S', 'id': '0xabcde' }) # Generate 100 random mock frames and append their trace events. for _ in xrange(0, 100): mock_frame = MockFrame(mock_timer) mock_frame.AppendTraceEventForMainThreadStats(trace_events) mock_frame.AppendTraceEventForImplThreadStats(trace_events) # Exclude the first frame, because it may have started before the # benchmark run. if not first_frame: total_time_seconds += mock_frame.duration / 1e6 num_frames_sent += mock_frame.main_stats['frame_count'] num_frames_sent += mock_frame.impl_stats['frame_count'] first_frame = False current_frame_time = mock_timer.microseconds / 1000.0 if previous_frame_time: difference = current_frame_time - previous_frame_time difference = round(difference, 2) expected_frame_times.append(difference) previous_frame_time = current_frame_time # Append finish trace events for the timeline and gesture markers, in the # reverse order from how they were added, with some time in between. trace_events.append({ 'name': SYNTHETIC_GESTURE_MARKER, 'tts': mock_timer.microseconds, 'args': {}, 'pid': 20978, 'ts': mock_timer.microseconds, 'cat': 'webkit', 'tid': 11, 'ph': 'F', # Phase: finish. 'id': '0xabcde' }) mock_timer.Advance() trace_events.append({ 'name': RENDERER_PROCESS_MARKER, 'tts': mock_timer.microseconds, 'args': {}, 'pid': 20978, 'ts': mock_timer.microseconds, 'cat': 'webkit', 'tid': 11, 'ph': 'F', 'id': '0x12345' }) # Create a timeline object from the trace. trace_result = TraceResult(ChromeRawTraceResult(trace_events)) timeline = trace_result.AsTimelineModel() # Find the timeline marker and gesture marker in the timeline, # and create a RenderingStats object. renderer_process_markers = timeline.FindTimelineMarkers( RENDERER_PROCESS_MARKER) self.assertEquals(len(renderer_process_markers), 1) renderer_process = renderer_process_markers[0].start_thread.parent timeline_markers = timeline.FindTimelineMarkers( SYNTHETIC_GESTURE_MARKER) stats = rendering_stats.RenderingStats(renderer_process, timeline_markers) # Make a results object and add results to it from the smoothness metric. results = PageMeasurementResults() results.WillMeasurePage(page.Page('http://foo.com/', None)) smoothness_metric = smoothness.SmoothnessMetric(None) smoothness_metric.SetStats(stats) smoothness_metric.AddResults(None, results) results.DidMeasurePage() self.assertEquals(expected_frame_times, results.page_results[0]['frame_times'].value) self.assertAlmostEquals( 1000.0 * (total_time_seconds / num_frames_sent), results.page_results[0]['mean_frame_time'].value, places=2) # We don't verify the correctness of the discrepancy computation itself, # because we have a separate unit test for that purpose. self.assertAlmostEquals(statistics.FrameDiscrepancy( stats.frame_timestamps, True), results.page_results[0]['jank'].value, places=4) # We do not verify the correctness of Percentile here; Percentile should # have its own test. # The 17 here represents a threshold of 17 ms; this should match the value # in the smoothness metric. self.assertEquals( statistics.Percentile(expected_frame_times, 95.0) < 17.0, results.page_results[0]['mostly_smooth'].value)
def MeasurePage(self, page, tab, results): # Exit if threaded forced compositing is not enabled. if (not self._compositing_features_enabled): logging.warning('Warning: compositing feature status unknown or not '+ 'forced and threaded. Skipping measurement.') sys.exit(0) # TODO(ernstm): Remove this temporary workaround when reference build has # been updated to branch 1671 or later. backend = tab.browser._browser_backend # pylint: disable=W0212 if (not hasattr(backend, 'chrome_branch_number') or (sys.platform != 'android' and backend.chrome_branch_number < 1671)): print ('Warning: rasterize_and_record requires Chrome branch 1671 or ' 'later. Skipping measurement.') sys.exit(0) # Rasterize only what's visible. tab.ExecuteJavaScript( 'chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent();') # Wait until the page has loaded and come to a somewhat steady state. # Needs to be adjusted for every device (~2 seconds for workstation). time.sleep(float(self.options.start_wait_time)) # Render one frame before we start gathering a trace. On some pages, the # first frame requested has more variance in the number of pixels # rasterized. tab.ExecuteJavaScript( 'window.__rafFired = false;' 'window.webkitRequestAnimationFrame(function() {' 'chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers();' 'window.__rafFired = true;' '});') time.sleep(float(self.options.stop_wait_time)) tab.browser.StartTracing('webkit.console,benchmark', 60) tab.ExecuteJavaScript( 'window.__rafFired = false;' 'window.webkitRequestAnimationFrame(function() {' 'chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers();' 'console.time("' + rendering_stats.RENDER_PROCESS_MARKER + '");' 'window.__rafFired = true;' '});') # Wait until the frame was drawn. # Needs to be adjusted for every device and for different # raster_record_repeat counts. # TODO(ernstm): replace by call-back. time.sleep(float(self.options.stop_wait_time)) tab.ExecuteJavaScript( 'console.timeEnd("' + rendering_stats.RENDER_PROCESS_MARKER + '")') timeline = tab.browser.StopTracing().AsTimelineModel() try: timeline_markers = timeline.FindTimelineMarkers( rendering_stats.RENDER_PROCESS_MARKER) except (MarkerMismatchError, MarkerOverlapError) as e: raise page_measurement.MeasurementFailure(str(e)) stats = rendering_stats.RenderingStats(timeline_markers, timeline_markers) results.Add('rasterize_time', 'ms', max(stats.rasterize_time)) results.Add('record_time', 'ms', max(stats.record_time)) results.Add('rasterized_pixels', 'pixels', max(stats.rasterized_pixel_count)) results.Add('recorded_pixels', 'pixels', max(stats.recorded_pixel_count))