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