[Performance] What is eating up frametime in this scene with only simple 3d hexagon meshes?

IDK about desktop Mac, can only confirm on my laptop…

I tried using “scene.useRightHandedSystem = true;” in the PG with latest BJS version and WebGL 2 enabled, and that does fix the issue. Draw calls is down to 2 and FPS is at 60FPS (absolute at 800FPS), and GPU frame time under 1 ms. :slightly_smiling_face:

1 Like

Glad it fixes it but I do think there’s still a problem as doing 529 draw calls should not take 55ms on the GPU…

2 Likes

Agree there’s still a problem since that performs much worse than on version 4.2… Let me know if I can help with more testing… :beers:

PS, there’s another perf issue further up the thread about CPU usage, before I noticed the Mac issue on one of the posted PG’s and sorta hijacked the thread LOL… :open_hands:

1 Like

It did get off the rails a bit!

The scene, even with instances, seems to still be doing 1 draw call per mesh. However, @Evgeni_Popov note on scene.useRightHandedSystem = true; , dropped the draw calls to 2, and boosted the absolute FPS from ~230-> 500!!

What is this even doing, and why is it necessary?


Lets summarize the perf improvements thus far, thanks to the contributions of @roland & @Blake @Evgeni_Popov starting with a scene with no performance help.

  1. freezeWorldMatrix() +32%
  2. Using Instances: +36%
  3. Freezing Materials: +24%
  4. useRightHandedSystem: +73% :fire:

My concern is that this is essentially an empty scene for my use case, there will be scenery, building, moving objects, roads…etc all added that will bring the complexity up. It’s CPU bound, which means the game logic will have to fight for CPU time, potentially degrading it to the point of unsuitability on mid-low end mobile devices. And even on higher end devices, the power usage on this scene with only hexagons is through the roof.

My questions:

  • What eats so much CPU time?
  • Why does useRightHandedSystem fix drawcalls and nearly double the FPS?
  • How could I better setup the scene to be more performant in the first place?

Note: I’ve put this updated info in the OP.

3 Likes

Actually it was @Evgeni_Popov’s fix to use useRightHandedSystem to get all of the instances to draw in one draw call. IDK why it’s needed exactly but it seems like ideally it could be handled automatically for us somehow, but I guess it gets more complicated when the scene has some GLB models that use right handed and some other models in the scene that use left handed… :thinking: :beers:

Thanks for the correction, comment updated with more appropriate attribution.

Looks like blender is right-handed, so I guess that makes sense. Though I’m surprised 500 drawcalls would chew up so much CPU time. I’m expecting a good 50-100 model variations in scene, so it would be reasonable for me to expect 100+ drawcalls in a more fleshed out scene.

1 Like

useRightHandedSystem = true is needed for instances to be drawn in a single draw call because else (when useRightHandedSystem = false) the determinant of the world matrix of the master mesh and the determinant of the world matrix of the instances have different signs. When that happens, the instances can’t be batched in a single call and instead a draw call will be issued per instance.

It happens because by default the instances are created (when calling mesh.createInstance()) at the root of the scene graph, meaning their parent == null. If you set their parent to the same parent than their master mesh then it will work because the determinant of their world matrix will have the same sign than the determinant of the master mesh:
https://playground.babylonjs.com/#XEP0ZD#74

If you do that you don’t need to use useRightHandedSystem = true.

Regarding the CPU problem, you should monitor the CPU time by using the Performance tab of the browser: you will be able to clearly see which functions take most of the time in your scene.

3 Likes

Thank you for the explanation, I’m brand new to this, so that is very helpful. I appreciate it!


Good point :+1:, I went ahead and checked.

Looks like for each 7ms frame, ~3ms is spent on Animation Frame Called, the scene isn’t using animations afaik. To be fair, I don’t really know what I’m looking at here, or what my next steps would be to start reducing the frame time.

evaluateActiveMeshes seems to be pretty expensive (And it’s called twice per frame?). If I use scene.freezeActiveMeshes(); that pmuch sorts out all remaining performance problems (~2900 FPS!!), however, that also causes issues with meshes not on screen never actually loading when the camera pans… And I’m guessing I’ll run into more problems with that as the scene becomes more “alive”?

What would you suggest I inspect, change, or look at next?

Edit: I can look into thin instances I think. Though I may have a need for different materials for different meshes, which may be problematic? The same goes with animating individual tiles, will that post a problem there as well?

I stripped out animation related leftovers from the original scene I was modifying: https://playground.babylonjs.com/#XEP0ZD#76

1 Like

Yes, evaluateActiveMeshes is expected to take some time because it loops over all meshes of the scene to evaluate those that must be displayed in the end. To reduce its footprint you will need to reduce the number of total meshes in your scene (thin instances will help if applicable) or freeze the meshes. But as you could see, freezing meshes has some drawbacks.

Have you read this doc page about perf optimization?

In your case, however, it’s expected that this function is one taking most of the time because, basically, there’s no other functions called as the PG is quite simple… Still, using thin instances in this PG will help a lot because you will reduce the total number of meshes from 549 to 1 (but there are also some drawbacks for using thin instances: Thin Instances | Babylon.js Documentation)

however, that also causes issues with meshes not on screen never actually loading when the camera pans

If you expect to always see most of the meshes on the screen (meaning you won’t move in a big level that would only have a small part of all the meshes displayed at any one time), you can set alwaysSelectAsActiveMesh = true for all meshes and freeze all active meshes.

2 Likes

I have read that page! I was trying some of the optimizations listed before starting this thread.


I have completely switched over to thin instances, and the performance difference is astounding!

One perf improvement that helped significantly was using a single material for everything instead of multiple materials. For some reason draw calls just eat CPU time, even just a handful.

Similarly, I didn’t realize shadows doubled the vertice count of the scene if everything is a shadow caster & receiver (Is that expected?). So turning that off for some old devices makes a big difference. Also reducing the shadow-map size can make a drastic change.

2 Likes

Using shadows double the number of vertices that are processed because all the objects that are shadow generators get rendered in the shadow map: so, if all objects are shadow generators you will double the number of vertices (and faces / draw calls) processed.

7 Likes