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.getRenderingStats_ = function() { 85 var stats = chrome.gpuBenchmarking.renderingStats(); 86 stats.totalTimeInSeconds = getTimeMs() / 1000; 87 return stats; 88 }; 89 90 /** 91 * Tracks rendering performance using requestAnimationFrame. 92 * @constructor 93 */ 94 function RafRenderingStats() { 95 this.recording_ = false; 96 this.frameTimes_ = []; 97 } 98 99 RafRenderingStats.prototype.start = function() { 100 if (this.recording_) 101 throw new Error('Already started.'); 102 this.recording_ = true; 103 requestAnimationFrame(this.recordFrameTime_.bind(this)); 104 } 105 106 RafRenderingStats.prototype.stop = function() { 107 this.recording_ = false; 108 } 109 110 RafRenderingStats.prototype.getStartValues = function() { 111 var results = {}; 112 results.numAnimationFrames = 0; 113 results.numFramesSentToScreen = 0; 114 results.droppedFrameCount = 0; 115 return results; 116 } 117 118 RafRenderingStats.prototype.getEndValues = function() { 119 var results = {}; 120 results.numAnimationFrames = this.frameTimes_.length - 1; 121 results.numFramesSentToScreen = results.numAnimationFrames; 122 results.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_); 123 return results; 124 } 125 126 RafRenderingStats.prototype.getDeltas = function() { 127 var endValues = this.getEndValues(); 128 endValues.totalTimeInSeconds = ( 129 this.frameTimes_[this.frameTimes_.length - 1] - 130 this.frameTimes_[0]) / 1000; 131 return endValues; 132 }; 133 134 RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) { 135 if (!this.recording_) 136 return; 137 138 this.frameTimes_.push(timestamp); 139 requestAnimationFrame(this.recordFrameTime_.bind(this)); 140 }; 141 142 RafRenderingStats.prototype.getDroppedFrameCount_ = function(frameTimes) { 143 var droppedFrameCount = 0; 144 for (var i = 1; i < frameTimes.length; i++) { 145 var frameTime = frameTimes[i] - frameTimes[i-1]; 146 if (frameTime > 1000 / 55) 147 droppedFrameCount++; 148 } 149 return droppedFrameCount; 150 }; 151 152 function RenderingStats() { 153 if (window.chrome && chrome.gpuBenchmarking && 154 chrome.gpuBenchmarking.renderingStats) { 155 return new GpuBenchmarkingRenderingStats(); 156 } 157 return new RafRenderingStats(); 158 } 159 160 window.__RenderingStats = RenderingStats; 161 })(); 162