使用requestAnimationFrame在Canvas中计算FPS

Enr*_*ent 33 javascript frame-rate canvas requestanimationframe

我怎么能计算画布游戏应用程序的FPS?我已经看过一些例子,但没有一个使用requestAnimationFrame,我不知道如何在那里应用他们的解决方案.这是我的代码:

http://jsfiddle.net/WCKhH/

顺便说一句,有没有我可以添加的库来监督性能?

Jus*_*mas 26

您可以跟踪上次调用requestAnimFrame的时间.

var lastCalledTime;
var fps;

function requestAnimFrame() {

  if(!lastCalledTime) {
     lastCalledTime = Date.now();
     fps = 0;
     return;
  }
  delta = (Date.now() - lastCalledTime)/1000;
  lastCalledTime = Date.now();
  fps = 1/delta;
} 
Run Code Online (Sandbox Code Playgroud)

http://jsfiddle.net/vZP3u/

  • 不要使用`Date()`.它非常不精确(通常小于1毫秒),如果操作系统当前正在将其时钟与NTP定时器服务器同步(这种情况一直发生),甚至可以返回错误的数据.另外不要忘记时区和所有这些东西.相反,请使用Performance API:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now - 它是针对此用例而开发的. (3认同)
  • 答案用于包括`Date.now()`和`new Date().getTime()`这令人困惑.我编辑了在两个地方使用`Date.now()`的答案,因为它们返回[相同的东西](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/日期/现在#填充工具) (2认同)
  • 我强烈建议使用 https://github.com/mrdoob/stats.js/ (它甚至有一个书签版本) (2认同)

maj*_*aja 26

不使用 new Date()

此API有几个缺陷,仅对获取当前日期+时间有用.不适用于测量时间跨度.

Date-API使用操作系统的内部时钟,该时钟不断更新并与NTP时间服务器同步.这意味着,该时钟的速度/频率有时更快,有时比实际时间更慢 - 因此不能用于测量持续时间和帧速率.

如果有人更改了系统时间(手动或由于夏令时),如果一帧突然需要一小时,您至少可以看到问题.或者是负面时间.但如果系统时钟与世界时间同步快20%,则几乎不可能检测到.

此外,Date-API非常不精确 - 通常远小于1ms.这使得它对于帧速率测量尤其无用,其中一个60Hz帧需要~17ms.

相反,使用 performance.now()

Performance API专门针对此类用例而制作,可以等效地使用new Date().只需采取其中一个答案并替换new Date()performance.now(),您就可以开始了.

资料来源:

与Date.now()不同,Performance.now()返回的值总是以恒定速率增加,与系统时钟无关(可以手动调整或由NTP等软件调整).否则,performance.timing.navigationStart + performance.now()将大致等于Date.now().

https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

对于Windows:

[时间服务]调整本地时钟速率,使其收敛到正确的时间.如果本地时钟和[准确时间样本]之间的时间差太大而无法通过调整本地时钟速率来校正,则时间服务将本地时钟设置为正确的时间.

https://technet.microsoft.com/en-us/library/cc773013(v=ws.10).aspx


maj*_*aja 17

Chrome有一个内置的fps计数器:https://developer.chrome.com/devtools/docs/rendering-settings

在此输入图像描述

只需打开dev-console(F12),打开抽屉(Esc),然后添加"渲染"选项卡.

在这里,您可以激活FPS-Meter叠加层以查看当前帧速率(包括漂亮的图形)以及GPU内存消耗.

跨浏览器解决方案: 您可以使用JavaScript库stat.js获得类似的覆盖:https://github.com/mrdoob/stats.js/

在此输入图像描述

它还为帧速率(包括图形)提供了很好的叠加,并且非常易于使用.

比较stats.js和chrome dev工具的结果时,两者都显示完全相同的测量值.所以你可以相信那个库实际上做了正确的事情.


Dan*_*mms 9

这是另一个解决方案:

var times = [];
var fps;

function refreshLoop() {
  window.requestAnimationFrame(function() {
    const now = performance.now();
    while (times.length > 0 && times[0] <= now - 1000) {
      times.shift();
    }
    times.push(now);
    fps = times.length;
    refreshLoop();
  });
}

refreshLoop();
Run Code Online (Sandbox Code Playgroud)

这通过以下方式改进了其他一些:

  • performance.now()用于在Date.now()增加精度(如覆盖在此答案
  • FPS 是在最后一秒测量的,因此数字不会如此不规律地跳跃,特别是对于具有单个长帧的应用程序。

在我的网站上更详细地描述了这个解决方案。


ker*_*nel 8

我有一个不同的方法,因为如果你计算FPS,你会在返回数字时得到这个闪烁.我决定计算每一帧并每秒返回一次

window.countFPS = (function () {
  var lastLoop = (new Date()).getMilliseconds();
  var count = 1;
  var fps = 0;

  return function () {
    var currentLoop = (new Date()).getMilliseconds();
    if (lastLoop > currentLoop) {
      fps = count;
      count = 1;
    } else {
      count += 1;
    }
    lastLoop = currentLoop;
    return fps;
  };
}());

requestAnimationFrame(function () {
  console.log(countFPS());
});
Run Code Online (Sandbox Code Playgroud)

的jsfiddle

  • 是否很难将代码复制粘贴到jsfiddle中?这就是你想要的http://jsfiddle.net/krnlde/u1fL1cs5/ (6认同)

Mic*_*ick 7

事实上,没有一个答案对我来说是足够的。这是一个更好的解决方案:

  • 使用的 Performance.now()
  • 计算每秒实际平均fps
  • 每秒平均值和小数位数可配置

代码:

// Options
const outputEl         = document.getElementById('fps-output');
const decimalPlaces    = 2;
const updateEachSecond = 1;

// Cache values
const decimalPlacesRatio = Math.pow(10, decimalPlaces);
let timeMeasurements     = [];

// Final output
let fps = 0;

const tick = function() {
  timeMeasurements.push(performance.now());

  const msPassed = timeMeasurements[timeMeasurements.length - 1] - timeMeasurements[0];

  if (msPassed >= updateEachSecond * 1000) {
    fps = Math.round(timeMeasurements.length / msPassed * 1000 * decimalPlacesRatio) / decimalPlacesRatio;
    timeMeasurements = [];
  }

  outputEl.innerText = fps;

  requestAnimationFrame(() => {
    tick();
  });
}

tick();
Run Code Online (Sandbox Code Playgroud)

JSFiddle


col*_*lxi 7

我缺少一个允许为平均 FPS 值自定义样本大小的实现。这是我的,它具有以下功能:

  • 准确:基于 performance.now()
  • 稳定:返回的 FPS 值是平均值( fps.value | fps.tick() )
  • 可配置:可以自定义 FPS 样本数组大小( fps.samplesSize )
  • 高效:用于收集样本的旋转阵列(避免阵列调整大小)

const fps = {
    sampleSize : 60,    
    value : 0,
    _sample_ : [],
    _index_ : 0,
    _lastTick_: false,
    tick : function(){
        // if is first tick, just set tick timestamp and return
        if( !this._lastTick_ ){
            this._lastTick_ = performance.now();
            return 0;
        }
        // calculate necessary values to obtain current tick FPS
        let now = performance.now();
        let delta = (now - this._lastTick_)/1000;
        let fps = 1/delta;
        // add to fps samples, current tick fps value 
        this._sample_[ this._index_ ] = Math.round(fps);
        
        // iterate samples to obtain the average
        let average = 0;
        for(i=0; i<this._sample_.length; i++) average += this._sample_[ i ];

        average = Math.round( average / this._sample_.length);

        // set new FPS
        this.value = average;
        // store current timestamp
        this._lastTick_ = now;
        // increase sample index counter, and reset it
        // to 0 if exceded maximum sampleSize limit
        this._index_++;
        if( this._index_ === this.sampleSize) this._index_ = 0;
        return this.value;
    }
}


// *******************
// test time...
// *******************

function loop(){
    let fpsValue = fps.tick();
    window.fps.innerHTML = fpsValue;
    requestAnimationFrame( loop );
}
// set FPS calulation based in the last 120 loop cicles 
fps.sampleSize = 120;
// start loop
loop()
Run Code Online (Sandbox Code Playgroud)
<div id="fps">--</div>
Run Code Online (Sandbox Code Playgroud)