More objects, more draw calls, more performance loss?

Hi! I’m struggling to understand something on how to optimize my Babylon.js scene.

I’m currently preparing 3D assets of a house to export into a GLB file. Each of the objects pictured here will move independently with physics. However, I’m struggling to finish preparing this scene because I’m trying to cram everything into one GLB, one object/mesh, one material, one image texture, etc.

I’m doing this because I’m under the impression that reducing draw calls is the primary way to make my scene run faster, and I was told that reducing draw calls is done by consolidating as many 3D assets as possible. How much performance will my scene actually lose if I have more than one object instead of a single mesh? What about if I have more than one material or image texture? What if I have one GLB per object with its own individual meshes, textures, and materials?

I basically need help understanding the actual impact of not consolidating each of the certain components in a 3D file. Getting everything into a single object/mesh and material is slowing down my workflow greatly and I would like to know where I can safely cut corners without significantly affecting my scene, especially if Babylon.js is affected by the actual size of these assets and not the number of separate pieces it has to process. Let me know if I need to elaborate this question further and thank you so much!

Have you seen these page?

1 Like

I’m afraid it’s not that ez. Overall, the strategy I guess mostly depends on what your scene is made of and how it is viewed. Are all meshes visible at the same time? Are there meshes identical to others (like your two chairs). Are you targeting lower end systems or mobile? Is the scene rather small or large?

Say you would merge all meshes into a single mesh and a giant wrap texture for all. You will have just one single mesh to load but in the end the count of vertices and indices will just be the total of all your merged meshes. Since this large mesh is always visible, it will always load, even if you are just looking at a very small part of it. Say that instead, you load multiple objects (in a single import or from multiple files I don’t think it matters cause in the end, they are all imported and will be compiled the same). For whatever mesh is not viewed it will not be rendered (simply put). So in this case and up to a certain point, a larger number of distinct smaller meshes will provide better performance unless of course the view accounts all of’em. I believe where you really gain performance boost is when you start working with clones, instances and thin-instances. You can merge clones in BJS and this works very well on lower-end systems. Instances are great for creating copies of objects which do not require too much transformation (else they will be turned into a clone). Thin instances do a great job for a large number of static objects. Aside from these parts and from the link above provided by @JohnK there are a number of options you can try for setting-up the rendering, removing all parts that are not required.
Same goes for textures and materials. You can clone both materials and textures. You should also avoid making unnecessary reloads/duplicates of a same texture. Of course, it takes less time to load a single 2k texture than a bunch of 10 smaller textures. But then again, it takes less time to render an adapted to the object texture when this object is rendered than storing a large texture for parts that are not viewed. Altough, here again, it strongly depends on the rig. A GPU with fast and large memory will handle these large textures probably better than a bunch of smaller ones. Where an older and slow memory GPU will deal better with smaller textures (and clones rather than instances).

There’s always the possibility to merge your meshes in BJS. Something I do very often. Even better, you can merge both meshes and clones together, which gives you great latitude for cloning parts and re-merging these parts with other meshes to create a variety of merged objects made from identical cloned parts and new meshes.

Aside from the above, the draw calls you mentioned don’t just come from the meshes and materials.
There are parts you will want to avoid that can double or triple these draw calls. Lights and real time shadows have a strong impact. FX like the glow or highlight layers are very performance consuming. And the list goes on… Not to mention the physics engine which is probably the first part you would want to set-up properly for your needs.

5 Likes

Hi @wavetro , if you really want to see the impact of your consolidation you should have a look at the Spector.js extension for chrome, I found it very usefull to optimize draw calls. As @mawa said it depends on many things and the best way to understand is to actually view each drawCall.

With Spector you will have a list like this :

Each blue line is a drawCall and if you click on it you will have all the detail of this drawCall including the frame buffer wich is very usefull to understand how everything works

4 Likes

These answers are very helpful, thank you everyone! I’m going to do separate objects with smaller textures and optimize my scene later with these tools. I’m getting the impression that consolidating my 3D objects really only saves on loading time over the internet and not on GPU power (and I will definitely save resources by instancing/cloning that second chair)

1 Like

I believe handling and workflow also has its importance. What worth is a project if you cannot edit and maintain it properly? I personally hate these nonsense merges of parts that have nothing to do together. Like merging all bolts or all chairs in a scene even though they are part of another group and on the other end of the map. This just doesn’t make sense to me. I very much prefer working it like you would in real life. You don’t craft a M8 bolt each time you need it for an assembly. And you don’t glue together all of your bolts. So,I import just one single bolt and next I make copies of it (clones where I want to merge them with the final object - instances or thin instances where I want to parent them or create a bunch of loose ones. In the end, from these single parts (copies and instances) I create what I call ‘prefab objects’ (a merge or a parented group of meshes/clones/instances) forming an object (a chair, a table, a fence…). Next I make instances or thin-instances of this prefab whenever I need it in my scene. Not to mention that on update, if I change any of my cloned or instanced mesh/part, all my objects using this part will instantly update.

1 Like