0

So I have a running loop:

var FRAME_TIME = 1000 / 60;
var lastTime = 0;
var fpsTime = 0;
var fps = 0;

function mainLoop(timestamp) {
    if (timestamp - lastTime >= FRAME_TIME) {
        fps++;
        lastTime = timestamp;
    }

    if (timestamp - fpsTime >= 1000) {
        console.log(fps);
        fps = 0;
        fpsTime = timestamp;
    }
    requestAnimationFrame(mainLoop);
}

Now, when running with monitor refresh at 60, I get "60" printed in console as expected. But, when running with resfresh at 144, I get "48" = 1/3 of the refresh.

Why? Time is time, right...?

ludolover
  • 21
  • 6
  • are you calling mainLoop() once per rendered frame? – Ben Abraham Mar 21 '17 at 17:21
  • Well it seems your browser cannot keep up with 144fps, so it will render an animation frame only every third monitor update. – Bergi Mar 21 '17 at 17:22
  • @Bergi It runs 144 fine without any limiting. – ludolover Mar 21 '17 at 17:25
  • @BenAbraham It is called continously by raf, so 144 times per second. – ludolover Mar 21 '17 at 17:28
  • @ludolover How do you know it runs at 144 times per second when your counter does not show that? – Bergi Mar 21 '17 at 17:43
  • @Bergi When increasing the fps variable unconditionally (outside the if-sentence), it will reach 144 before resetting. – ludolover Mar 21 '17 at 17:48
  • Oh how could I miss that, yes of course. But *why* did you do it conditionally at all? – Bergi Mar 21 '17 at 17:51
  • @Bergi The point is to limit the number of updates to 60 for all refresh rates. – ludolover Mar 21 '17 at 18:24
  • No, let the browser determine the number of updates by itself. It won't always be the same. That's exactly what `requestAnimationFrame` was designed to do - get called when the browser is *ready* to render the next frame. Depending on monitor, load, battery, user settings etc. - everything is done for you already. If it wants to go faster, you should let it do so. – Bergi Mar 21 '17 at 18:31

2 Answers2

2

So I believe the main issue comes down to these lines.

if (timestamp - lastTime >= FRAME_TIME) {
    fps++;
    lastTime = timestamp;
}

What that is saying is if our current frame time (timestamp - lastTime) is OVER (or equal) to that of an 'ideal' frame (1000ms / 60 [144]), then we count it, otherwise, it's not counted.

If this function is called (once and only once) every frame, then you don't need an if statement, you can simply do "fps++" every frame, because it will be reset by the 1 second counter.

Ben Abraham
  • 482
  • 5
  • 10
1

The if (timestamp - lastTime >= FRAME_TIME) fps++; is no good. That means it does not count every frame - it counts whenever FRAME_TIME has passed. At 144 fps, that means there are two frames (at 7 ms and 14 ms) before you count the next frame at 21 ms that is after the expected FRAME_TIME for 60 fps - 17 ms. That comes down to only every third frame being counted - exactly what you experience.

To fix it, do fps++ on every invocation:

var fpsTime = 0;
var fps = 0;

function mainLoop(timestamp) {
    fps++;
    if (timestamp - fpsTime >= 1000) {
        console.log(fps);
        fps = 0;
        fpsTime = timestamp;
    }
    requestAnimationFrame(mainLoop);
}
mainLoop(performance.now());
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Yes, I see it now. 144 fps is not enough for measuring 16ms steps when comparing timestamps. But by changing "lastTime = timestamp" to "lastTime += FRAME_TIME" I get 60 fps excactly though... Or do I? Thanks. – ludolover Mar 21 '17 at 18:15
  • No, it's rather that there are no 16ms steps when running at 144fps. By doing `+= FRAME_TIME`, yes, you'll get to 60fps, as it will run *on average* every 16ms. But I don't see why you would ever want to do that – Bergi Mar 21 '17 at 18:27
  • I need to pass a constant timedelta for my "pixel perfect physics simulation", so speed will be controlled by hard-limiting the frequency of updates. That's why. :) – ludolover Mar 21 '17 at 18:41
  • Well, if you want to set the frequency of *updates*, then use `setInterval` (or something [more accurate](http://stackoverflow.com/a/29972322/1048572)). Use `requestAnimationFrame` only to *draw* things at the current state. – Bergi Mar 21 '17 at 18:44
  • The full program also draws every frame etc. I'll stick with raf, but this answer got me to the point where I'm happy, so I'll mark it as accepted. Thanks again! – ludolover Mar 21 '17 at 19:11