Javascript  |  171行  |  4.88 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

/**
 * @fileoverview This file provides the RenderingStats object, used
 * to characterize rendering smoothness.
 */
(function() {
  var getTimeMs = (function() {
    if (window.performance)
      return (performance.now       ||
              performance.mozNow    ||
              performance.msNow     ||
              performance.oNow      ||
              performance.webkitNow).bind(window.performance);
    else
      return function() { return new Date().getTime(); };
  })();

  var requestAnimationFrame = (function() {
    return window.requestAnimationFrame       ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame    ||
           window.oRequestAnimationFrame      ||
           window.msRequestAnimationFrame     ||
           function(callback) {
             window.setTimeout(callback, 1000 / 60);
           };
  })().bind(window);

  /**
   * Tracks rendering performance using the gpuBenchmarking.renderingStats API.
   * @constructor
   */
  function GpuBenchmarkingRenderingStats() {
  }

  GpuBenchmarkingRenderingStats.prototype.start = function() {
    this.startTime_ = getTimeMs();
    this.initialStats_ = this.getRenderingStats_();
  }

  GpuBenchmarkingRenderingStats.prototype.stop = function() {
    this.stopTime_ = getTimeMs();
    this.finalStats_ = this.getRenderingStats_();
  }

  GpuBenchmarkingRenderingStats.prototype.getStartValues = function() {
    if (!this.initialStats_)
      throw new Error('Start not called.');

    if (!this.finalStats_)
      throw new Error('Stop was not called.');

    return this.initialStats_;
  }

  GpuBenchmarkingRenderingStats.prototype.getEndValues = function() {
    if (!this.initialStats_)
      throw new Error('Start not called.');

    if (!this.finalStats_)
      throw new Error('Stop was not called.');

    return this.finalStats_;
  }

  GpuBenchmarkingRenderingStats.prototype.getDeltas = function() {
    if (!this.initialStats_)
      throw new Error('Start not called.');

    if (!this.finalStats_)
      throw new Error('Stop was not called.');

    var stats = {}
    for (var key in this.finalStats_)
      stats[key] = this.finalStats_[key] - this.initialStats_[key];
    return stats;
  };

  GpuBenchmarkingRenderingStats.prototype.isUsingGpuBenchmarking = function() {
    return true;
  }

  GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() {
    var stats = chrome.gpuBenchmarking.renderingStats();
    stats.totalTimeInSeconds = getTimeMs() / 1000;
    return stats;
  };

  /**
   * Tracks rendering performance using requestAnimationFrame.
   * @constructor
   */
  function RafRenderingStats() {
    this.recording_ = false;
    this.frameTimes_ = [];
  }

  RafRenderingStats.prototype.start = function() {
    if (this.recording_)
      throw new Error('Already started.');
    this.recording_ = true;
    requestAnimationFrame(this.recordFrameTime_.bind(this));
  }

  RafRenderingStats.prototype.stop = function() {
    this.recording_ = false;
  }

  RafRenderingStats.prototype.getStartValues = function() {
    var results = {};
    results.numAnimationFrames = 0;
    results.numFramesSentToScreen = 0;
    results.droppedFrameCount = 0;
    return results;
  }

  RafRenderingStats.prototype.getEndValues = function() {
    var results = {};
    results.numAnimationFrames = this.frameTimes_.length - 1;
    results.numFramesSentToScreen = results.numAnimationFrames;
    results.droppedFrameCount = this.getDroppedFrameCount_(this.frameTimes_);
    return results;
  }

  RafRenderingStats.prototype.getDeltas = function() {
    var endValues = this.getEndValues();
    endValues.totalTimeInSeconds = (
        this.frameTimes_[this.frameTimes_.length - 1] -
        this.frameTimes_[0]) / 1000;
    return endValues;
  };

  RafRenderingStats.prototype.isUsingGpuBenchmarking = function() {
    return false;
  }

  RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) {
    if (!this.recording_)
      return;

    this.frameTimes_.push(timestamp);
    requestAnimationFrame(this.recordFrameTime_.bind(this));
  };

  RafRenderingStats.prototype.getDroppedFrameCount_ = function(frameTimes) {
    var droppedFrameCount = 0;
    var droppedFrameThreshold = 1000 / 55;
    for (var i = 1; i < frameTimes.length; i++) {
      var frameTime = frameTimes[i] - frameTimes[i-1];
      if (frameTime > droppedFrameThreshold)
        droppedFrameCount += Math.floor(frameTime / droppedFrameThreshold);
    }
    return droppedFrameCount;
  };

  function RenderingStats() {
    if (window.chrome && chrome.gpuBenchmarking &&
        chrome.gpuBenchmarking.renderingStats) {
      return new GpuBenchmarkingRenderingStats();
    }
    return new RafRenderingStats();
  }

  window.__RenderingStats = RenderingStats;
})();