blob: c2a85fa0584e054e04dfaf47d194bf4519659903 [file] [log] [blame]
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from perf_tools import loading_metrics
from perf_tools import smoothness_metrics
from telemetry.core import util
from telemetry.page import page_measurement
class DidNotScrollException(page_measurement.MeasurementFailure):
def __init__(self):
super(DidNotScrollException, self).__init__('Page did not scroll')
class MissingDisplayFrameRate(page_measurement.MeasurementFailure):
def __init__(self, name):
super(MissingDisplayFrameRate, self).__init__(
'Missing display frame rate metrics: ' + name)
def DivideIfPossibleOrZero(numerator, denominator):
if denominator == 0:
return 0
return numerator / denominator
def CalcScrollResults(rendering_stats_deltas, results):
num_frames_sent_to_screen = rendering_stats_deltas['numFramesSentToScreen']
mean_frame_time_seconds = (
rendering_stats_deltas['totalTimeInSeconds'] /
float(num_frames_sent_to_screen))
dropped_percent = (
rendering_stats_deltas['droppedFrameCount'] /
float(num_frames_sent_to_screen))
num_impl_thread_scrolls = rendering_stats_deltas.get(
'numImplThreadScrolls', 0)
num_main_thread_scrolls = rendering_stats_deltas.get(
'numMainThreadScrolls', 0)
percent_impl_scrolled = DivideIfPossibleOrZero(
float(num_impl_thread_scrolls),
num_impl_thread_scrolls + num_main_thread_scrolls)
num_layers = (
rendering_stats_deltas.get('numLayersDrawn', 0) /
float(num_frames_sent_to_screen))
num_missing_tiles = (
rendering_stats_deltas.get('numMissingTiles', 0) /
float(num_frames_sent_to_screen))
results.Add('mean_frame_time', 'ms', round(mean_frame_time_seconds * 1000, 3))
results.Add('dropped_percent', '%', round(dropped_percent * 100, 1),
data_type='unimportant')
results.Add('percent_impl_scrolled', '%',
round(percent_impl_scrolled * 100, 1),
data_type='unimportant')
results.Add('average_num_layers_drawn', '', round(num_layers, 1),
data_type='unimportant')
results.Add('average_num_missing_tiles', '', round(num_missing_tiles, 1),
data_type='unimportant')
def CalcTextureUploadResults(rendering_stats_deltas, results):
if (('totalCommitCount' not in rendering_stats_deltas)
or rendering_stats_deltas['totalCommitCount'] == 0) :
averageCommitTimeMs = 0
else :
averageCommitTimeMs = (
1000 * rendering_stats_deltas['totalCommitTimeInSeconds'] /
rendering_stats_deltas['totalCommitCount'])
results.Add('texture_upload_count', 'count',
rendering_stats_deltas.get('textureUploadCount', 0))
results.Add('total_texture_upload_time', 'seconds',
rendering_stats_deltas.get('totalTextureUploadTimeInSeconds', 0))
results.Add('average_commit_time', 'ms', averageCommitTimeMs,
data_type='unimportant')
def CalcFirstPaintTimeResults(results, tab):
if tab.browser.is_content_shell:
results.Add('first_paint', 'ms', 'unsupported')
return
tab.ExecuteJavaScript("""
window.__rafFired = false;
window.webkitRequestAnimationFrame(function() {
window.__rafFired = true;
});
""")
util.WaitFor(lambda: tab.EvaluateJavaScript('window.__rafFired'), 60)
first_paint_secs = tab.EvaluateJavaScript(
'window.chrome.loadTimes().firstPaintTime - ' +
'window.chrome.loadTimes().startLoadTime')
results.Add('first_paint', 'ms', round(first_paint_secs * 1000, 1))
def CalcImageDecodingResults(rendering_stats_deltas, results):
totalDeferredImageDecodeCount = rendering_stats_deltas.get(
'totalDeferredImageDecodeCount', 0)
totalDeferredImageCacheHitCount = rendering_stats_deltas.get(
'totalDeferredImageCacheHitCount', 0)
totalImageGatheringCount = rendering_stats_deltas.get(
'totalImageGatheringCount', 0)
totalDeferredImageDecodeTimeInSeconds = rendering_stats_deltas.get(
'totalDeferredImageDecodeTimeInSeconds', 0)
totalImageGatheringTimeInSeconds = rendering_stats_deltas.get(
'totalImageGatheringTimeInSeconds', 0)
averageImageGatheringTime = DivideIfPossibleOrZero(
(totalImageGatheringTimeInSeconds * 1000), totalImageGatheringCount)
results.Add('total_deferred_image_decode_count', 'count',
totalDeferredImageDecodeCount,
data_type='unimportant')
results.Add('total_image_cache_hit_count', 'count',
totalDeferredImageCacheHitCount,
data_type='unimportant')
results.Add('average_image_gathering_time', 'ms', averageImageGatheringTime,
data_type='unimportant')
results.Add('total_deferred_image_decoding_time', 'seconds',
totalDeferredImageDecodeTimeInSeconds,
data_type='unimportant')
def CalcAnalysisResults(rendering_stats_deltas, results):
totalTilesAnalyzed = rendering_stats_deltas.get(
'totalTilesAnalyzed', 0)
solidColorTilesAnalyzed = rendering_stats_deltas.get(
'solidColorTilesAnalyzed', 0)
totalTileAnalysisTimeInSeconds = rendering_stats_deltas.get(
'totalTileAnalysisTimeInSeconds', 0)
averageAnalysisTimeMS = \
1000 * DivideIfPossibleOrZero(totalTileAnalysisTimeInSeconds,
totalTilesAnalyzed)
results.Add('total_tiles_analyzed', 'count',
totalTilesAnalyzed,
data_type='unimportant')
results.Add('solid_color_tiles_analyzed', 'count',
solidColorTilesAnalyzed,
data_type='unimportant')
results.Add('average_tile_analysis_time', 'ms',
averageAnalysisTimeMS,
data_type='unimportant')
def CalcLatency(rendering_stats_deltas, count_name, total_latency_name,
result_name, results):
eventCount = rendering_stats_deltas.get(count_name, 0)
totalLatencyInSeconds = rendering_stats_deltas.get(total_latency_name, 0)
averageLatency = DivideIfPossibleOrZero(
(totalLatencyInSeconds * 1000), eventCount)
results.Add(result_name, 'ms', averageLatency, data_type='unimportant')
def CalcLatencyResults(rendering_stats_deltas, results):
CalcLatency(rendering_stats_deltas, 'inputEventCount', 'totalInputLatency',
'average_latency', results)
CalcLatency(rendering_stats_deltas, 'touchUICount', 'totalTouchUILatency',
'average_touch_ui_latency', results)
CalcLatency(rendering_stats_deltas, 'touchAckedCount',
'totalTouchAckedLatency',
'average_touch_acked_latency',
results)
CalcLatency(rendering_stats_deltas, 'scrollUpdateCount',
'totalScrollUpdateLatency',
'average_scroll_update_latency', results)
class Smoothness(page_measurement.PageMeasurement):
def __init__(self):
super(Smoothness, self).__init__('smoothness')
self.force_enable_threaded_compositing = False
self.use_gpu_benchmarking_extension = True
self._metrics = None
def AddCommandLineOptions(self, parser):
parser.add_option('--report-all-results', dest='report_all_results',
action='store_true',
help='Reports all data collected, not just FPS')
def CustomizeBrowserOptions(self, options):
if self.use_gpu_benchmarking_extension:
options.extra_browser_args.append('--enable-gpu-benchmarking')
if self.force_enable_threaded_compositing:
options.extra_browser_args.append('--enable-threaded-compositing')
def CanRunForPage(self, page):
return hasattr(page, 'smoothness')
def WillRunAction(self, page, tab, action):
if tab.browser.platform.IsRawDisplayFrameRateSupported():
tab.browser.platform.StartRawDisplayFrameRateMeasurement()
self._metrics = smoothness_metrics.SmoothnessMetrics(tab)
if action.CanBeBound():
self._metrics.BindToAction(action)
else:
self._metrics.Start()
def DidRunAction(self, page, tab, action):
if tab.browser.platform.IsRawDisplayFrameRateSupported():
tab.browser.platform.StopRawDisplayFrameRateMeasurement()
if not action.CanBeBound():
self._metrics.Stop()
def MeasurePage(self, page, tab, results):
rendering_stats_deltas = self._metrics.deltas
if not (rendering_stats_deltas['numFramesSentToScreen'] > 0):
raise DidNotScrollException()
loading_metrics.AddResultsForTab(tab, results)
CalcFirstPaintTimeResults(results, tab)
CalcScrollResults(rendering_stats_deltas, results)
CalcTextureUploadResults(rendering_stats_deltas, results)
CalcImageDecodingResults(rendering_stats_deltas, results)
CalcAnalysisResults(rendering_stats_deltas, results)
CalcLatencyResults(rendering_stats_deltas, results)
if self.options.report_all_results:
for k, v in rendering_stats_deltas.iteritems():
results.Add(k, '', v)
if tab.browser.platform.IsRawDisplayFrameRateSupported():
for r in tab.browser.platform.GetRawDisplayFrameRateMeasurements():
if r.value is None:
raise MissingDisplayFrameRate(r.name)
results.Add(r.name, r.unit, r.value)