Possible Unnecessary World Matrix Updates on Hidden Objects

I was looking to get some feedback on a huge performance gain I achieved in the forums and it was suggested by @Mugen87 to bring following topic to github.

https://discourse.threejs.org/t/updatematrixworld-performance/3217

This seems to be related to #14138 but I did not want to distract from the PR.

Anyway, my app was suffering from a performance hit from there being far too many world matrix updates on objects (skinned and regular meshes).

I set matrixAutoUpdate to false and manually update when needed. I also set visibility to false on objects a certain distance from camera. I was expecting to have better world matrix performance from at least the visibilty being false but no.

As a test, I added if( !this.visible ){ return false; } to Object3D.updateMatrixWorld. The results were pretty amazing. Performance increased by no less than 50%% in my app.

I’m not really sure how to proceed. Obviously I can add one line of code to three.js each release without much of an issue. But, what about other users who have a bunch of invisible objects? I’m sure they wouldn’t mind increased performance.

One thing is though… this only seems to work well since updateMatrixWorld traverses down the object hierarchy. This method may be hurting performance in itself. I’m thinking of a SkinnedMesh with many Bones, or really any object with a large set of children.

So yea, should I bother making a PR to return false on invisible objects when calling updateMatrixWorld or wait till this whole situations regarding the method is resolved?

Author: Fantashit

3 thoughts on “Possible Unnecessary World Matrix Updates on Hidden Objects

  1. I’ve been thinking the same optimization, skipping world matrix calculation for invisible object and its children in .updateMatrixWorld(). If I’m right, .updateMatrixWorld()‘s main purpose is to update nodes under a rendered scene in renderer. So it’d be ok for that purpose to skip them because invisible objects won’t be rendered.

    Probably we need to add ‘skip invisible objects’ argument to .updateMatrixWorld() and set it to false as the default not to break the compatibility. Some users may use the method in their user code and expect all node under an object will be updated regardless of their visibility.

  2. If the user’s scene is polluted with many invisible renderable objects, my preference would be to suggest the user redefine THREE.Object3D.prototype.updateMatrixWorld in his application — assuming he is unable to handle his use case in any other way.

  3. @titansoftime Thank you so much for this advice! I got huge performance boost when adding this visibility-test. I use many groups of instanced geometries (rendering lot’s of objects tied to notes in a musical score) where all positions are calculated in the shader, so need to manually cull all groups outside the frustum. Using visible instead of add/remove was lighter and smoother when keeping the scene-graph intact. I also switch visibility on/off when rendering different post-effects like glow.

    I used this way of overriding the updateMatrixWorld prototype so I don’t have to fork and patch:

    (function () {
          let _updateMatrixWorld = THREE.Object3D.prototype.updateMatrixWorld
          THREE.Object3D.prototype.updateMatrixWorld = function () {
            if (!this.visible) {
              return
            }
            _updateMatrixWorld.apply(this)
          }
        })()
    

Comments are closed.