Bug with audio engine dispose()

In short BABYLON.Engine.audioEngine.dispose(); doesn’t work

Full story:
I’m using angular for my project and i tried to find why one of my components is not garbage collected.

I noticed that when i call “new BABYLON.Engine(canvas);” for the first time in component, this component will never be garbage collected. When i call it multiple times all other components will be garbage collected. So when component with engine is active i have 2 components in memory: the active one and the first created one.
It looked like that Engine have pointer to canvas which is parameter of it’s constructor.

I checked sources of constructor and found:

// Create Audio Engine if needed.
if (!Engine.audioEngine && audioEngine && Engine.AudioEngineFactory) {
Engine.audioEngine = Engine.AudioEngineFactory(this.getRenderingCanvas());
}

So the first created engine creates audio engine which has pointer to canvas after that.
I use engine.dispose(); when angular component is not needed anymore and it works perfectly for all other components except first one, because first one(his canvas) is pointed by audio engine. I’m not sure it’s ok to use some dead canvas for audio engine, i think it should die with engine which uses the same canvas as audioEngine.

Moreover:
engine.dispose() method has
if (Engine.Instances.length === 1 && Engine.audioEngine) {
Engine.audioEngine.dispose();
}
this code is called every time for my app(because i have only one engine at a time) but it looks like " Engine.audioEngine.dispose();" doesn’t do anything. If i call this method myself audioEngine is not released too

I’ve created playground. You need to check console.
https://playground.babylonjs.com/#2S3B6N#2
BABYLON.Engine.Instances contains all created engines:
Engine.Instances.push(this); //line from Engine constructor

lines from dispose():
var index = Engine.Instances.indexOf(this);
if (index >= 0) {
Engine.Instances.splice(index, 1);
}

sources of Engine:

What you think?

The audio engine is not an engine (or better yet - is not a rendering engine), so it shouldn’t be stored at the engine instances array. Might be a naming issue in that case, but they are completely separated. That’s why the playground works this way.
Apart from that - dispose does not mean “garbage collected”. at least not in the babylon world :-). What it does mean is that the resources that were tied to it (like a GL buffer or an Audio Context) are now released, and this object can potentially be garbage collected. as long as some other object is referencing it, it will stay in the memory.

The reason the audioEngine is not garbage collected is that it is not being removed from the (static) Engine.audioEngine reference, which should be the case after dispose was called. The audio engine should be garbae collected when creating a new one. So there is only one missing reference, which is Engine.audioEngine.

TL;dr - I agree that we should se the audioEngine to null after disposing it :slight_smile:

Does that answer your question?

Thank you for answer.
imagine next situation:
1 canvas dynamically is created
2 new engine(canvas) is called. engine is added to static reference. audioengine is added to separate static reference. both are pointing to canvas
3 engine.dispose() is called. engine is not pointing to canvas enymore. engine removed from static reference. audioengine is still pointing canvas.
4 canvas deleted from dom. but it cant be garbage collected because audioengine is still pointing it
5 new canvas is created
6 new engine(canvas) is called. engine is added to static reference with pointer to new canvas. audioengine already exists with pointer to previous canvas so no new audio engine.
7 repeat 3-6
This is what happens in my app. It is a situation when first canvas will never be released.

i see 3 problems here
audioengine should release canvas when audioengine.dispose() is called.
audioengine reference should be null after audioengine.dispose(). You agreed with that.
audio engine should call dispose during engine.dispose if engine.canvas===audioengine.canvas

I hope it is clearer what i wanted to explain

The reason the audio engine was not disposed is because it was referenced as a static member of Engine. Once the audio engine can be disposed and garbage collected, the canvas (if not referenced any other place in your code) can be garbage collected as well. Together with some other elements that the audio engine generated. If the audio engine is garbage collected, the problem is solved, isn’t it?

Yes, if only i have one engine in app.
if i have 2 engines audio engine will not be disposed because of code
if (Engine.Instances.length === 1 && Engine.audioEngine) {
Engine.audioEngine.dispose();
}
and dead canvas will not be garbage collected

I see.
It will however provide you with a working reusable audio engine (since the dependency on the canvas is very minimal). You can create the audio engine yourself and set it, it will free the older audio engine and the canvas as well.

The best recommendation for you would be to create the audio engine yourself, using a different element other than the canvas. The canvas container would make a lot of sense. This way your canvases will not be connected to the audio engine and wil be disposed. You will need to init it before creating the engine, and you will need to do it once.

will it be fixed in next version?

I am not sure what part you are referring to. I have pushed the fix to make the audio engine null when it is disposed so it will be garbage collected. This will be available in the next preview version. The rest is not a bug, but the wat the framework is working