freeing memory in three.js - javascript

my application loads a lot of meshes.
to get rid of old meshes i try to dispose them. but the memory is never being freed.
am i missing something ?
my simple example for reproducing:
load 100 of big binary meshes
dispose all of them again
chrome task manager says 250mb memory used, its exactly the same as without step 2
memtest
var scene = new THREE.Scene();
var mymesh=Array();
// 1. load a lot of geometry/meshes...
for(var i=0;i<100;i++)
{
var bloader;
bloader = new THREE.BinaryLoader();
bloader.load( "objekte/presto_6.js" , function( geometry )
{
mymesh.push(new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( {color:0xffffff } ) ));
scene.add(mymesh.length-1);
});
}
// 2. try to dispose objects and free memory...
for(var j=0;j<mymesh.length;j++)
{
mymesh[j].geometry.dispose();
mymesh[j].material.dispose();
screne.remove(mymesh[j]);
}
mymesh=Array();
</script>

Probably a typo, but if it isn't: screne.remove(mymesh[j]); should be scene.remove(mymesh[j]);
Other than that: rember (or find out) how JS manages the memory. Its garbage collector is a sweep-and-clean GC. It flags objects that aren't being referenced anywhere and then cleans them next time the GC kicks in:
for(var j=0;j<mymesh.length;j++)
{
mymesh[j].geometry.dispose();
mymesh[j].material.dispose();
scene.remove(mymesh[j]);
}
The mymesh array still contains references to the mesh objects you are attemting to free. The GC sees this referenec, and therefore refrains from flagging these objects. Reassign, delete, either the entire array of those specific keys you no longer need:
for(var j=0;j<mymesh.length;j++)
{
mymesh[j].geometry.dispose();
mymesh[j].material.dispose();//don't know if you need these, even
scene.remove(mymesh[j]);
mymesh[j] = undefined;//or
delete(mymesh[j]);
}
//or simply:
mymesh = undefined;//or some other value
That allows the memory to be freed, unless another variable remains in scope that references some or all of these objects, too.
As an asside:
mymesh=Array();
Is bad code on many levels. JS functions that begin with an UpperCase are constructors, and should be called usign the new keyword, though most constructors (especially the native objects) shoul be called as little as possibe.
Their behaviour can be unpredictable, and there's often a shorter way to write the code:
mymesh = [];//an array literal
//example of werird behaviour:
mymesh = new Array(10);//[undefined, undefined, undefined....]
mymesh = [10];
mymesh = new Array('10');//['10']
mymesh = new Array(1.2);//RangeError
var o = new Object(123);//returns new Nuber
o = new Object('something');//new String
o = new Object(false);//new Boolean
o = new Object('foo', 'bar');//new String('foo')!!!
o = {foo: 'bar'};//<-- this is soooo much easier

Related

Three.js: how to correctly dispose a scene in memory

