Optimizing the scene for a huge amount of simple meshes

Hi all,

I would like to council you regarding scene optimization.

However I am looking for additional leverages.

I think my situation is somewhat specific.

  • I am drawing a huge amount of simple meshes
  • Imagine a warehouse with a lot of boxes in the shelves. (Shelves and boxes are instanced wherever possible).
  • The shelves are all instanced (I spawn a shelve prototype and then create instances of those prototypes)
  • The products in the shelves can be displayed on demand. Those are drawn as simple cubes with different colors (5-10). There might be up to 30k boxes (all instanced, such that I have only as much “non-instanced” meshes as I have colors (5-10).
  • Things become very slow (fps) if I spawn the boxes. (I am a bit surprised as according to my perception the shelves are harder to render then the boxes, however it seems to be contrary)

As mentioned I am looking for additional leverages.
Which settings would you advise me to test / which are important in your experience?
Or more in general do you have tips for me how I can discover performance issues?

Some other ideas I wanted to test (maybe you could evaluate those before I waste time ^^):

  • Create a sphere around the free cam (“horizon”) to reduce the number of meshes visible
  • Displaying the boxes on an transparent offscreen canvas and sync the position of the free cam - However I am not sure how it will behave, if one scene is faster then the other.

Thank you in advance.

2 Likes

You should first look for where the time is spent by using the Performance tab (if using Chrome).

With so many meshes, it’s expected that scene._evaluateActiveMeshes will take some time. So, one thing you can do is helping the _evaluateActiveMeshes function by reducing the work it must do.

For eg, you could check that shelf X is visible (check the bouding box against the frustum - mesh.isInFrustum()):

  • if not, call .setEnabled(false) on it and on all products pertaining to this shelf
  • if yes, call .setEnabled(true) and .alwaysSelectAsActiveMesh = true on it and on all products pertaining to this shelf

It would even be better to reconstruct the scene.meshes array by putting in only meshes that you determined as being visible through the process described above.

Maybe the isReady functions of the materials take also some time: you can call .freeze() on them to help things.

But as said above, you should start by checking what really takes some time before going any further.

4 Likes

Hi @Mykyta_Fedorovskiy, you could use instance buffers (you can add parameters to instances - like the color). More details on the video: Weekly Video: Fun with Instance Buffers

3 Likes

Hey guys,

thank you very much for the advices.

@thomlucc I will definitely be able to reduce the number of non-instanced meshes even furher. So thank you here.

@Evgeni_Popov:

  • you are totally right: _evaluateActive meshes takes ~70% of the renderLoop

  • I am a bit surprised that it is called several times (see Screenshot below). Is it normal or do I have a glitch somewhere?

  • In general I have a quite suffisticated datastructure behind the scene. So I can tell anytime which boxes are in which shelves.

  • Still I have a question:
    (1) Shell I do this directly in the render-loop or is this there a better way?
    (2) As far as I understand babylon does not render not visible meshes (i.e. behind a Wall). will I loose that or is mesh.isInFrustum() that smart?

Here is an example for one of my scenes to boost your imagination:

1 Like

_evalauteActiveMeshes is called every frame UNLESS you can call secene.freezeActiveMeshes()
This will boost your perf by a order of magnitude :wink:

2 Likes

As @Deltakosh said, calling scene.freeActiveMeshes() will help tremendously for _evaluateActivesMeshes as this will make this function takes virtually 0 time.

However, the problem is that the visible mesh list won’t be reconstructed according to the camera view. So, to be usable in your case, you should flag all meshes with .alwaysSelectAsActiveMesh = true and then call scene.freeActiveMeshes(). It means all meshes will be rendered, whatever the camera view is.

It’s up to you to see if it’s better to have all meshes rendered at each frame and avoid the work done by _evaluateActivesMeshes, or let the latter do its job as usual.

Seing your screenshot, I think you will get the best performance by grouping all meshes into a single mesh (Mesh.mergeMeshes) (try to have as few different materials as possible).

Does it mean the product must disappear from the shelf? If yes, you would need to remove the box from the geometry indices of the merged mesh.

Use for eg scene.onBeforeRenderObservable to update the scene.meshes list (if you don’t merge meshes).

Yes it does (if the object is at least partially in the frustum, meaning it is sent to the GPU for rendering). It’s the zBuffer that will make the object not visible in this case, because the wall is in front of it.

You can implement your own culling strategy to fill the scene.meshes list (managing “by hand” the product visibility inside a shelf is a first step), but I don’t see a lot of possibility for your scene, except may be by having bounding boxes around big structures (each shelf (+products inside), big white thing on the right in your screenshot, …) and testing the visibility of these boxes yourself: if not visible, all meshes inside the bouding box are not visible.

4 Likes

Awesome work dudeeeee!!!

Nice post I going to use some of the ideas here.

Kudos to @Evgeni_Popov!!

1 Like

and anyway, for the still selected meshes, use the boundsphere_only culling strategy
https://doc.babylonjs.com/how_to/optimizing_your_scene#changing-mesh-culling-strategy

it increases by far the selection time of evaluateActiveMeshes()

when using instances, use instanceArrayBuffers and skip the instance WM matrix automatic computations for immobile instances : Mesh - Babylon.js Documentation

another approach : if lots of certain meshes don’t move each frame, you can also merge them in a SPS Solid Particle System - Babylon.js Documentation

3 Likes

Hey guys,

thank you very much for your help. With your tips I managed to nearly tripple the frame rate. This was very helpful!!!

I also wanted to share my strategy a bit (may be useful for someone):

As suggested by @Evgeni_Popov in the first post I influence babylons culling strategy:

  • After spawning an object as a rack ( = lots of paranted & instanced meshes) I do the following: (1) I refreshBoundingInfo (2) and then doNotSyncBoundingInfo & freezeWorldMatrix.

  • My objects are mostly a composition of different paranted meshes. The bounding information is refreshed as suggested here: [solved]how to get boundingbox info of a parent mesh with all children size - Questions & Answers - HTML5 Game Devs Forum

  • My custom culling strategy works as follows:

    • I introduced two distance contraints one for building elements (as walls) one for more complex objects (as racks or workers).

    • Before rendering I loop all of my objects (f.e. rack, the position of which is static and cached) and derive the distance to the object. If it is smaller then the defined value, I check the parentMesh.isInFrustum(frustumPlanes) condition.

    • (The bounding Info of the parent mesh is updated uppon spawning as mentioned before)

    • If the conditions are met I execute (1) setEnabled(true) and (2) alwaysSelectAsActiveMesh = true for all meshes belonging to the object (otherwise contrary).

    • To improve the .isInFrustum Performance i set the boundsphere_only culling strategy for all meshes upon spawning as suggested by @jerome.

Thanks again :+1: :+1: :+1: !

8 Likes

Can instance buffers be transparent?