I'm trying to destroy (or destruct or dispose) an 'instance' of Three.JS using this:
Full example: https://jsfiddle.net/v7oLzs4m/58/
function kill() {
const rendererDomWas = renderer.domElement;
renderer.dispose();
controls.dispose();
renderer = scene = camera = controls = null;
document.body.removeChild( rendererDomWas );
shouldRender = false;
}
function animate() {
if(!shouldRender) {return} // no more requesting animation frames if 'shouldRender' is false
frameID = window.requestAnimationFrame( animate );
renderer.render( scene, camera );
}
(i.e. disposing, setting references to null, and stopping the draw loop from touching renderer while shouldRender is false)
It appears to work at first (The renderer content stops showing) but when I re-create the instance, it never comes back.
It's as if something is still... holding onto the GLContext which prevents it from being invoked again.
Why can't I re-create a new instance of Three.JS?
I'm not sure why this works (since I was already doing this in my Fiddle...)
But it turns out the secret ingredient (for me) to letting Three.JS get GC'd/disposed is this:
window.cancelAnimationFrame(frameID);
I suppose that this stops the render function from being stored in the hardware-level draw loop, which holds references to GLContext (and the chain)
The GC can't ever happen unless the 'loop is broken'
(Correct me if I'm wrong please)
Related
I have created a game in three.js which exists of 3 scenes the GameScene, MenuScene and the HighScoreScene. The user can switch between those scenes - When the player is changing the Scene e.g. from the MenuScene to the HighScoreScene I'm trying to clean up the resources from the old Scene.
But cleaning up the resources seems not to work.
So here is my code which is getting executed when the user is switching between scenes:
So in the old Scene I have this animation loop:
function cancelAnimation() {
cancelAnimationFrame(animationFrameId); // EXECUTING WHEN SWITCHING SCENE!!!
}
function animate() {
animationFrameId = requestAnimationFrame(animate); // Saving id
render();
}
function render() {
renderer.clear();
renderer.render(scene, camera);
}
So when switching the scene I unset all click listeners and I'm calling cancelAnimation.
Because I'm storing that code for each Scene in an object like var menuScene = new MenuScene() I'm also doing this menuScene = undefined when switching the scene.
I'm also passing the same instance of var renderer = new THREE.WebGLRenderer(); to the scenes.
This all seems not to help the game is executing more slowly.
What is the proper way to switch between those scenes and clearing up the resources? What am I doing wrong here?
Basically, I want the setup where I could go to preserveDrawingBuffer=true, render the scene once, grab the screenshot, and go back. However, this poses two problems:
there is no method in renderer to dispose all the buffers,
canvas goes black if I do
renderer = new THREE.WebGLRenderer({canvas:renderer.domElement,preserveDrawingBuffer:true});
How do I do this properly?
EDIT: I did not find the way to toggle this, so I had to clone scene and create second renderer instead to make the screenshot. See https://github.com/mrdoob/three.js/issues/189
You don't need preserveDrawingBuffer: true to take a screenshot. What you need is to take the screenshot immediately after rendering. A screenshot is guaranteed to work as long as you take it after rendering but before exiting the current event.
So for example this will always work
renderer.render( scene, camera );
var screenshot = renderer.domElement.toDataURL();
Whereas this will only work randomly if you're luck
someElement.addEventListener('click', function() {
// this is not immediately after rendering. you have no
// idea when it is relative to rendering.
var screenshot = renderer.domElement.toDataURL();
});
Most of the THREE.js examples have a render function if you need to take a screenshot when the user requests one you could do this
someElement.addEventListener('click', function() {
render();
var screenshot = renderer.domElement.toDataURL();
});
Or you could do this
var takeScreenshot;
function render() {
...
if (takeScreenshot) {
takeScreenshot = false;
var screenshot = renderer.domElement.toDataURL();
}
}
someElement.addEventListener('click', function() {
takeScreenshot = true;
});
Or any number of other ways to just make sure you take the screenshot immediately after rendering.
I have also encountered this problem.my screenshot is blank in threejs webgl canvas, because not set renderer = new THREE.WebGLRenderer({canvas:renderer.domElement,preserveDrawingBuffer:true});,
Here is my way to toggle preserveDrawingBuffer.
let canvas = this.renderer.domElement;
canvas.getContext('webgl' , {preserveDrawingBuffer: true});
this.render();
var data = {
image: canvas.toDataURL(),
};
canvas.getContext('webgl' , {preserveDrawingBuffer: false});
In this project I am working on I have several Collada models being displayed and then removed when not needed anymore. There seems to be a memory leak somewhere in the project and I am looking for ways to get it to run as smooth as possible as time is not on my sideā¦
I feel like I don't remove the meshes the right way and that this might cause some of the memory leakage I have.
I load the objects with LoadObject(level_1_character, "Assets/Level_1_Character.dae"); for example, where level_1_character is a Object3D. This calls the following function:
function LoadObject(name, url) {
var mesh, geometry, material, animation;
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(url, function(col) {
mesh = col;
geometry = col.scene;
name.name = url.toString();
name.add(geometry);
});
}
I add the objects to the scene depending on the level through scene.add(level_1_character); and then remove it by doing the following:
level_1_character.traverse(function(child){
if (child instanceof THREE.Mesh) {
child.material.dispose();
child.geometry.dispose();
}
});
I am not sure if this actually fully removes the object though. It seems as though the objects still are present in memory. Any idea what I'm doing wrong here?
I am creating a static scene with a lot of objects and I want to save as image after it is rendered. How do I do that in THREE.js r69?
Most probably, you are not going in the right direction.
if you have some function like this
function animate() {
requestAnimationFrame(animate);
render ();
}
The render is doing it's work all the time.
When you are seeing the scene unfinished, it's because the meshes haven't finished loading, not because the render hasn't finished rendering.
So, you have to listen for some onload event on the models that you are loading, that will depend on the method that you use to load them (and that you don't explain, so I can't help you more)
Given the code in your reply, it isn't clear when you add the mesh to the scene. Anyway, I would try something in the line of
function loaderCallback() {
mesh = new THREE.Mesh (...
scene.add (mesh);
requestAnimationFrame(saveImage);
render ();
}
the function invoked at requestAnimationFrame should be called when render finishes.
I have a large scene with a lot of Mesh and MorphAnimMesh. I want to free memory when the meshes are removed. If i know right this is the best way to do:
for ( var i = scene.children.length - 1; i >= 0 ; i -- ) {
var obj = scene.children[i];
scene.remove(obj);
obj.deallocate();
obj.geometry.deallocate();
obj.material.deallocate();
obj.material.map.deallocate();
}
if i check the memory usage at task manager after this, nothing changes. ( tried to wait a few min for GC but nothing. ) Google Chrome memory snapshot shows the objects still there. morphTargets in THREE.Geometry #1862203 etc.
Tried to set the obj to null, but still no memory decrease.
Any idea what am i doing wrong?
Its a game with levels and the player can change from one to another. After a few change memory usage increases to really high. Thats why i want to remove all object from memory before the level change.
Most likely, you need to add some, or all, of the following:
geometry.dispose();
material.dispose();
texture.dispose();
Check out these examples:
http://mrdoob.github.com/three.js/examples/webgl_test_memory.html
http://mrdoob.github.com/three.js/examples/webgl_test_memory2.html
three.js r.60
I did try all the dispose and deallocate methods but nothing worked.
Then I did the following for my ionic application which is using webgl renderer to render a 360 image.
this.renderer = new THREE.WebGLRenderer({ antialias: true });
RicohView.prototype.stopRendering = function () {
this.canRender = false;
this.renderer.forceContextLoss();
this.renderer.dispose();
console.log('renderer disposed');
cancelAnimationFrame(this.requestId);
}
requestId is something which can be captured from
this.requestId = requestAnimationFrame(render);