Jump to content

computeWorldMatrix is very slow when scaling in large scale scene


yokewang
 Share

Recommended Posts

It's very fast without scaling or the number of mesh is small(less than 1000). But with scaling, the more meshes, the slower. 

https://playground.babylonjs.com/#BU5QQR#4  it's fast with out scaling. 234ms for my computer.

https://playground.babylonjs.com/#BU5QQR#3 it's very slow when scaling. 2607ms . And when size change to 30, it will be timeout.

Why?

Thanks.

 

Link to comment
Share on other sites

I have tested the performance for different number meshes.

number     no-scaling     scaling  

1000             31ms           61ms

3375              80ms           587ms

8000              291ms         3276ms    

15625           470ms         20615ms

The time of no-scaling is almost linear to the number of meshes.

But with scaling, the time is exponential increasing.

It's confused. 

 

Link to comment
Share on other sites

Each time you set a new scaling value, the WM is marked as dirty, meaning it has to be recomputed from scratch what is a heavy operation. If don't set any scaling (nor position, nor rotation) value, the WM isn't recomputed but is got from the cache, even by calling computeWorldMatrix().

The fact that the performance falls down with the increasing number of meshes is not surprising : the more meshes, the more computations. The fact the time increasing is no linear isn't surprising either : this is the way it happens when it overcrosses the limits of the JS engine and the CPU capabilities.

For you very example (a huge bunch of spheres with different scalings), I would recommand to use a SPS (single particle system) what uses a single global WM and a single draw call to be rendered instead of thousands of individual meshes.  

Link to comment
Share on other sites

I found some clues from the source code. If scaling.isNonUniform, it will iterate every mesh in the scene to _markAllSubMeshesAsDirty(I don't know why) when call computeWorldMatrix.

Below is the stack detail.

sphereMesh.computeWorldMatrix();

-->

if (this.scaling.isNonUniform) {
  this._updateNonUniformScalingState(true);
}

-->

AbstractMesh.prototype._updateNonUniformScalingState = function (value) {
    if (!_super.prototype._updateNonUniformScalingState.call(this, value)) {
        return false;
    }
    this._markSubMeshesAsMiscDirty();
    return true;
};

-->

AbstractMesh.prototype._markSubMeshesAsMiscDirty = function () {
    if (!this.subMeshes) {
        return;
    }
    for (var _i = 0, _a = this.subMeshes; _i < _a.length; _i++) {
        var subMesh = _a[_i];
        var material = subMesh.getMaterial();
        if (material) {
            material.markAsDirty(BABYLON.Material.MiscDirtyFlag);
        }
    }
};

-->

Material.prototype.markAsDirty = function (flag) {
    if (flag & Material.TextureDirtyFlag) {
        this._markAllSubMeshesAsTexturesDirty();
    }
    if (flag & Material.LightDirtyFlag) {
        this._markAllSubMeshesAsLightsDirty();
    }
    if (flag & Material.FresnelDirtyFlag) {
        this._markAllSubMeshesAsFresnelDirty();
    }
    if (flag & Material.AttributesDirtyFlag) {
        this._markAllSubMeshesAsAttributesDirty();
    }
    if (flag & Material.MiscDirtyFlag) {
        this._markAllSubMeshesAsMiscDirty();
    }
    this.getScene().resetCachedMaterial();
};

-->

Material.prototype._markAllSubMeshesAsMiscDirty = function () {
    this._markAllSubMeshesAsDirty(function (defines) { return defines.markAsMiscDirty(); });
};

-->

Material.prototype._markAllSubMeshesAsDirty = function (func) {
    for (var _i = 0, _a = this.getScene().meshes; _i < _a.length; _i++) {
        var mesh = _a[_i];
        if (!mesh.subMeshes) {
            continue;
        }
        for (var _b = 0, _c = mesh.subMeshes; _b < _c.length; _b++) {
            var subMesh = _c[_b];
            if (subMesh.getMaterial() !== this) {
                continue;
            }
            if (!subMesh._materialDefines) {
                continue;
            }
            func(subMesh._materialDefines);
        }
    }
};

The last function _markAllSubMeshesAsDirty will iterate every mesh in the scene, so the performance will decrease dramatically.

It's very confusing that changing scaling is related to the material and will iterate each mesh in the scene.

BTW: My babylonjs version is 3.1.1  

Link to comment
Share on other sites

Hello,

when scaling is non uniform we have to process the normals (in the shader) using a different (a slower) path. This is why we need to flag all materials as dirty.

I can add a property on the mesh which could be mesh.ignoreNonUniformScalingUpdate. It should work flawlessly in your case as you are doing everything before the first frame

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...