Javascript HTML5 Canvas 性能 - 计算每秒循环次数/帧数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5078913/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HTML5 Canvas performance - calculating loops/frames per second
提问by Henryz
I know a few questions have been asked like this one before, such as this: Check FPS in JS?- which did work to some degree, I was able to find out how long each loop took to complete.
我知道以前有人问过这样的几个问题,例如:在 JS 中检查 FPS?- 这在某种程度上确实有效,我能够找出每个循环需要多长时间才能完成。
What I am looking for though is something more readable and controllable. I want to be able to set the refresh rate for the FPS counter to make it slow so it is human readable or as fast as the application can run, so I can use it on some kind of speedometer.
我正在寻找的是更具可读性和可控性的东西。我希望能够设置 FPS 计数器的刷新率以使其变慢,以便人类可读或与应用程序运行的速度一样快,因此我可以在某种速度计上使用它。
Anyway so here is the code I have right now:
无论如何,这是我现在拥有的代码:
var lastLoop = new Date().getTime();
function updateStage()
{
clearCanvas();
updateStageObjects();
drawStageObjects();
var thisLoop = new Date().getTime();
var fps = (thisLoop - lastLoop);
$('#details').html(fps);
lastLoop = thisLoop;
iteration = setTimeout(updateStage, 1);
}
Am I right to be setting the setTimeout function to a speed of 1 millisecond? I was thinking this will just make it loop as fast as it possibly can.
Should I count every 100 frames or so, find out how many milliseconds it took to run 100 frames then make a calculation to find out how many frames it would have done if the milliseconds were 1000? What would this calculation be?
To make the result more accurate I am guessing I need to display averages as one frame can vary a significant amount, how should I do this?
我将 setTimeout 函数设置为 1 毫秒的速度是否正确?我在想这只会让它尽可能快地循环。
我应该每 100 帧左右计算一次,找出运行 100 帧需要多少毫秒,然后进行计算以找出如果毫秒为 1000 会完成多少帧?这个计算会是什么?
为了使结果更准确,我猜我需要显示平均值,因为一帧可能会有很大的变化,我应该怎么做?
Any tips are greatly appreciated.
非常感谢任何提示。
Thanks.
谢谢。
回答by Phrogz
Note that the faster you update your output, the more you will affect your measurement. Although minimal, I try to update my fps output once per second or less unless it's necessary to go faster.
I like to have a low-pass filter on my results so that a temporary hiccup doesn't affect the values too strongly. This is easier to compute and write than a moving average, and doesn't have the problem of an overall average where your 'current' readings are affected by total performance over the entire run (e.g. anomalous readings during startup).
请注意,更新输出的速度越快,对测量的影响就越大。虽然很小,但我尝试每秒更新一次或更少的 fps 输出,除非有必要加快速度。
我喜欢在我的结果上使用低通滤波器,这样临时打嗝不会对值产生太大影响。这比移动平均更容易计算和写入,并且不存在整体平均的问题,即您的“当前”读数受整个运行期间的总体性能影响(例如启动期间的异常读数)。
Put together, here's how I usually measure FPS:
综合起来,以下是我通常衡量 FPS 的方式:
var fps = 0, now, lastUpdate = (new Date)*1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
if (now!=lastUpdate){
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now;
}
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
回答by Niels
Ive tried something out,
我试过了,
If you change the
如果你改变
lastUpdate = now
to
到
lastUpdate = now * 1 - 1;
Your NaN problem is solved! This is also used where the lastUpdate is defined. Probably because it is not able to convert the date to unix timestamp.
你的 NaN 问题解决了!这也用于定义 lastUpdate 的地方。可能是因为它无法将日期转换为 unix 时间戳。
The new result will be:
新的结果将是:
var fps = 0, now, lastUpdate = (new Date)*1 - 1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now * 1 - 1;
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
回答by user2552832
I've taken the solution(s) posted and enhanced them a little. Have a look here - http://jsfiddle.net/ync3S/
我已经采用了发布的解决方案并稍微增强了它们。看看这里 - http://jsfiddle.net/ync3S/
- I fixed that NaN error by using Date.now() instead of constructing a new date object each time and trying to reference it. This also prevents some garbage collection necessity.
- I neatened up the variable and function names a bit and added some extra commenting - not necessary but nice to have.
- I included some drawing code for testing.
- I added fpsDesired as a test var for the engine loop.
- I started fpsAverage at fpsDesired so with the fpsFilter it doesn't work up from 0 to the real FPS, rather starting at the desired FPS and adjusting from there.
- Drawing now blocks incase it already was drawing, and this can be used for pausing and other control functions.
- 我通过使用 Date.now() 而不是每次构造一个新的日期对象并尝试引用它来修复 NaN 错误。这也防止了一些垃圾收集的必要性。
- 我稍微整理了变量和函数名称,并添加了一些额外的注释 - 不是必需的,但很好。
- 我包含了一些用于测试的绘图代码。
- 我添加了 fpsDesired 作为引擎循环的测试变量。
- 我以 fpsDesired 开始 fpsAverage,因此使用 fpsFilter 它不能从 0 到实际 FPS,而是从所需的 FPS 开始并从那里调整。
- 现在绘图会阻止它已经在绘图,这可用于暂停和其他控制功能。
The main block is as follows:
主要块如下:
var fpsFilter = 1; // the low pass filter to apply to the FPS average
var fpsDesired = 25; // your desired FPS, also works as a max
var fpsAverage = fpsDesired;
var timeCurrent, timeLast = Date.now();
var drawing = false;
function fpsUpdate() {
fpsOutput.innerHTML = fpsAverage.toFixed(2);
}
function frameDraw() {
if(drawing) { return; } else { drawing = true; }
timeCurrent = Date.now();
var fpsThisFrame = 1000 / (timeCurrent - timeLast);
if(timeCurrent > timeLast) {
fpsAverage += (fpsThisFrame - fpsAverage) / fpsFilter;
timeLast = timeCurrent;
}
drawing = false;
}
setInterval(fpsUpdate, 1000);
fpsUpdate();
setInterval(frameDraw, 1000 / fpsDesired);
frameDraw();
Going to have a tinker and see if I can come up with something smoother, as this thread is near the top in Google results.
有一个修补匠,看看我是否能想出更流畅的东西,因为这个线程在谷歌结果中接近顶部。
Let's see what we can all come up with as a team, and I think it's always neat to not use 3rd party libraries, making the code portable for anyone :)
让我们看看我们作为一个团队都能想出什么,我认为不使用 3rd 方库总是很好的,使代码对任何人都可移植:)
-Platima
-Platima
回答by Gustav G
Just set a interval that is resetting the fps counter every second.
只需设置一个间隔,即每秒重置 fps 计数器。
var fpsOut, fpsCount;
var draw = function () {
fpsCount++;
..Draw To Canvas..
..Get the fps value: fpsOut
requestAnimationFrame(draw);
};
setInterval(function () {
fpsOut = fpsCount;
fpsCount = 0;
}, 1000);
draw();