This tutorial will help you find some links and info on how you can improve your scene regarding rendering performance.
Babylon.js uses an advanced and automatic shaders engine. This system will keep shaders up to date regarding material options. If you are using a static material (ie. an immutable material) then you can let it know to Babylon.js by using the following code:
Once frozen, the shader will remain unchanged even if you change material's properties. You will have to unfreeze it to update the inner shader:
Every mesh has a world matrix to specify its position / rotation / scaling. This matrix is evaluated on every frame. You can improve performances by freezing this matrix. Any subsequent changes to position / rotation / scaling will then be ignore:
You can unfreeze a mesh with:
If you are CPU bound, you can decide to keep the list of active meshes unchanged and then free the time spent by the CPU to determine active meshes:
You can unfreeze the active meshes with:
Note that you can force a mesh to be in the active meshes before freezing the list with
mesh.alwaysSelectAsActiveMesh = true.
As soon as you can please use instances as they are drawn with one single draw call.
If sharing the same material is a problem, you can then think about using clones which share the same geometry with
When dealing with complex scenes, it could be useful to use depth pre-pass. This technique will render designated meshes only in the depth buffer to leverage early depth test rejection. This could be used for instance when a scene contains meshes with advanced shaders.
To enable a depth pre-pass for a mesh, just call
mesh.material.needDepthPrePass = true.
By default Babylon.js uses indexed meshes where vertices can be reuse by faces. When vertex reuse is low and when vertex structure is fairly simple (like just a position and a normal) then you may want to unfold your vertices and stop using indices:
For example this works very well for a cube where it is more efficient to send 32 positions instead of 24 positions and 32 indices.
By default, Babylon.js will adapt to device ratio in order to produce the best possible quality even on high-DPI devices.
The drawback is that this could cost a lot on low-end devices. You can turn it off with the fourth parameter of the Engine constructor:
var engine = new BABYLON.Engine(canvas, antialiasing, null, false);
In the same constructor, you may also want to turn off antialiasing support with the second parameter.
Babylon.js processes speed depending on the current frame rate.
On low-end devices animations or camera movement may differ from high-end devices. To compensate this you can use:
The return value is higher on low frame rates.
Starting with version 3.1, Babylon.js can handle WebGL context lost event. This event is raised by the browser when the GPU needs to be taken away from your code. This can happen for instance when using WebVR in hybrid scenario (with multiple GPU). In this case, Babylon.js has to recreate ALL low level resources (including textures, shaders, program, buffers, etc.). The process is entirely transparent and done under the hood by Babylon.js.
As a developer you should not be concerned by this mechanism. However, to support this scenario, Babylon.js may need an additional amount of memory to keep track of resources creation. If you do not need to support WebGL context lost event, you can turn off the tracking by instantiating your engine with doNotHandleContextLost option set to true.
If you created resources that need to be rebuilt (like vertex buffers or index buffers), you can use the
engine.onContextRestoredObservable observables to keep track of the context lost and context restored events.
Instrumentation is a key tool when you want to optimize a scene. It will help you figure out where are the bottlenecks so you will be able to optmize what needs to be optimized.
The EngineInstrumentation class allows you to get the following counters:
instrumentation.captureGPUFrameTime = true.
instrumentation.captureShaderCompilationTime = true.
Here is an example of how to use engine instrumentation:
Please note that each counter is PerfCounter object which can provide multiple properties like average, total, min, max, count, etc.
The SceneInstrumentation class allows you to get the following counters (per scene):
instrumentation.captureActiveMeshesEvaluationTime = true.
instrumentation.captureRenderTargetsRenderTime = true.
instrumentation.captureFrameTime = true.
instrumentation.captureRenderTime = true.
instrumentation.captureInterFrameTime = true.
instrumentation.captureParticlesRenderTime = true.
instrumentation.captureSpritesRenderTime = true.
instrumentation.capturePhysicsTime = true.