• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 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.
4
5'use strict';
6
7/**
8 * @fileoverview This file provides the RenderingStats object, used
9 * to characterize rendering smoothness.
10 */
11(function() {
12  var getTimeMs = (function() {
13    if (window.performance)
14      return (performance.now       ||
15              performance.mozNow    ||
16              performance.msNow     ||
17              performance.oNow      ||
18              performance.webkitNow).bind(window.performance);
19    else
20      return function() { return new Date().getTime(); };
21  })();
22
23  var requestAnimationFrame = (function() {
24    return window.requestAnimationFrame       ||
25           window.webkitRequestAnimationFrame ||
26           window.mozRequestAnimationFrame    ||
27           window.oRequestAnimationFrame      ||
28           window.msRequestAnimationFrame     ||
29           function(callback) {
30             window.setTimeout(callback, 1000 / 60);
31           };
32  })().bind(window);
33
34  /**
35   * Tracks rendering performance using the gpuBenchmarking.renderingStats API.
36   * @constructor
37   */
38  function GpuBenchmarkingRenderingStats() {
39  }
40
41  GpuBenchmarkingRenderingStats.prototype.start = function() {
42    this.startTime_ = getTimeMs();
43    this.initialStats_ = this.getRenderingStats_();
44  }
45
46  GpuBenchmarkingRenderingStats.prototype.stop = function() {
47    this.stopTime_ = getTimeMs();
48    this.finalStats_ = this.getRenderingStats_();
49  }
50
51  GpuBenchmarkingRenderingStats.prototype.getStartValues = function() {
52    if (!this.initialStats_)
53      throw new Error('Start not called.');
54
55    if (!this.finalStats_)
56      throw new Error('Stop was not called.');
57
58    return this.initialStats_;
59  }
60
61  GpuBenchmarkingRenderingStats.prototype.getEndValues = function() {
62    if (!this.initialStats_)
63      throw new Error('Start not called.');
64
65    if (!this.finalStats_)
66      throw new Error('Stop was not called.');
67
68    return this.finalStats_;
69  }
70
71  GpuBenchmarkingRenderingStats.prototype.getDeltas = function() {
72    if (!this.initialStats_)
73      throw new Error('Start not called.');
74
75    if (!this.finalStats_)
76      throw new Error('Stop was not called.');
77
78    var stats = {}
79    for (var key in this.finalStats_)
80      stats[key] = this.finalStats_[key] - this.initialStats_[key];
81    return stats;
82  };
83
84  GpuBenchmarkingRenderingStats.prototype.isUsingGpuBenchmarking = function() {
85    return true;
86  }
87
88  GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() {
89    var stats = chrome.gpuBenchmarking.renderingStats();
90    stats.totalTimeInSeconds = getTimeMs() / 1000;
91    return stats;
92  };
93
94  /**
95   * Tracks rendering performance using requestAnimationFrame.
96   * @constructor
97   */
98  function RafRenderingStats() {
99    this.recording_ = false;
100    this.frameTimes_ = [];
101  }
102
103  RafRenderingStats.prototype.start = function() {
104    if (this.recording_)
105      throw new Error('Already started.');
106    this.recording_ = true;
107    requestAnimationFrame(this.recordFrameTime_.bind(this));
108  }
109
110  RafRenderingStats.prototype.stop = function() {
111    this.recording_ = false;
112  }
113
114  RafRenderingStats.prototype.getStartValues = function() {
115    var results = {};
116    results.numAnimationFrames = 0;
117    results.numFramesSentToScreen = 0;
118    results.droppedFrameCount = 0;
119    return results;
120  }
121
122  RafRenderingStats.prototype.getEndValues = function() {
123    var results = {};
124    results.numAnimationFrames = this.frameTimes_.length - 1;
125    results.numFramesSentToScreen = results.numAnimationFrames;
126    results.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_);
127    return results;
128  }
129
130  RafRenderingStats.prototype.getDeltas = function() {
131    var endValues = this.getEndValues();
132    endValues.totalTimeInSeconds = (
133        this.frameTimes_[this.frameTimes_.length - 1] -
134        this.frameTimes_[0]) / 1000;
135    return endValues;
136  };
137
138  RafRenderingStats.prototype.isUsingGpuBenchmarking = function() {
139    return false;
140  }
141
142  RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) {
143    if (!this.recording_)
144      return;
145
146    this.frameTimes_.push(timestamp);
147    requestAnimationFrame(this.recordFrameTime_.bind(this));
148  };
149
150  RafRenderingStats.prototype.getDroppedFrameCount_ = function(frameTimes) {
151    var droppedFrameCount = 0;
152    var droppedFrameThreshold = 1000 / 55;
153    for (var i = 1; i < frameTimes.length; i++) {
154      var frameTime = frameTimes[i] - frameTimes[i-1];
155      if (frameTime > droppedFrameThreshold)
156        droppedFrameCount += Math.floor(frameTime / droppedFrameThreshold);
157    }
158    return droppedFrameCount;
159  };
160
161  function RenderingStats() {
162    if (window.chrome && chrome.gpuBenchmarking &&
163        chrome.gpuBenchmarking.renderingStats) {
164      return new GpuBenchmarkingRenderingStats();
165    }
166    return new RafRenderingStats();
167  }
168
169  window.__RenderingStats = RenderingStats;
170})();
171