lst60916

Mesh Really Dispose ? Avoid memory issue while rendering meshes which are generated by TOB exporter from Blender.

Recommended Posts

Hi

Thanks for the great BJS and TOB exporter, the meshes could be generated from Blender and rendered in BJS environment efficiently.

 

However I find out something interesting that JS heap increases when the mesh is instanced in the scene. But the heap size could not be decreased when the mesh is disposed. Im curious whether this will cause some memory issues(such as memory leak).:blink:

 

Here is the demo of the system:

https://willlinifm.github.io/Playground_bjs/index.html 

 

The Source code is as the following  or the link :
https://willlinifm.github.io/Playground_bjs/js/Scene.js

scenejs.png

 

If the playground is better for debugging, please try this link.

The implementation and the outcome are nearly the same.

(Click Sphere to generate mesh //// Click ground to dispose it)

https://www.babylonjs-playground.com/#4GQCEL#2

The attachment are the heap snapshots !
 

。The heap1 snapshot is took before the mesh is made an instance by TOB-generated-js file

heap1.png

。The heap2 heap snapshot is took after the mesh is made an instance by TOB-generated-js file. We can find that the heap size is increased.:o

heap2.png

。The heap3 snapshot is somehow hard to understand that when the mesh dispose function is called, the heap size still raise a high level compared to the first snapshot.:wacko: NO~~

heap3.png

My questions are 

1. If the meshes are generated and the Js heap size is increasing at the same time. Will it cause the memory issue and make the system run slowly?

2. How to make the best to decrease the heap size after mesh dispose process.

3. Im not sure, but during watching the snapshot constructor information, I find there may be some looping references within BJS/TOB/MeshFactory structure. Will it cause the GC(Garbage Collection) unable to release memory?

Thank you !

BJS and everything here are awesome!

  

Share this post


Link to post
Share on other sites

1. Depends on your amount of memory and number of meshes.  Keep in mind if you are using a TOB MeshFactory, it will be creating clones, which are for saving GPU memory, not CPU memory.  Also the default dispose() only releases GPU memory, or only use count when using cloning.  More meshes WILL reduce speed though, just not because of memory.  There are 2 cause:

  • The cpu time to loop through each mesh in the scene, do computations, see if it will need to be drawn, etc
  • OpenGL draw calls for each mesh.  This can be avoided using BABYON.InstancedMesh not clones, but not really that useful imho.  They still suck cpu just just like clones.  They save on the draw call, BUT they must be of the same material.  Clones can change the material.  If these meshes do not move like trees, better to merge them all together after cloning than use instances.  You save on the draw call & cpu time.  FYI, meshes can only be merge when the material is the same.

2.  You must make sure you hold no references in order for the Javascript class to be garbage collected.  Any heap reference vars should be set to null.  There is also a way to delete Javascript objects, but heard that it is slower than GC.

3. If you are using a MeshFactory, there is a function clean() in it.  In each Mesh class, dispose() is overridden & clean() is called when you have a mesh factory.  This should remove it as a held reference, and thus not be an impediment to GC.  The reason they have to be kept around in MeshFactory is so a donor mesh, that has not disposed of its GPU geometry, can be located.

Share this post


Link to post
Share on other sites

Hi, @JCPalmer

Thanks for your Expert - Explanation:)
After studying your instruction and related resource,
I’m still curious about the following concept.

 

1. My scene is designed to display only 5- 10 meshes simultaneously! So maybe draw calls are not a big burden in this condition ?


2. Even when I try to instance only one mesh, I face the following condition. The instance function drives a lot of looping-reference relationship? (sorry I dont know the exact term. Just find a lot of similar reference with a long distance). It’s is hard for me to find it and de-reference it! Do you know where can I make it? Or do you experience the similar situation before ?

