General Guidelines
Here are some tips and hints on how to achieve good performance in your PlayCanvas app.
JavaScript
- Calling 'new' to allocate a JavaScript object (particularly vectors, matrices and quaternions) represents a dynamic allocation and can be expensive. Therefore you should, where possible, preallocate objects in a script's initialize function and reuse them in the update function. It also leads to Garbage Collection which can cause periodical freezes.
Graphics - CPU
- In PlayCanvas, a mesh instance is a draw call (a command to draw an individual graphical primitive). Each draw call requires some effort on the CPU to dispatch to WebGL. Therefore, keeping the number of draw calls low is advisable, particularly on mobile. You can see a list of the draw calls for a particular Model by selecting the model asset and viewing it in the Inspector. 100-200 draw calls is a rough target for low end mobile devices. High end desktop machines on the other hand can process thousands every frame and still maintain 60fps.
- Use Batching to reduce draw calls. By creating Batch Groups in your Project and assigning them to Render, Model and Element components, the engine will try to merge them in as few mesh instances as possible, reducing draw calls and increasing performance.
- Try to keep the number of shaders generated by your app as low as possible. Shaders have to be compiled and linked on demand and this operation is expensive, causing delay in app startup and glitches in frame rate. If material A has an emissive map but material B doesn't, two shaders will be generated. If you set a black emissive map on material B, the materials can share the same shader. Reducing the number of materials in your scene should also reduce the number of generated shaders.
- For skinned meshes, the engine generates precise bounding box, required by the camera frustum culling, each frame. This operation has a cost for each bone, and executes even when the character is completely outside of the view frustum. To avoid this cost, consider setting up a custom AABB for the character, which is a property of the
Render
orModel
component. - Only enable frustum culling on a camera component if, on balance, it is likely to save more performance than it costs to calculate visibility. If you are rendering a scene where all mesh instances are always visible, disable this option.
Graphics - GPU
- Be careful when enabling 'Use Device Pixel Ratio' in your project settings. This will cause your PlayCanvas app to utilize the native resolution of a device reducing pixelation but can result in many more pixels being filled, which can cause a significant drop in frame rate. This can be adjusted at runtime after assessing the user's device capabilities. Read more at Adjusting Device Pixel Ratio.
- Be mindful of the number of dynamic lights in your scene. Keep them to a minimum.
- As the value for texture anisotropy increases, visual improve but performance decreases. Be careful to balance visuals against performance.
- Look for opportunities to pack multiple textures into single images. For example, a grayscale opacity map can be stored in the alpha channel of a diffuse map. Or a grayscale gloss map can be stored in the alpha channel of a specular map. This results in lower VRAM usage.
- Post effects can be expensive so think carefully before you enable them. They can cost a lot in terms of pixel fill.
- Enabling backface culling on a material will be cheaper than disabling it. Generally speaking, backface culling reduces the number of pixels that the GPU has to fill. This is the default setting for newly created materials.
Graphics - CPU and GPU
- For applications where there is little visual change over a period such as product configurators, there is a special property to reduce CPU and GPU usage.
pc.Application#autoRender
can be set tofalse
so that frames are rendered on demand viapc.Application#renderNextFrame
when there is a visual change such as the user moving the camera or adding a part to the product. - Enabling shadow casting on dynamic lights is expensive. Omni light shadows are particularly expensive. For each omni light that casts shadow, the scene must be rendered 6 times into a shadow map.
- Keep the number of blended mesh instances in your scene to a minimum. Blended meshes are deferred until all opaque mesh instances have been dispatched and are then submitted in back to front camera depth order. This results in pixels being filled multiple times and can result in a lot of render state changes since blended meshes cannot be sorted by material.
Physics
- Collision meshes do not need to be the same level of detail as the renderable mesh. It is recommended that you set a lower resolution mesh for collision.
- Keep the number of dynamic rigid bodies in your scene to a minimum, particularly on mobile.