Performance Difference between Clone, Instance, and Containers

Context:
What I am trying to do is create a PrototypeFactory I can use to produce copies of glTF loaded assets within my scene to create a 3D environment: use assetManager to load once, and reuse the glTF model everywhere. Things such as FloorTiles are in abundance and I’m trying to evaluate what would be the best performance

Question:
What are the pros/cons to the following cloning objects/meshes methods? I am trying to figure out the best approach that would cover the needs I have, but I don’t know what I loose/gain with each copy approach.

  1. TransformNode.Clone()
  2. assetContainerTask.loadedContainer.instantiateModelsToScene()
  3. Use an invisible “root” mesh and use rootMesh.createInstance()
  4. Use rootMesh.thinInstancesAdd()

All this depends on the specific use case. Actually there is 3 possibilities to improve performance when you have a lot of identical meshes: clone, instance, thinInstance. For the best undestanding read this section - Copies, Clones, and Instancing | Babylon.js Documentation

For the whole GLTF model it is better to speak here about rootMesh.Clone(). But it is possible to use TransformNode as well.
Cloning is the “slowest” method (regarding the performance), but it allows to use different materials and even to change geometry in cloned meshes. Be aware that this method by itself will not clone neither skeleton nor animations (if there are any), you need to worry about it yourself.
Here is a small example with cloning GLB barrel model and changing clone’s geometry - https://playground.babylonjs.com/#4AJ16M#823

This method is able to create instances or clones (doNotInstantiate parameter). What is wonderful here is that it allows to make the whole copy of your GLTF model with all hierarchy, animations, skeletons etc and you don’t need to write all this stuff by yourself - https://github.com/BabylonJS/Babylon.js/blob/2291509fc9644ebf6faeaa7c3d9b8ccff7e43168/packages/dev/core/src/assetContainer.ts#L292

This method will create instance of the given mesh, but will not instance its children. In order to do that one needs to use rootMesh.instantiateHierarchy() - Mesh | Babylon.js Documentation
(and again, you may choose between cloning and instancing here with doNotInstantiate parameter).

This will not work, you need to iterate over all child meshes and position your thin instances through matrix operations. Example with 25 thin instances added to the same barrel PG example - https://playground.babylonjs.com/#4AJ16M#822 As one may see, the number of draw calls is the same (10) as here.

Thin instancing is the most performant way to render a huge number of identical meshes.
Regular instances still have a performance penalty on the javascript side because each instance is its own object (InstancedMesh): if you have 10000 instances in your scene, the engine must loop over all those objects to make a number of processing (visibility check, etc).

Thin instances don’t create new objects so you don’t incur any penalty on the javascript side by having thousands of them. This performance increase does come with a cost:

  • all thin instances are always all drawn (if the mesh is deemed visible) or none. It’s all or nothing.
  • adding / removing a thin instance is more costly than with InstancedMesh

More about thin instancing - Thin Instances | Babylon.js Documentation

Also, most of the functions which are enabled for ‘usual’ meshes and instances by default will not work for thin instances without enabling them specially. For example, to turn on mesh picking for thin instances there is thinInstanceEnablePicking parameter. Example - https://www.babylonjs-playground.com/#RC2IAH#1
Since all thin instances of a mesh are just one object, collisions also behave differently; one may need to use invsible collider.
Also, have a look at Thinnizator by @roland - https://forum.babylonjs.com/t/thinnizator-automatic-thin-instances/22120a

Summing-up:

  1. Clones are the less performant but most flexible
  2. Instances are the golden middle. all of the instances to share a material, again to manage draw calls, but remember you have custom buffers to pass information to specific instances. You can use the custom buffers to add variation to your instances as you can see in this Medium article.
  3. Thin instances are the most performant after creation but have some limitations.

Hope it will help :slight_smile:

9 Likes

Great writeup @labris !

1 Like

@labris Exactly what I was needing to evaluate my approach. Thank you!

1 Like

@labris too much free time :wink:

We could finish the advanced examples for YUKA. How about that buddy?

2 Likes

Dive looks the most prominent for the further development - DIVE

Ok, I can do this in typescript if you don’t insist to work on it. Do we have the soccer example ready?