I am having many problems to get rid of memory leaks using three.js in a reactive application. Investigating the problem, I found that I am not even able to correctly dispose a scene (without rendering).
Let me show you:
https://plnkr.co/edit/Z0sXOnXYc2XOV4t9tETv
In the above example, initially 3 THREE objects are instantiated (as you can see there is no rendering going on, just instantiating the objects):
scene
camera
renderer
Using the chrome devtools let’s take a memory snapshot just after loading the page:
Now let’s click on the “ADD 1000 MESHES” button, which as you can guess simply create 1000 meshes (BoxGeometry + MeshBasicMaterial) and add them to the scene object.
Let’s take another memory snapshot and see the comparison (delta) with the previous snapshot:
As you can see we passed from 25.2 Mb to 36.2 Mb and there are +1000 Mesh objects added in memory.
Now clicking the “DISPOSE” button we’ll trigger the following dispose function:
const dispose = (e) => {
// dispose geometries and materials in scene
sceneTraverse(scene, o => {
if (o.geometry) {
o.geometry.dispose()
console.log("dispose geometry ", o.geometry)
}
if (o.material) {
if (o.material.length) {
for (let i = 0; i < o.material.length; ++i) {
o.material[i].dispose()
console.log("dispose material ", o.material[i])
}
}
else {
o.material.dispose()
console.log("dispose material ", o.material)
}
}
})
scene = null
camera = null
renderer && renderer.renderLists.dispose()
renderer = null
addBtn.removeEventListener("click", addMeshes)
disposeBtn.removeEventListener("click", dispose)
console.log("Dispose!")
}
In this function we traverse the scene and dispose every geometry and material. Then we set to null the references to scene, camera and renderer and finally we remove the listeners to avoid memory leaks.
Let’s click on DISPOSE button and take another memory snapshot. I expected that the garbage collector will completely remove from memory all the data related to the 1000 Meshes (Mesh, Matrix4, Vector3, BoxGeometry, etc…) but if we take another memory snapshot we’ll find something very different:
It seems that 1000 Mesh objects have been deleted, but the memory usage is almost the same as in the previous snapshot (34.6 vs 36.2 Mb). There are some drops in the Vector3, Matrix4, Quaternion and Euler objects, but most of the objects keep to be persisted in memory and are not collected from the garbage collector. Indeed, if we compare the snapshot 3 to the snapshot 1 we find exactly that:
Please could someone explain what’s going on and how correctly dispose things in three.js?
Three.js: 102
Google Chrome: 72.0.3626.121 (64-bit)
Actually the problem were the console.log statements which prevent to garbage-collect the objects printed on the chrome console. Removing the console.log statements solved the problem.

three.js Memory Leak when using `elementsNeedUpdate`

I am creating a Geometry in three.js and populating it with vertices to build a 2D terrain. I am pushing all of the Vector3s and Face3s to the geometry as soon as my terrain is created, and then modifying each vertex and face every frame.
Because I am modifying the face vertices every frame, I need to tell three.js to update the faces. I am doing this using geometry.elementsNeedUpdate = true. This works, however I have noticed it causes a substantially large amount of memory usage (my app uses an extra ~50mb of RAM every second).
The following code demonstrates what I'm trying to do:
function pushEverything(geom) {
for (var i = 0; i < 10000; i++) {
geom.vertices.push(new THREE.Vector3(...));
geom.faces.push(new THREE.Face3(...));
geom.faces.push(new THREE.Face3(...));
}
}
function rebuild(geom) {
for (var face of geom.faces) {
face.a = ...
face.b = ...
face.c = ...
}
geom.elementsNeedUpdate = true
}
var renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("my-canvas")
});
var geom = new THREE.Geometry();
var camera = new THREE.PerspectiveCamera(...);
pushEverything(geom);
while (true) {
// Perform some terrain modifications
rebuild(geom);
renderer.render(geom, camera);
sleep(1000 / 30);
}
I have already followed the advice of this question, which suggested using geometry.vertices[x].copy(...) instead of geometry.vertices[x] = new Vector3(...).
My question is: why is my memory usage so high when using geometry.elementsNeedUpdate = true? Is there an alternative method to updating a Geometry's faces?
I am using three.js 0.87.1 from NPM.
I have found and solved the issue. It was not a memory leak on three.js' part, but it was a memory leak on my part.
I was creating a Geometry and allowing myself to clone it, perform modifications to the clone, and then merge it back into the original. What I didn't realise is that I should call geometry.dispose() on the cloned geometry when I was done with it. So, I was basically cloning the geometry every frame, which explains the huge memory usage.
I have fixed my issue by converting the Geometry to a BufferGeometry, and calling geometry.dispose() on the geometry when I am done with it. I now have expected memory usage.

Javascript multi-nested Objects losing deeper-level Object's variables

