Check FPS in JS? [closed]
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
开发者_JAVA百科 Improve this questionOriginal close reason(s) were not resolved
How would I check the fps of my javascript? I'm using this to loop:
gameloopId = setInterval(gameLoop, 10);
The code by @Slaks gives you only the instantaneous FPS of the last frame, which may vary or be misleading with hiccups. I prefer to use an easy-to-write-and-compute low-pass filter to remove quick transients and display a reasonable pseudo-average of recent results:
// The higher this value, the less the fps will reflect temporary variations
// A value of 1 will only keep the last value
var filterStrength = 20;
var frameTime = 0, lastLoop = new Date, thisLoop;
function gameLoop(){
// ...
var thisFrameTime = (thisLoop=new Date) - lastLoop;
frameTime+= (thisFrameTime - frameTime) / filterStrength;
lastLoop = thisLoop;
}
// Report the fps only every second, to only lightly affect measurements
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = (1000/frameTime).toFixed(1) + " fps";
},1000);
The 'halflife' of this filter—the number of frames needed to move halfway from the old value to a new, stable value—is filterStrength*Math.log(2)
(roughly 70% of the strength).
For example, a strength of 20
will move halfway to an instantaneous change in 14 frames, 3/4 of the way there in 28 frames, 90% of the way there in 46 frames, and 99% of the way there in 92 frames. For a system running at about 30fps, a sudden, drastic shift in performance will be obvious in half a second, but will still 'throw away' single-frame anomalies as they will only shift the value by 5% of the difference.
Here is a visual comparison of different filter strengths for a ~30fps game that has a momentary dip to 10fps and then later speeds up to 50fps. As you can see, lower filter values more quickly reflect 'good' changes, but also are more susceptible to temporary hiccups:
Finally, here is an example of using the above code to actually benchmark a 'game' loop.
In gameLoop
, look at the difference between new Date
and new Date
from the last loop (store it in a variable).
In other words:
var lastLoop = new Date();
function gameLoop() {
var thisLoop = new Date();
var fps = 1000 / (thisLoop - lastLoop);
lastLoop = thisLoop;
...
}
thisLoop - lastLoop
is the number of milliseconds that passed between the two loops.
My 2 cents:
Useful to me to compare optimizations. Burn a bit of resources, of course, for testing only.
Ideally, your app frame rate should always stay well under 50ms by frame, at full usage, when using events, loops, etc. This is equal to 20FPS.
The Human eye feels lags under 24 FPS, this is 1000 / 24 = 41ms
So, 41ms for a frame is the smallest time window, to maintain natural fluidity. Higher than that is to be avoided.
let be = Date.now(),fps=0,info='';
requestAnimationFrame(
function loop(){
let now = Date.now()
fps = Math.round(1000 / (now - be))
be = now
requestAnimationFrame(loop)
if (fps < 35){
kFps.style.color = "red"
kFps.textContent = fps
} if (fps >= 35 && fps <= 41) {
kFps.style.color = "deepskyblue"
kFps.textContent = fps + " FPS"
} else {
kFps.style.color = "black"
kFps.textContent = fps + " FPS"
}
kpFps.value = fps;
info+=(''+new Date()+' '+fps+'\n');
}
)
<span id="kFps"></span>
<progress id="kpFps" value="0" min="0" max="100" style="vertical-align:middle"></progress>
<button onclick="try{console.clear();console.info(info)}catch{}">Statistics</button>
Just a test loop to get the idea, 50ms interval, should keep up smooth!
See the progress bar above jumping? ^
Those are frames losses, the browser is trying to keep up by sacrifice, by jumping to the next frame. Those spikes are to be avoided. The next snippet burns resources (i.e FPS):
let t
for (let i=0;i<99999;i++){
t = setTimeout(function(){
console.log("I am burning your CPU! " + i)
clearTimeout(t)
},50)
}
Recent versions of debuggers, have a FPS counter, in the performance
tab, when recording. This is not perfect because it overload the testing.
What about requestAnimationFrame?
var before,now,fps;
before=Date.now();
fps=0;
requestAnimationFrame(
function loop(){
now=Date.now();
fps=Math.round(1000/(now-before));
before=now;
requestAnimationFrame(loop);
console.log("fps",fps)
}
);
I use this to calculate fps
var GameCanvas = document.getElementById("gameCanvas");
var GameContext = doContext(GameCanvas,"GameCanvas");
var FPS = 0;
var TimeNow;
var TimeTaken;
var ASecond = 1000;
var FPSLimit = 25;
var StartTime = Date.now();
var TimeBefore = StartTime;
var FrameTime = ASecond/FPSLimit;
var State = { Title:0, Started:1, Paused:2, Over:3 };
var GameState = State.Title;
function gameLoop() {
requestAnimationFrame(gameLoop);
TimeNow = Date.now();
TimeTaken = TimeNow - TimeBefore;
if (TimeTaken >= FrameTime) {
FPS++
if((TimeNow - StartTime) >= ASecond){
StartTime += ASecond;
doFPS();
FPS = 0;
}
switch(GameState){
case State.Title :
break;
case State.Started :
break;
case State.Paused :
break;
case State.Over :
break;
}
TimeBefore = TimeNow - (TimeTaken % FrameTime);
}
}
Sprites.onload = function(){
requestAnimationFrame(gameLoop);
}
function drawText(Context,_Color, _X, _Y, _Text, _Size){
Context.font = "italic "+ _Size +" bold";
Context.fillStyle = _Color;
Context.fillText(_Text, _X, _Y);
}
function doFPS()(
drawText(GameContext,"black",10,24,"FPS : " + FPS,"24px");
}
function doContext(Canvas,Name){
if (Canvas.getContext) {
var Context = Canvas.getContext('2d');
return Context;
}else{
alert( Name + ' not supported your Browser needs updating');
}
}
精彩评论