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.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