butterchurn icon indicating copy to clipboard operation
butterchurn copied to clipboard

Is there a way to clean up a created visualizer? Or to update its canvas node?

Open nukeop opened this issue 4 years ago • 6 comments

I have the following problem: I want to create a visualizer, destroy its canvas node, then later create a new canvas node, and move the visualizer to it. Alternatively, I could create a new visualizer with this new node, but that leaks memory.

This situation occurs when a user of my music player visits the visualizer view, switches to something else, then comes back. I'm thinking of a workaround involving keeping the canvas node alive but hidden, but it's going to be hacky and ugly and I would prefer a way to do either of the above.

nukeop avatar Feb 10 '21 23:02 nukeop

Hi @nukeop ,

I think I would go the create a new visualizer with a new node approach.

I looked into it a bit, and I think one way to do it would be to expose a method that calls loseContext on the internally created webGL2 context on the canvas: https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context/loseContext

With that + losing the reference to the old visualizer and canvas, I think everything should be properly garbage collected.

If that seems reasonable, I can get that in for the next release. Also, the next release will include some internal usage of WASM which will add some nice speed increases :D (should hopefully be out next week)

Excited to see butterchurn being integrated into nuclear, let me know if there is anything I can do to help.

jberg avatar Feb 11 '21 03:02 jberg

Yep that sounds perfect. I tried to limit the impact of leaks for now, which should be usable until the next release.

nukeop avatar Feb 11 '21 08:02 nukeop

I've been creating / removing canvas nodes to create new Butterchurn instances dozens of times within my app and I have never noticed any memory leaks, but I'm using Chrome on desktop so memory isn't such an issue. I never considered that Butterchurn would require any explicit destruction other than disconnecting the audio node and removing the canvas from the DOM.

@jberg nice find. A cleanup method that calls loseContext seems like a good idea.

void loseContext()

Implementations should destroy the underlying graphics context and all graphics resources when this method is called. This is the recommended mechanism for applications to programmatically halt their use of the WebGL API.

evoyy avatar Feb 11 '21 17:02 evoyy

I need to reinitialize butterchurn on canvas resize because of an implementation detail causing the visualizer not to be rendered otherwise when the visualizer screen is entered for the first time. I'm debouncing canvas resize to prevent it from being reinitialized too many times, and the memory usage goes up and down depending on what's happening, but still, resizing too many times causes some leakage.

nukeop avatar Feb 11 '21 17:02 nukeop

I need to reinitialize butterchurn on canvas resize because of an implementation detail causing the visualizer not to be rendered otherwise when the visualizer screen is entered for the first time. I'm debouncing canvas resize to prevent it from being reinitialized too many times, and the memory usage goes up and down depending on what's happening, but still, resizing too many times causes some leakage.

I'm assume you mean its an implementation detail in nuclear, but if you can get the canvas to hang around, you can always resize the butterchurn renderer. One small downside of recreating the visualizer is it will need to start rendering again (and you'll probably want to ensure it reloads the same preset). I figure resizing isn't done too often though, so probably not a huge concern.

jberg avatar Feb 12 '21 01:02 jberg

Hey @nukeop , sorry for the delay.

I haven't been able to finish everything up to release the new version, but the lose context work has found its way into the most recent beta: 3.0.0-beta.3.

That beta is currently running live on webamp.org and a couple other sites without issues.

In order to drop the GL context, you will want to run:

visualizer.loseGLContext()

visualizer being the object returned by createVisualizer

jberg avatar Feb 26 '21 02:02 jberg