Hey everybody.
This thread is going to be a mix of me sharing what I managed to figure out so far and asking for advice to push the optimization even further.
Here’s a playground of a scene similar to what i got locally and it implements some of the optimization methods talked about below.
https://playground.babylonjs.com/#DKY6VF#6
In our project we have a field of up to 10k mesh instances of a very low poly mesh created with createInstance()
. It’s basically a box with a small bevel and very simple.
The blocks act as a playing field and allow characters to move block by block (like a chessboard).
The blocks all share a texture, but can have a different base color that mixes together with the texture to allow a tint that can change on runtime which is achieved by Instance buffers.
Thanks to using instances we only have 1 draw call for the entire field, but the frame rate was still really bad in the beginning.
On a beefy pc we barely got ~60fps when the entire field is visible and the camera is still. When moving the camera it drops to 40~50 fps.
On an Iphone 6 it was ~19 fps when not moving the camera and sub 10 fps when the camera is being moved. Ideally we’d like the game to run at stable 60 fps on pc and more recent smartphones. The field seems to be the biggest hurdle but some calculations, particle effects and such will also dig into performance a little bit later.
When looking at the chrome profiler I could see that the functions in the render loop which take too long and cause the frame rate to drop are evaluateActiveMeshes
and renderForCamera
as you can see in the image.
At first I tried using an Octree, however I found the documentation for this quite confusing and am not even sure if my implementation is correct.
https://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
var octree = this._scene.createOrUpdateSelectionOctree();
meshes.forEach(mesh => {
octree.addMesh(mesh)
});
After implementing this the performance actually got worse and i lost about 20fps.
1. How do you properly implement an octree for static meshes and does it even make sense in a case like this where it’s one open field?
After abandoning Octrees I tried many methods that are listed in https://doc.babylonjs.com/how_to/optimizing_your_scene , however most of them had no effect whatsoever in my case. The only thing that seemed to lead to a slight improvement was mesh.freezeWorldMatrix();
which saved about 2 milliseconds.
2. Should these functions be run on the mesh after you load it in or on each instance of it after you created it? e.g. mesh.material.freeze();
mesh.freezeWorldMatrix();
I then stumbled upon this amazing thread which describes a situation which is pretty close to mine.
https://forum.babylonjs.com/t/optimizing-the-scene-for-a-huge-amount-of-simple-meshes/7398
Using the following allowed me to halve the time everything takes and have smooth 60 fps on pc even when moving the camera!
this._scene.freezeActiveMeshes();
meshes.forEach(mesh => {
mesh.alwaysSelectAsActiveMesh = true;
});
However on smartphone it crashes when creating the mesh instances
I made a Google doc where I make a before and after comparisons for each optimization method and compare the performance in the profiler: Google Doc
Some additional questions:
3. When I create instances of a mesh does the performance decrease exponentially the more instances I have? (For example would a scene with 10k cubes run smoother than a scene with 10k cubes that have a bevel and a few more vertices?)
4. Do you guys think it’s even possible to get a scene with this many mesh instances to run on ~60 on mobile or is that a pipe dream?
5. One thing I haven’t tried yet is SceneOptimizer, would it make sense in a case like mine?