1# Copyright 2014 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. 4import sys 5 6from measurements import smooth_gesture_util 7from telemetry.core.platform import tracing_category_filter 8from telemetry.core.platform import tracing_options 9from telemetry.timeline.model import TimelineModel 10from telemetry.page import page_test 11from telemetry.page.actions import action_runner 12from telemetry.value import list_of_scalar_values 13from telemetry.value import scalar 14from telemetry.web_perf import timeline_interaction_record as tir_module 15from telemetry.web_perf.metrics import smoothness 16 17 18RUN_SMOOTH_ACTIONS = 'RunSmoothAllActions' 19 20# Descriptions for results from platform.GetRawDisplayFrameRateMeasurements(). 21DESCRIPTIONS = { 22 'avg_surface_fps': 'Average frames per second as measured by the ' 23 'platform\'s SurfaceFlinger.' 24} 25 26 27class MissingDisplayFrameRateError(page_test.MeasurementFailure): 28 def __init__(self, name): 29 super(MissingDisplayFrameRateError, self).__init__( 30 'Missing display frame rate metrics: ' + name) 31 32class SmoothnessController(object): 33 def __init__(self): 34 self._timeline_model = None 35 self._tracing_timeline_data = None 36 self._interaction = None 37 38 def SetUp(self, page, tab): 39 # FIXME: Remove webkit.console when blink.console lands in chromium and 40 # the ref builds are updated. crbug.com/386847 41 custom_categories = ['webkit.console', 'blink.console', 'benchmark'] 42 custom_categories += page.GetSyntheticDelayCategories() 43 category_filter = tracing_category_filter.TracingCategoryFilter() 44 for c in custom_categories: 45 category_filter.AddIncludedCategory(c) 46 options = tracing_options.TracingOptions() 47 options.enable_chrome_trace = True 48 tab.browser.platform.tracing_controller.Start(options, category_filter, 60) 49 if tab.browser.platform.IsRawDisplayFrameRateSupported(): 50 tab.browser.platform.StartRawDisplayFrameRateMeasurement() 51 52 def Start(self, tab): 53 # Start the smooth marker for all smooth actions. 54 runner = action_runner.ActionRunner(tab) 55 self._interaction = runner.BeginInteraction( 56 RUN_SMOOTH_ACTIONS, is_smooth=True) 57 58 def Stop(self, tab): 59 # End the smooth marker for all smooth actions. 60 self._interaction.End() 61 # Stop tracing for smoothness metric. 62 if tab.browser.platform.IsRawDisplayFrameRateSupported(): 63 tab.browser.platform.StopRawDisplayFrameRateMeasurement() 64 self._tracing_timeline_data = tab.browser.platform.tracing_controller.Stop() 65 self._timeline_model = TimelineModel( 66 timeline_data=self._tracing_timeline_data) 67 68 def AddResults(self, tab, results): 69 # Add results of smoothness metric. This computes the smoothness metric for 70 # the time ranges of gestures, if there is at least one, else the the time 71 # ranges from the first action to the last action. 72 73 renderer_thread = self._timeline_model.GetRendererThreadFromTabId( 74 tab.id) 75 run_smooth_actions_record = None 76 smooth_records = [] 77 for event in renderer_thread.async_slices: 78 if not tir_module.IsTimelineInteractionRecord(event.name): 79 continue 80 r = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) 81 if r.label == RUN_SMOOTH_ACTIONS: 82 assert run_smooth_actions_record is None, ( 83 'SmoothnessController cannot issue more than 1 %s record' % 84 RUN_SMOOTH_ACTIONS) 85 run_smooth_actions_record = r 86 elif r.is_smooth: 87 smooth_records.append( 88 smooth_gesture_util.GetAdjustedInteractionIfContainGesture( 89 self._timeline_model, r)) 90 91 # If there is no other smooth records, we make measurements on time range 92 # marked smoothness_controller itself. 93 # TODO(nednguyen): when crbug.com/239179 is marked fixed, makes sure that 94 # page sets are responsible for issueing the markers themselves. 95 if len(smooth_records) == 0: 96 if run_smooth_actions_record is None: 97 sys.stderr.write('Raw tracing data:\n') 98 sys.stderr.write(repr(self._tracing_timeline_data.EventData())) 99 sys.stderr.write('\n') 100 raise Exception('SmoothnessController failed to issue markers for the ' 101 'whole interaction.') 102 else: 103 smooth_records = [run_smooth_actions_record] 104 105 # Create an interaction_record for this legacy measurement. Since we don't 106 # wrap the results that are sent to smoothness metric, the label will 107 # not be used. 108 smoothness_metric = smoothness.SmoothnessMetric() 109 smoothness_metric.AddResults( 110 self._timeline_model, renderer_thread, smooth_records, results) 111 if tab.browser.platform.IsRawDisplayFrameRateSupported(): 112 for r in tab.browser.platform.GetRawDisplayFrameRateMeasurements(): 113 if r.value is None: 114 raise MissingDisplayFrameRateError(r.name) 115 if isinstance(r.value, list): 116 results.AddValue(list_of_scalar_values.ListOfScalarValues( 117 results.current_page, r.name, r.unit, r.value, 118 description=DESCRIPTIONS.get(r.name))) 119 else: 120 results.AddValue(scalar.ScalarValue( 121 results.current_page, r.name, r.unit, r.value, 122 description=DESCRIPTIONS.get(r.name))) 123 124 def CleanUp(self, tab): 125 if tab.browser.platform.IsRawDisplayFrameRateSupported(): 126 tab.browser.platform.StopRawDisplayFrameRateMeasurement() 127 if tab.browser.platform.tracing_controller.is_tracing_running: 128 tab.browser.platform.tracing_controller.Stop() 129