(High poly mesh model link is here  : https://willlinifm.github.io/Playground_bjs/obj/Armour/Armour.js )

loop_ref_question.thumb.png.2e3bfc19aea4038634cc882d8f6d5c73.png


3. If it still raises a high level of heap after I dispose the mesh. What/Where is a good flow to de-reference objects (If MeshFactory is used to instance mesh) ?

 

Thank you so much!

 

Share this post


Link to post
Share on other sites

I am not really sure.  I do not use this browser.  Do not know what a back_pointer exactly is. Looking at your very large file, I see you have child meshes as well.  That is probably something to do with it.  Each child mesh has a reference to the parent.  This is BJS, not TOB.

First, using existing export, call without using MeshFactory:

var mesh = new Armour.armour("armour", scene);
mesh.dispose();
mesh = null;

If that does not release memory, then either it is either the Armour.armour mesh sub-class or BJS.  I doubt it is the child meshes, since they are member of the parent class, and should go out of scope after mesh = null;

this.armour1 = cloning ? child_armour1(scene, this, source.armour1) : child_armour1(scene, this);
this.armour2 = cloning ? child_armour2(scene, this, source.armour2) : child_armour2(scene, this);
this.armour3 = cloning ? child_armour3(scene, this, source.armour3) : child_armour3(scene, this);
this.armour4 = cloning ? child_armour4(scene, this, source.armour4) : child_armour4(scene, this);

 

Share this post


Link to post
Share on other sites

Now that I think about it, it probably is the children holding a reference to the parent by the BABYLON.Node super class.  Combined with the parent holding a reference to children, neither gets to a reference count of 0.

In my work, I never did a lot of removal with meshes which were both big & had children to notice.  Try to manually edit the file to remove the references to children.  they are not really used by the parent, I just put them there so you could easily get a reference without going thru the framework, getChildren & then sort out the right one.

cloning ? child_armour1(scene, this, source.armour1) : child_armour1(scene, this);
cloning ? child_armour2(scene, this, source.armour2) : child_armour2(scene, this);
cloning ? child_armour3(scene, this, source.armour3) : child_armour3(scene, this);
cloning ? child_armour4(scene, this, source.armour4) : child_armour4(scene, this);

FYI, your example is way to big, so I made a sample to show code (top level Cube wt ChildPlane).  This is version 5.6.  I have not committed it.  It has one minor problem that I have not gotten around to dealing with.  It is now ES6.  Looks like now 2 problems.  Was going to finalize soon to be the final version pre-Blender 2.8 though.  Anyway, the top level mesh is subclassed from the base class you specify, and any children are just instances of the base class.  Have not thought of whether I will keep the child references or add  lines in dispose override.  There can be many levels of children, so I'll look at the python to decide.

class Cube extends BABYLON.Mesh {
    constructor(name, scene, materialsRootDir, source) {
        super(name, scene, null, source, true);

        if (!materialsRootDir) { materialsRootDir = "./"; }
        defineMaterials(scene, materialsRootDir); //embedded version check
        var cloning = source && source !== null;
        this.position.x  = 0;
        this.position.y  = 0;
        this.position.z  = 0;
        this.rotation.x  = 0;
        this.rotation.y  = 0;
        this.rotation.z  = 0;
        this.scaling.x   = 1;
        this.scaling.y   = 1;
        this.scaling.z   = 1;
        this.ChildPlane = cloning ? child_ChildPlane(scene, this, source.ChildPlane) : child_ChildPlane(scene, this);

        this.id = this.name;
        this.billboardMode  = 0;
        this.isVisible  = false; //always false; evaluated again at bottom
        this.setEnabled(true);
        this.checkCollisions = false;
        this.receiveShadows  = false;
        this.castShadows  = false;
        this.isPickable = true;
        this.initComplete = false;
        if (!cloning){
            this.setVerticesData(_B.VertexBuffer.PositionKind, new Float32Array([
                1,-1,-1,-1,-1,1,1,-1,1,-1,1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1,-1
            ]),
            false);

            var _i;//indices & affected indices for shapekeys
            _i = new Uint32Array([0,1,2,3,4,5,5,0,2,4,6,0,6,3,1,2,3,5,0,6,1,3,7,4,5,4,0,4,7,6,6,7,3,2,1,3]);
            this.setIndices(_i);

            this.setVerticesData(_B.VertexBuffer.NormalKind, new Float32Array([
                .577,-.577,-.577,-.577,-.577,.577,.577,-.577,.577,-.577,.577,.577,.577,.577,-.577,.577,.577,.577,-.577,-.577,-.577,-.577,.577,-.577
            ]),
            false);

            this.setMaterialByID("Layout.Material");
            this.subMeshes = [];
            new _B.SubMesh(0, 0, 8, 0, 36, this);
            if (scene._selectionOctree) {
                scene.createOrUpdateSelectionOctree();
            }
        }
        if (this.postConstruction) this.postConstruction();
        this.initComplete = true;
        if (matLoaded && !_sceneTransitionName){
            if (typeof this.grandEntrance === "function") this.grandEntrance();
            else makeVisible(this);

        } else waitingMeshes.push(this);
    }

    dispose(doNotRecurse) {
        super.dispose(doNotRecurse);
        if (this.skeleton) this.skeleton.dispose();
    }
}
Layout.Cube = Cube;

function child_ChildPlane(scene, parent, source){
    var ret = new BABYLON.Mesh(parent.name + ".ChildPlane", scene, parent, source);
    var cloning = source && source !== null;
    ret.position.x  = 1.0001;
    ret.position.y  = .2351;
    ret.position.z  = -.2684;
    ret.rotation.x  = 0;
    ret.rotation.y  = 0;
    ret.rotation.z  = 0;
    ret.scaling.x   = 1;
    ret.scaling.y   = 1;
    ret.scaling.z   = 1;

    ret.id = ret.name;
    ret.billboardMode  = 0;
    ret.isVisible  = false; //always false; evaluated again at bottom
    ret.setEnabled(true);
    ret.checkCollisions = false;
    ret.receiveShadows  = false;
    ret.castShadows  = false;
    ret.isPickable = true;
    ret.initComplete = false;
    if (!cloning){
        ret.setVerticesData(_B.VertexBuffer.PositionKind, new Float32Array([
            1,0,-1,-1,0,1,-1,0,-1,1,0,1
        ]),
        false);

        var _i;//indices & affected indices for shapekeys
        _i = new Uint32Array([0,1,2,0,3,1]);
        ret.setIndices(_i);

        ret.setVerticesData(_B.VertexBuffer.NormalKind, new Float32Array([
            0,1,0,0,1,0,0,1,0,0,1,0
        ]),
        false);

        ret.subMeshes = [];
        new _B.SubMesh(0, 0, 4, 0, 6, ret);
        if (scene._selectionOctree) {
            scene.createOrUpdateSelectionOctree();
        }
    }
    if (this.postConstruction) this.postConstruction();
    ret.initComplete = true;
    return ret;
}

 

Share this post


Link to post
Share on other sites

Hi @JCPalmer

 

Thanks for your great instruction! These are good points!

Sorry for the late reply, I spent some time following your implementation.

Gladly, the memory heap decreases!

 

Recently I still find that during rendering the high-poly mesh,

the cpu heats up and fan runs fast and loudly.

If the memory is controlled in a good way,

will it reduce the CPU heat problem ?

 

Thank you again, have a nice day !!

Share this post


Link to post
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...

  • Recently Browsing   0 members

    No registered users viewing this page.