A proper way to target 12FPS (or less) on fanless GPUless industrial PC

Hello Everyone!
First let me express my sincere thanks to all Babylon.js team and community.
I’m using babylon.js since two years, and i always manage to find the answers to my questions after quick check of the code and the API doc, or after quick tests on the great PG.
It’s my first question to the community, and maybe it’s already answered lot of times, but not for the same purpose or use-case as mine.
There are lot of topics on getting the max possibles FPS from the hardware, or optimize scene and nodes to get smooth experience with 60 FPS (or more), but my aim is different.
Here is my use case :

  • It’s an industrial monitoring tool, with some (almost) real-time constraints, on which the babylon.js scene is not the main task, the other monitoring tasks have the most important priority (charting and scientific calculations, on top of real-time data reception).
  • The dynamic of the scene is not too high, the quickest animation need to be refreshed 10 times per second based on real-time industrial data bus data reception interval.
  • The industrial PC is a fanless GPUless PC (lot of dust arround), the PC work 24h a day, and shouldn’t get hot, nor be rebooted.
  • the reason why i’m trying keep babylon.js at 12FPS (more then enouph for my application) to keep CPU time available for other tasks.

But i still can not find a proper way to do it, even tried to modify babylon.js engine code, but FPS always get back to 60 if CPU is available.
I tried timeout on render loop, conditional rendering,… but nothing worked
Your ideas are welcome
LedDev

2 Likes

You might find better results tapping directly into the RAF loop; it sounds like you want to control the timing of each individual system. Just handle the timing loop yourself and integrate a delta time for BJS rendering.

You can try something like this:

https://playground.babylonjs.com/#VPQHSP#3

6 Likes

A bit late to the party but here’s another solution for the same problem. It’s different in that:

  • you get maximum fps when you need it (for a set amount of time) and 0fps when not
    • but as a dev you have to explicitly declare when your code is expected to update the scene visually
  • subsequent requests to render (while already rendering) reset the timeout counter
  • supports multiple scenes

First we add this file in our project, it acts as our simple “state management”:

// renderScene.ts
import { Scene } from '@babylonjs/core/scene'

const ADDITIONAL_RENDER_TIME_IN_MS = 300

interface ScheduledScene {
  scene: Scene
  timesOutAt: number
}

const scheduledScenes: { [sceneUid: string]: ScheduledScene } = {}

export function renderScene(scene: Scene): void {
  const foundScene: ScheduledScene | undefined = scheduledScenes[scene.uid]
  const deferredTimeout = Date.now() + ADDITIONAL_RENDER_TIME_IN_MS

  if (!foundScene) {
    scheduledScenes[scene.uid] = { scene, timesOutAt: deferredTimeout }
  } else {
    foundScene.timesOutAt = deferredTimeout
  }
}

export function shouldRenderScene(sceneUid: string): boolean {
  const now = Date.now()
  const foundScene: ScheduledScene | undefined = scheduledScenes[sceneUid]

  const hasSceneTimedOut = Boolean(foundScene && now < foundScene.timesOutAt)

  return hasSceneTimedOut
}

Then we need to adjust our render loop:

    this.engine.runRenderLoop(() => {
      if (shouldRenderScene(this.scene.uid)) {
        // measured to have a sub-millisecond cost if canvas hasn't been resized
        this.engine.resize()
        this.scene.render()
      }
    })

And here’s an example of how we use it:

export class PerspectiveCamera extends ArcRotateCamera {
  // ... blah blah, feel free to ignore all this code
  public zoom(delta: number): void {
    this.radius += delta * (this.upperRadiusLimit || 0)
    this.radius = Math.max(
      this.lowerRadiusLimit || 0,
      Math.min(this.upperRadiusLimit || 0, this.radius),
    )

    renderScene(this.scene) // <-- any time the scene changes we must remember to call this!
  }
}
2 Likes