# Copyright 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 metrics import Metric
from metrics import rendering_stats
from metrics import statistics
from telemetry.core.timeline.model import MarkerMismatchError
from telemetry.core.timeline.model import MarkerOverlapError
from telemetry.page import page_measurement
TIMELINE_MARKER = 'Smoothness'
class NotEnoughFramesError(page_measurement.MeasurementFailure):
def __init__(self):
super(NotEnoughFramesError, self).__init__(
'Page output less than two frames')
class NoSupportedActionError(page_measurement.MeasurementFailure):
def __init__(self):
super(NoSupportedActionError, self).__init__(
'None of the actions is supported by smoothness measurement')
class SmoothnessMetric(Metric):
def __init__(self):
super(SmoothnessMetric, self).__init__()
self._stats = None
self._timeline_marker_names = []
def AddTimelineMarkerNameToIncludeInMetric(self, timeline_marker_name):
self._timeline_marker_names.append(timeline_marker_name)
def Start(self, page, tab):
tab.browser.StartTracing('webkit.console,benchmark', 60)
tab.ExecuteJavaScript('console.time("' + TIMELINE_MARKER + '")')
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 SetStats(self, stats):
""" Pass in a RenderingStats object directly. For unittests that don't call
Start/Stop.
"""
self._stats = stats
def AddResults(self, tab, results):
# List of raw frame times.
results.Add('frame_times', 'ms', self._stats.frame_times)
# Arithmetic mean of frame times.
mean_frame_time = statistics.ArithmeticMean(self._stats.frame_times,
len(self._stats.frame_times))
results.Add('mean_frame_time', 'ms', round(mean_frame_time, 3))
# Absolute discrepancy of frame time stamps.
jank = statistics.FrameDiscrepancy(self._stats.frame_timestamps)
results.Add('jank', '', round(jank, 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(self._stats.frame_times, 95.0)
results.Add('mostly_smooth', '', 1.0 if percentile_95 < 19.0 else 0.0)