How do I wait for the <canvas> to update? All three.js FPS counters are inaccurate without this ability

Calling renderer.render() in a requestAnimationFrame() loop will often fire faster than the <canvas> element can update its image, making any “true” FPS measurement inaccurate.

In three.js, how can I block each iteration long enough to let the <canvas> update its image?

This is just to back you up that recently I witnessed a similar situation (although I thought it was impossible). An animation had stats.js reporting 40-60FPS, but visually it was 5-10FPS. I’m sure of this, because I was able to see individual frames.

Then I found something surprising. Even if a buffer is ready to be shown, browsers may skip displaying it on their own discretion. And they feel no guilty about this.

At that time I had an idea to read a pixel from the canvas with getImageData, hoping that this will force the browser to update the canvas before returning any result. Unfortunately I have never tried this, so I do not know whether it works or does not work. Also, I’m not sure how getImageData will impact performance, as it is known to be a slow function.

1 Like

I had an idea to read a pixel from the canvas with getImageData

That’s funny–this is the same idea that I had yesterday. I’m experimenting with a related solution. I noticed something else too that may be helpful: it’s not just the <canvas> image that doesn’t update for every fire of requestAnimationFrame(), it’s everything in the web page. This includes CSS styles and text.

If you are also displaying the frame number in an element on your page, you will notice that the frame number only gets updated when the <canvas> is updated. I verified this by recording my three.js scene at a high capture rate (120Hz) and manually stepping through each frame when I played it back to see if the canvas updated with each frame number update.

What I noticed was that the frame number displayed in the HTML element would “skip” a frame or two occasionally, which artificially inflated the FPS measurement. For example, between only two unique images in the canvas, the frame number would go from 1,340 to 1,343, essentially skipping 2 frames.

So, if you are worried about calling getImageData() on the canvas on every frame, you may not have to, although I’m not quite of what the alternative may be. I’m currently investigating this.

1 Like

I just want to throw out there…

FPS logging tools are usually timing a single RAF. I think r3f/similar applications often run multiple RAFs for different components etc, so it’s not always clear exactly which thing is being timed… and the scheduling of RAFs is dictated by the browser internally.

There is a built in FPS counter in the chrome debugger that shows an FPS overlay.

It may be valuable to enable that, and compare what it reports with what your FPS tools are reporting, as a sanity check.

1 Like