Artificially limiting framerate

I have a real-time ML model executing on GPU using WebGL and its always competing with resources with BabylonJS scene that visualizes results from the model.

When ML model is paused, babylon both engine.getFps() and engine.performanceMonitor.averageFPS return ~144 which is a refresh rate of my display.

When model is running, that drops to ~20 fps.

But the thing is, there are different user scenarios:
a) smooth visualization or b) as-fast-as-possible ML results.

for (a) I know how to slow down ML processing
and that does free up resources so babylon fps rendering speeds up

for (b) Question is how can I artificially slow down babylon rendering loop
(to lets say target of 5FPS) to free up as much resources as possible for ML?

I’ve looked at engine timeStep option, but that doesn’t appear to do the trick.
And using scene optimizer is exactly the opposite of what I need.

I do not know what ML is, but here goes. Fun fact, I do not believe you have to have an actual render “LOOP” for BJS. You just need to call scene.render(). If you have a different process which runs over and over that is used for input to a scene, seems like you could just put the render at the end.

Probably, not going to dramatically increase rates, there is only so much time in a second. Would eliminate un- necessary render when the “input” has not changed yet.

1 Like

Wouldn’t the simplest way just be when in that low FPS mode mode, in your render loop check if lastTickTime + some ms constant is < currentTime and don’t perform whatever logic/render until then

I do not know what ML is

ml = machine learning

ok, i’ve replaced:

engine.runRenderLoop(() => scene.render());

with something like:

const targetFps = 5;
setInterval(() => scene.render(), 1000 / targetFps);

and that gives me visual ~5fps which is exactly what i needed
and if i need to change targetFps, i can do a clearInterval and set a new one

only side-effects are that engine.getFps() now returns fixed 60 no matter what and engine.performanceMonitor.averageFPS is Infinity

ideally, there would be a engine.setFps() method, but i can work with this

In latest versions of Babylon.js for rendering scene you need to call function of beginning and ending, like this:

const targetFps = 90;
setInterval(()=>{ toLoop(); }, 1000 / targetFps); 

function toLoop(){
  engine.beginFrame();
      scene.render();
  engine.endFrame();
}

And engine.getFps() or in INSPECTOR it calculates FPS correctly

But for me it is still frustrating that I cant setup such basic thing like fps or tick rate for using default engine.runRenderLoop and need develop main RenderLoop by myself.

3 Likes

Can you open a feature request thread in the forum about it ? We would definitely prioritize it if it gets traction ?

1 Like

Yep and I’ll be happy to do it if people are interested

2 Likes

ok I did it :wink:
Allow the users to control the max fps of the engine by deltakosh · Pull Request #16075 · BabylonJS/Babylon.js

10 Likes

Thank you! I just read the reply to my message and to be honest I haven’t quite figured out what’s going on here yet and such a task would be a bit of a challenge for me. Anyway - I’m still too new to ask such things, as I may simply be doing it incorrectly or simply not have all the available information on the matter.

1 Like

This is actually mega helpful :heart_eyes: for debugging! Thank you!!

May I ask here, is the bottom right fps counter (in the playground) supposed to reflect the maxFps value (assuming it could go over)? Because there are some values that seem to be rounded or so:


Btw. please make a minFps next :grin:

2 Likes

Definitely. I made this feature by my own, just skipping some frames. Useful if this now is a part of engine.

1 Like

please make a minFps next

I think that’s called frame interpolation :joy:

1 Like

yes this is not supposed to be perfect as we are still under the heartbeat of the RequestAnimationFrame of the browser.

1 Like

Just tested this feature and how have some questions.

Why if going threw custom loop with engine.beginFrame(); and engine.endFrame(); - it show maximally close to target fps, but if I use this new feature - engine.maxFPS - when I setting 90 - it stucked at 72 fps, and if 60 - it stucked at 48.

If I use this._engine.beginFrame(); and this._engine.endFrame(); inside the
this._engine.runRenderLoop(() => {
It shows 96 with maxFPS 60…

The way it works is by checking the minimum time between frame. So it is not super precise as we still rely on RAF.

The begin/end frame you mentioned is checking just the minimal frame time you could possibly get.

Can you share a repro in the PG where you get 48 when asking for 60?

hum…that’s what I have:

In your case, if you do not use the maxFps, how much do you get?

144
maybe its because of freesync or something else, seems like its client browser/driver/OS side…
I’ve tested in chrome and firefox - same result :thinking:
Putting 75 - getting 72. Strange thing.

yeah it is not SUPER precise but should be super close though

that’s 'cause rendering relies on requestAnimationFrame

which servers a new frame vsynced w/ your refresh rate.

so say I have a 60hz display. I get a new frame every 16ms.
if I have maxFps = 50, it will,

  • render a frame - 0ms
  • wait on the next one 'cuz it’s too quick - 16ms
  • render the next 'cuz it’s fine now - 32ms

notice even though I set maxFps = 50 I get only 30fps.

if you use setInterval / setTimeout set to as quick as possible, you should be as close to your desired framerate as you can get.

w/ requestAnimationFrame I get around 30fps, w/ setTimeout I get about 47-48

same will also happen if RAF serves slightly slower than 60fps but you set your maxFps to 60

2 Likes