Dispose canvas under DynamicTexture

I was wondering if it was possible to dispose the canvas under a DynamicTexture if I know it’s not updated anymore to free up a lot of memory, maybe add a freeze() method or something that handles that.

Then I looked into the dynamicTexture.ts and didn’t see the _canvas being disposed anywhere period, and couldn’t find it being done elsewhere either. I’m pretty sure it’s not sticking around when the texture is disposed, so how does that work?

If a canvas is created by babylon it is created either using OffscreenCanvas (if executed in a worker) or using createElement(‘canvas’), executed on the document object. The canvas is not added to the DOM at any place in the code.
In both of those cases, the browser will garbage-colelct those variables once no object references them. So the minute the texture object is garbage collected (not disposed by babylon, but gc’ed by javascript), the canvas element will be garbage-collected as well.

I see, that explains it. Then back to my original question: can we safely create a freeze() method that dumps the canvas (maybe by making it undefined, thus GC’d)? or is the canvas still needed for something besides updating the texture later?

the canvas’ context is used throughout the class - to clear, to draw, to update the texture.

Why do you want to dispose the canvas?

I use DynamicTexture to create textures, but I don’t ever update them after that. As far as I know canvas objects keep the bitmaps in memory, so for me that is 100’s of MB’s that I don’t think are needed anymore, because they are already present on the GPU.

So I’m checking if I’m correct. And if so, to ask if Babylon is interested in having this freeze method as well.

If that’s the case you can serialize the dynamic texture and create a new texture based on the URL provided. You can then eliminate the dynamic texture. something like this:

Dynamic Texture Example | Babylon.js Playground (babylonjs.com)

(lines 50-54)

ah, yes, that is a way to do it.
I’m doing this in performance crucial moments as well though, so I will still look for a way to forego creating a second Texture.

In any case, it seems like there are 2 separate use cases for DynamicTexture. One where you want to draw a texture with the canvas, and one where you want a texture that you can update after creating it. If I’m correct about the memory concerns (still not 100% sure) it would be nice to support the first use case as well.

freezing a dynamic texture feels like contradicting the entire meaning behind a dynamic texture. And if the canvas is disposed there is no way of “unfreezing” it. Maybe someone else has a different opinion about it but i believe creating a new texture would be the best way to go.
I understand that it is happening at a critical point during the app’s lifecycle, but i assume there are ways to control it. Are you creating all of those dynamic textures at the same time?

freezing a dynamic texture feels like contradicting the entire meaning behind a dynamic texture.

that’s why I’m saying it’s 2 different use cases, it could be called CustomTexture or something.

Are you creating all of those dynamic textures at the same time?

no, I create them all the time for drawing all kinds of on the fly images, text, blur shadows… you can check the link in my profile.

I totally understand the need (and BTW - your site is amazing!!). I still think that it feels like a custom implementation that is rarely needed.

Why don’t you create the class yourself for your app? you can take the code of the dynamic texture and modify it to your needs

yeah, of course I will do that. I was just checking if there was interest in a pull request.

I implemented it, and I can confirm it works as expected. In Safari web inspector I can no longer find canvases for each dynamic texture in the Graphics tab and the memory footprint is a lot smaller.

the code is straightforward (without built in safeguards against calling update() etc):

freeze() {
    (this.dynamicTexture as any)._canvas = undefined;
    (this.dynamicTexture as any)._context = undefined;