Optimizing the render() call

Hi, we’ve recently converted our app from Three.js to Babylon, however there are some users who are saying that they get a lower FPS in the Babylon version. I’ve been optimizing various things, but there’s one piece that I can’t seem to sort out… It seems that Babylon’s render() function simply takes double as much CPU, sometimes more, than Three.js’ one… Is there a way to optimize this? I’ve already tried everything in the Optimize Your Scene page…

To test this at the most basic level, I created two examples which both simply create 5000 cubes (going overboard to make it more obvious) and then renders them. On my machine the Three.js version renders at a consistent 13ms per render(), while the Babylon version varies at around 22-34ms per render()…

I have also tried using freezeActiveMeshes() and it brings it down a lot, but not all the way to Three.js’ level, plus we wouldn’t be able to use that anyway since as I understand it, it turns off the detection of which objects are in the frustum for rendering…

BabylonThreeComparison.zip (1.0 MB)

2 Likes

This is something that will interest @sebavan and @Deltakosh

I’m not an expert on 3js but one thing could help us here:

Can you drop here the 3js code and create a pg with Babylon code?

I want to make sure that we are comparing apples to apples

If this is the case we obviously have a bug somewhere and I’ll fix it faster than light:)

1 Like

Hi, the code for both examples is attached in the first post. I wanted to make a playground, but I don’t think I can wrap a timer around the render() function from inside a playground, the playground seems to call render() on it’s own outside the user’s code…

In case it helps, you can make your own render loop inside the PG with code like this:

    //Override the playground render loop
    setTimeout(() => {
        engine.stopRenderLoop();
        engine.runRenderLoop(() => {
            console.log("render");
            scene.render();
        });
    }, 0);

Example:

1 Like

ok bjs version: Multi Scene Swapping with GUI | Babylon.js Playground (babylonjs.com)

Cool, looks like the render() timing is the same as for the non-playground example

Ok! so first thanks a lot for the feedback! It helps a lot

So first comment: The test as it is right now is a bit misleading because all meshes have the same matrix and then there is no activity on the 3js front where we do not spend time optimizing for this very specific case. A more “fair” comparison would be to use random position to avoid having the same matrix for all meshes: box.position.set(Math.random(), Math.random(), Math.random())

Then, you are not really comparing apples to apples as our material has more features enabled (box.material.dissableLighting = true has a typo :)). I would recommend using material.freeze() to get a comparable material

I updated the PG:
Multi Scene Swapping with GUI | Babylon.js Playground (babylonjs.com)

With these updates you should get much closer to 3js.

But now we hit the elephant on the room :slight_smile: 3js and babylonjs are very different engines with different goals and philosophies. Here you are seeing one difference: bjs has a lot of features available out of the box, easy to use but it comes with a price: a small perf hit

We introduced the scene.freezeActiveMeshes() feature for this specific need: turn off all the bells and whistles to focus on pure rendering.

But you raised a good point: In that mode there is a missing point: we have no culling.
Well…we HAD no culling :wink: Thanks to you we added the option with this PR:
Remove some unnecessary computations by deltakosh · Pull Request #12421 · BabylonJS/Babylon.js (github.com)

Thanks again!

5 Likes

Thanks, that helps a lot :+1:

But I think I don’t understand what an “active” mesh is, then… I thought active meshes were the ones that were in the frustum and so get rendered, but is it something else?

when you call freezeActiveMeshes you are forcing BJS to stop doing a lot of stuff :slight_smile: In your case you may not want these things. The active meshes are the rendered meshes. They evolved constantly based on several options (instances, transparency, etc…)

To keep them as precise as possible costs us a lot per frame,hence why having this freezeActiveMeshes is a good option. THis way you are the driver: you decide which mesh is active and the list remains constant. You can force a mesh to be active by setting mesh.alwaysSelectAsActiveMesh = true and you can force it to be inactive with mesh.setEnabled(false) for instance

Ok I see, thanks…

Hi @Deltakosh, I’ve tried to call unfreezeActiveMeshes();freezeActiveMeshes() every 1 second as a test and it really helps to reduce the CPU usage a lot, like to around ~50% usage. I do see the culling issue, but that’ll be fixed once your PR is on npm I guess…

My question is, is there anything bad about doing this? One issue I have noticed is that it seems to break animated models, is there a way for animations to continue working while it’s frozen? And is there anything else that would be an issue?

I think you can simply call mesh.skeleton.prepare() each frame to have your animations going on even when the mesh is frozen.