How and when to use mesh.freezeWorldMatrix()?

Hello once again,

I have lot of level meshes inside on glb file which i loaded. What I am trying to do basically generate level track procedurally. means, I am picking one mesh from an array, modifying its postion & rotation and that’s pretty much it. I want to increase performance of this game and came to know about mesh.freezeWorldMatrix() which I am calling as soon as I load all meshes and their children. But I don’t see any time reduction in computeWorldMatrix() in profiler. what could be the reason? I also wanna know, considering I am changing position & rotation often, should I call freeze function then?

Make sure you update the position/rotation/scaling of your meshes by updating the properties of the position / rotation / scaling and not by replacing them.

That is:

mesh.position.x = 34;

and not:

mesh.position = new BABYLON.Vector3(34, 0, 0);

When you replace one of these properties by a new object, the matrix is recomputed even if frozen.

If you don’t update the position/rotation each frame, calling freeze should be a gain so you should do it.

1 Like

Hi Evgeni, you always been very helpful. thanks a lot for that. I wanted to ask one more thing, I have read somewhere that evaluateActiveMesh takes most of the time in frustum culling. Can I disable fructum culling because right now, I am generating tracks at a distance and as soon as they go behind character I am calling mesh.setEnabled(false). do you think its a good idea? will it speed up the evaluateActiveMeshes() ? thanks. also, i am just trying your suggestion. will keep you posted. thanks.

Yes, calling setEnabled(false) is a good idea if your mesh is not visible because this mesh will be quickly rejected from further processing in _evaluateActiveMeshes.

I have read somewhere that evaluateActiveMesh takes most of the time in frustum culling

Frustum culling is avoided for disabled / invisible meshes and for meshes that will be displayed, you can disable the frustum culling by setting alwaysSelectAsActiveMesh to true.

1 Like

Hey Evgeni, two things I did,

  1. scene.skipFrustumCulling = true
  2. mesh.alwaysSelectAsActiveMesh = true

But I don’t see significant gain in performance in profiler report. The frustum culling enabled version is faster infact. :smiley: , is there anything else left to be done? did i miss anything?

If the frustum culling enabled version is faster it means that the processing of the objects that are not visible (those that would be culled if culling was enabled) takes more time than the gain provided by not doing the frustum culling.

Note that the frustum culling is pretty cheap by default, as it’s only a bounding sphere check, so it’s not unexpected that depending on your scene disabling culling may have adverse effects.

If you really have a lot of meshes and _evaluateActiveMeshes takes most of the time, then I fear you will need to reduce your mesh count… Either by using mergeMeshes or thin instances if possible.

1 Like

okay, just one last thing,

I have made sure that mesh.position is not getting overwritten with new one for freezing world matrix computation. I am also rotating mesh with mesh.rotateAround method. roateAround will not overwrite the mesh.rotationQuaternion or mesh.rotation right?

If you did not use rotationQuaternion in the first place for your mesh, it will be set with a new object the first time you call rotateAround. Then the existing rotationQuaternion property is updated and not recreated for subsequent calls.

2 Likes

Hey Evgeni,

here’s the scenario I have, as I said, I have level pieces(meshes) and I am picking pieces one by one changing its position & rotation and placing them so it will feel like an endless world. also keep in mind that I am manually enabling them while placing them and then disabling them as soon as they go behind player.

now, to optimize things around at the start of the game this is what I did,

scene.freezeActiveMeshes(); 
scene.skipFrustumClipping = true;

okay, now the moment I take a piece(mesh) to change is position from an array this is what I do,

    mesh.position.addInPlace(offset);
    mesh.rotateAround();
    mesh.freezeWorldMatrix();
   mesh.alwaysSetAsActiveMesh = true;

and when mesh goes behind the character…

mesh.unfreezeWorldMatrix();
mesh.alwaysSetAsActiveMesh = false;

also during loading I also disabled doNotSyncBoundingInfo = true for each meshes.

The problem : when called freezeActiveMesh() meshes disappear.(assuming their world matrix is not udated). but what I am trying to do is disable any sort of update for meshes which are placed.

