Home | History | Annotate | Download | only in metrics
      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