# Procedural texture memory leak

**URL:** <https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627>\
**Category:** Bugs\
**Tags:** texture, memory\
**Created:** [September 22, 2024, 12:39pm UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627 "2024-09-22T12:39:58Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![CrashMaster](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/crashmaster/32/23662_2.png) [@CrashMaster](https://forum.babylonjs.com/u/CrashMaster)\
**Post date:** [September 22, 2024, 12:39pm UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/1 "2024-09-22T12:39:58Z")

</div>

Hey everyone!

I have been tracking a memory leak in Cosmos Journeyer for about a week now where disposing planets would not release the RAM used.

I finally tracked it down to a large procedural texture that just does not want to die somehow 😂 Basically, calling `dispose` on the procedural texture does not free the RAM. What I don’t understand yet is why is the data stored in RAM while the procedural texture is used in GPU memory 🤔

Here is a repro of the bug (be careful it might crash your computer if you let it run for too long):

> **[Babylon.js Playground](https://playground.babylonjs.com/#T78RQV%2313)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

If anyone knows how to fix this I would be greatful, but that kinda looks like a bug to me 🤔

---

<div class="post-metadata">

**Author:** ![Evgeni\_Popov](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/evgeni_popov/32/4432_2.png) [@Evgeni\_Popov](https://forum.babylonjs.com/u/Evgeni_Popov)\
**Post date:** [September 23, 2024, 8:20am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/3 "2024-09-23T08:20:51Z")

</div>

You should not use a texture if it has been disposed, it is undefined behavior. So, when you dispose of the texture, you should also set `mat.diffuseTexture = null`:

> **[Babylon.js Playground](https://playground.babylonjs.com/#T78RQV%2317)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

---

<div class="post-metadata">

**Author:** ![Joe\_Kerr](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/joe_kerr/32/39017_2.png) [@Joe\_Kerr](https://forum.babylonjs.com/u/Joe_Kerr)\
**Post date:** [September 23, 2024, 8:52am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/4 "2024-09-23T08:52:01Z")

</div>

So in consequence, if we want to **play it safe** , whenever we

- dispose a texture,
- disopse a material with the dispose-texture paramter
- dispose a particle system (afaik auto-disposes its texture)

we need to iterate all materials and check all texture channels for leftover references?

Would it suffice to use [onTextureRemovedObservable\*\*](https://doc.babylonjs.com/typedoc/classes/BABYLON.Scene#onTextureRemovedObservable) in order to always prevent a memory leak? (\*\*I mean if the observable notifies, iterate materials then)

---

<div class="post-metadata">

**Author:** ![Evgeni\_Popov](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/evgeni_popov/32/4432_2.png) [@Evgeni\_Popov](https://forum.babylonjs.com/u/Evgeni_Popov)\
**Post date:** [September 23, 2024, 9:22am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/5 "2024-09-23T09:22:16Z")

</div>

Yes, `onTextureRemovedObservable` seems a good way to remove references to a texture that has just been removed.

---

<div class="post-metadata">

**Author:** ![CrashMaster](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/crashmaster/32/23662_2.png) [@CrashMaster](https://forum.babylonjs.com/u/CrashMaster)\
**Post date:** [September 23, 2024, 9:56am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/6 "2024-09-23T09:56:16Z")

</div>

Alright that’s definitely an improvement.

However setting the texture to null is not something that can be done with a ShaderMaterial for example (`setTexture` only accepts a texture and not null)

> **[Babylon.js Playground](https://playground.babylonjs.com/#T78RQV%2324)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

Also I still don’t understand why the texture has any cpu ram footprint as it is stored on the GPU. Is there some cached texture data that can be flushed to optimize RAM usage?

---

<div class="post-metadata">

**Author:** ![Evgeni\_Popov](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/evgeni_popov/32/4432_2.png) [@Evgeni\_Popov](https://forum.babylonjs.com/u/Evgeni_Popov)\
**Post date:** [September 23, 2024, 10:54am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/7 "2024-09-23T10:54:29Z")

</div>

You can’t pass `null` to `setTexture` because passing a null texture to a shader is not allowed. If your shader doesn’t use the texture anymore, you must update it, because the `texture(procTex, vUV);` instruction is wrong/undefined behavior.

> [@CrashMaster](#):
>
> Also I still don’t understand why the texture has any cpu ram footprint as it is stored on the GPU.

That’s probably because context lost management is enabled for your engine (it is the default). In that case, a number of resources (like textures) are cached, to be able to recreate them after a context has been lost/recreated. See [Babylon.js docs](https://doc.babylonjs.com/typedoc/interfaces/BABYLON.WebGPUEngineOptions#doNotHandleContextLost)

---

<div class="post-metadata">

**Author:** ![CrashMaster](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/crashmaster/32/23662_2.png) [@CrashMaster](https://forum.babylonjs.com/u/CrashMaster)\
**Post date:** [September 23, 2024, 11:21am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/8 "2024-09-23T11:21:19Z")

</div>

> [@Evgeni\_Popov](#):
>
> context lost management is enabled for your engine (it is the default).

Alright disabling the feature indeed saves a lot of RAM nice 👌

I also confused myself about the ShaderMaterial, sorry about that ^^

What I really meant is that in [this PG](https://playground.babylonjs.com/#T78RQV#30), the `ShaderMaterial` is disposed and so is the `ProceduralTexture` (every second). However, waiting for about 30s on my machine, the cpu ram usage starts to rise (even with `doNotHandleContextLost` set to true) and then It crashes:

```auto
WebGL: CONTEXT_LOST_WEBGL: loseContext: context lost
thinEngine.ts:2817 Uncaught (in promise) Error: Unable to create texture
    at t._createTexture (thinEngine.ts:2817:19)
    at t._createHardwareTexture (thinEngine.ts:2825:46)
    at new t (internalTexture.ts:309:44)
    at t._createInternalTexture (thinEngine.ts:2877:25)
    at t.createRenderTargetTexture (engine.renderTarget.ts:74:73)
    at t._createRtWrapper (proceduralTexture.ts:228:48)
    at new t (proceduralTexture.ts:202:32)
    at t.createProceduralTexture (nodeMaterial.ts:1294:35)
    at <anonymous>:63:52
t._createTexture @ thinEngine.ts:2817
t._createHardwareTexture @ thinEngine.ts:2825
t @ internalTexture.ts:309
t._createInternalTexture @ thinEngine.ts:2877
(anonymous) @ engine.renderTarget.ts:74
(anonymous) @ proceduralTexture.ts:228
t @ proceduralTexture.ts:202
(anonymous) @ nodeMaterial.ts:1294
(anonymous) @ VM41:63
Promise.then
createTex @ VM41:62
(anonymous) @ VM41:80
setTimeout
(anonymous) @ VM41:78
(anonymous) @ proceduralTexture.ts:338
(anonymous) @ effect.ts:579
e.notifyObservers @ observable.ts:393
e._onRenderingStateCompiled @ effect.ts:743
onRenderingStateCompiled @ effect.ts:790
(anonymous) @ effect.functions.ts:308
(anonymous) @ thinEngine.functions.ts:351
f @ thinEngine.functions.ts:249
t._finalizePipelineContext @ thinEngine.ts:2067
t._isRenderingStateCompiled @ thinEngine.ts:2127
get @ webGLPipelineContext.ts:37
e._isReadyInternal @ effect.ts:447
e._checkIsReady @ effect.ts:591
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604
setTimeout
e._checkIsReady @ effect.ts:603
(anonymous) @ effect.ts:604

```

When running `nvidia-smi`, I can see the VRAM usage increase until overflow while I would expect it to be stable over time if the texture was released from memory every second 🤔

---

<div class="post-metadata">

**Author:** ![Evgeni\_Popov](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/evgeni_popov/32/4432_2.png) [@Evgeni\_Popov](https://forum.babylonjs.com/u/Evgeni_Popov)\
**Post date:** [September 23, 2024, 1:29pm UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/9 "2024-09-23T13:29:12Z")

</div>

It looks like it could be a problem with the browser… I added some debug code and could check that each time we dispose a texture, we do execute `context.deleteTexture` for that texture, which should free the GPU memory, or at least mark the memory as being “reclamable”: it’s not required that the browser immediately releases the memory.

Also, I saw with nvidia-smi that once I reach my max GPU memory, refreshes still work for 5 or 6s, meaning the browser is able to reuse GPU memory under-the-hood: the used memory stays at max during all that time, proving (I think) that GPU memory is not really freed by the browser, but that it is able to recycle the released textures. But at some point it is not able to do it anymore for some reasons…

Note that it works in WebGPU, memory does not raise with each refresh.

It looks quite like [Memory Leak in Screenshots - #19 by Evgeni\_Popov](https://forum.babylonjs.com/t/memory-leak-in-screenshots/37178/19), but I don’t know what the final outcome of this case was.

---

<div class="post-metadata">

**Author:** ![CrashMaster](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/crashmaster/32/23662_2.png) [@CrashMaster](https://forum.babylonjs.com/u/CrashMaster)\
**Post date:** [September 24, 2024, 9:44am UTC](https://forum.babylonjs.com/t/procedural-texture-memory-leak/53627/10 "2024-09-24T09:44:34Z")

</div>

Okay, so I guess the solution for WebGL is to avoid doing large allocations and release in quick succession to let the browser take its time to give it back to the GPU.

I wanted to use WebGPU eventually so I won’t have to worry about it!

Thanks again for your time and knowledge 😉