I have what is basically an object that nests an object that nests a third.
What I have found is the last object's constructor information suddenly disappears.
Ex: A PlayerPos object that contains two Triangle objects, which each in turn contain three Point objects.
Here is what I have done to debug:
// Points to be used
var one = new Point(1, 2);
var two = new Point(2, 3);
var three = new Point(3, 4);
// Creation of the triangle
var tri = new Triangle(one, two, three);
// Check the console to see what it outputs.
console.log(tri);
At this point the console tells me this:
Triangle {front: Point, oneBack: Point, twoBack: Point}
Which, is exactly what I want. Everything is going well so far.
I then decided to make the PlayerPos.
// Create player position
var player = new PlayerPos(tri, tri);
// Check the log to make sure it can still find the triangles.
// (next) is one of PlayerPos's constructor names.
console.log(player.next);
This outputs:
Triangle {front: undefined, oneBack: undefined, twoBack: undefined}
Wait. What?
Why does the Triangle in PlayerPos suddenly decides to dump what it knows? And how to fix this.

The process of loading OBJ files into Three.js

I am trying two upload one OBJ and its associated MTL files into a WebGL scene using the lovely Three.js.
I have just came up with an issue which I can't find a justification for that. I have put some global variables in my code to monitor what is going on when loading the OBJ and MTL files. Although it is just one set of files, the "obj" variable is from "Group" type and it does have two "children". It is not itself instance of mesh but its children are, as console.log() says. However, when I am checking inside children, the first one -- which is measured by geo[0] -- contains just vertices and the "faces" array is empty. However, the second child -- measured by geo[1] -- has all the vertices and faces. It is even enough to just use geo[1] to get the same scene when you use obj.
My concern is the logic behind this. Why for a set of OBJ and MTL files we have an object with two children in which the first children has just vertices and the second one has vertices again and also faces.
It is important for me because I want to work on the vertices and faces to get a voxelized volume and this needs direct manipulations on the scene objects.
scapula = new THREE.OBJMTLLoader();
scapula.load('obj/scapulaTWO.obj', 'obj/scapulaTWO.mtl', function (object) {
// var material = new THREE.MeshFaceMaterial(materials);
var material = new THREE.MeshLambertMaterial({
color: 0xFFFF66
});
obj = object; //group
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
console.log(child instanceof THREE.Mesh)
geo[i] = child; //mesh
// apply custom material
child.material = material;
// enable casting shadows
child.castShadow = true;
child.receiveShadow = true;
i = i + 1;
}
});
scene.add(geo[1]);
});
What you will find in your object after it has been parsed by the loader is produced by your model.
You can open your *.obj with a text editor and see the groups etc. for yourself. Its not like three.js rips out the vertices and puts them in a seperate group.

Javascript Memory Management Using Global Arrays of THREE.JS Objects

I have a a question that's been bothering me for some time.
I am using the three.js webgl library to render a large scene with many textures and meshes.
This question is not necessarily bound to webgl, but more javascript arrays and memory management.
I am basically doing this:
var modelArray = [];
var model = function(geometry,db_data){
var tex = THREE.ImageUtils.loadTexture('texture.jpg');
var mat = new THREE.MeshPhongMaterial({map:tex})
this.mesh = new THREE.Mesh(geometry,mat);
this.db = db_data;
scene.add(this.mesh);
};
function loadModels(model_array){
for(i=0;i<geometry.length;i++){
modelArray.push(new model(model_array[i]['geometry'],model_array[i]['db_info']));
}
}
loadModels();
Am I being inefficient here? Am I essentially doubling up the amount of memory being used since I have the mesh loaded to the scene and an array. Or does the model (specifically the model.mesh) object in the array simply point to a singular memory block?
Should I just create an array of mesh ids and reference the scene objects, or is it ok to add the mesh to the scene and an array?
Thanks in advance and I hope I was clear enough.
The main thing that jumps out at me is this:
var tex = THREE.ImageUtils.loadTexture('texture.jpg');
var mat = new THREE.MeshPhongMaterial({map:tex})
If you are loading the same texture every time you create a new model, that could create a lot of overhead (and it can also be pretty slow). I would load the texture(s) and corresponding material(s) you need outside of your loop once.
Your modelArray is a list of plain model objects, each of which has a pointer to the corresponding mesh object (and db object). The scene has a pointer to the same mesh object so you are not exploding your memory use by cloning meshes.
It's possible that your memory use is just because your mesh geometries take up a lot of memory. Try loading your models one by one while watching memory usage; perhaps you have one that is unexpectedly detailed.

Categories