2nd problem : I have disabled frustum culling and set doNotSyncBoudnINfo but in profiler boundingInfo.update is still being called. its taking big chunk of time.

what I want is I want to update mesh data (world matrix,bounding info or whatever) for once when placing the piece. how can I achieve that and reduce evaluateActiveMesh time. Thanks.

You should pass true to this call, this way you will avoid a loop on the meshes. That’s a loop over the active meshes that call computeWorldMatrix() on each one. As matrices are frozen this call returns early but it’s still something you may want to avoid.

As you have frozen the active meshes, scene.skipFrustumClipping = true and mesh.alwaysSetAsActiveMesh = true will do nothing because those properties are checked inside the _evaluateActiveMeshes function to decide if a mesh is in the frustum or not.

It’s ok in the playground: Babylon.js Playground

I think your scene.meshes list is empty when you call freezeActiveMesh() or the camera is not setup correctly and no mesh are in the frustum.

Hard to say without a repro… You should try to see the tree of the callers to see which function issues the calls.

If calling scene.freezeActiveMeshes(true) you should see ~0s spent in this function except if you have particle systems because those ones are still animated even when the active meshes are frozen.

1 Like

Thanks. also do I have to call scene.freezeActiveMeshes(true) before rendering right after updating position/rotation of the mesh?

That’s two different things.

scene.freezeActiveMeshes(true) simply takes the current list of active meshes (meaning, the meshes that are in the frustum and visible/enabled/… at the time you make the call) and this list will be used for rendering for all subsequent frames, whatever you do with the meshes. That means if you move a mesh that wasn’t in the frustum (so not in the active list when you froze it) so that it should now be visible, it won’t be visible because it is not in the active list.

So it’s up to you to call freezeActiveMeshes when you need to based on the information given above. If you want the active mesh list to always be up to date, so whenever you update a mesh you should call unfreeze before the update and freeze after. Of course, if you call these methods too often you would better not called them at all because it could be counter productive.

1 Like

Hi, this is an old topic, but I have a question about this specific post. Maybe I am misunderstanding, but, as per babylon documentation, “You can improve performances by freezing this matrix. Any subsequent changes to position / rotation / scaling will then be ignore:”
https://doc.babylonjs.com/features/featuresDeepDive/scene/optimize_your_scene#reducing-world-matrices-computation

From what I tested that’s not actually true, as changing rotation and scaling for example will still be applied if it’s done by an animation or in the inspector. Are there some overrides in these cases?

1 Like

It’s a bit weird that they are different, but it’s expected as @Evgeni_Popov explained in the second post: How and when to use mesh.freezeWorldMatrix()? - #2 by Evgeni_Popov. If the position, rotation, or scale vectors are replaced completely, it will still recompute the world matrix even if frozen. Depending on what’s happening in the code for position, rotation, scale, it might replace the vector completely or update it. Perhaps there is an argument to be made that we should always update instead of assigning a new vector though in the inspector / animation code.

2 Likes

Thanks for the reply. I have to say that from a users perspective I find it very confusing to have documentation say one thing and then have to go on the forum, go through posts to see that the case isn’t actually that cut and dry.
Also, the vectors aren’t replaced, the values are updated.
I think the best description would be “You can improve performances by freezing this matrix. Any subsequent changes to position / rotation / scaling will be applied and the matrix unfrozen. So only use freeze() when not updating transform.”

1 Like

cc @PatrickRyan for the doc update if we have a discrepancy

1 Like

If I’m not mistaken, I think the issue the imprecision of the word “change.” When frozen, the change that triggers a recalculation is replacing an object (that is, using a setter function, an assignment to the property as a whole vector or quaternion). Any “change” that bypasses the setter will not retrigger the recalculation. Examples include accessing the vector or quaternion properties and assigning those directly (such as position.x, rotationQuaternion.w) or functions that place values into the object such as copyFrom, InPlace, ToRef.

Avoiding the unqualified term “change” may be prudent to avoid confusion.