WebGPURenderer: Textures never removes dispose listeners - #34368
Conversation
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
1dd1f54 to
d25de02
Compare
|
The ordering in three.js/src/renderers/common/Renderer.js Lines 2703 to 2721 in 7013726 You would have to move for ( const canvasTarget of this._frameBufferTargets.keys() ) {
canvasTarget.dispose();
}
this._textures.dispose();
this.info.dispose(); |
|
|
||
| renderTarget.addEventListener( 'dispose', renderTargetData.onDispose ); | ||
|
|
||
| this._disposeListeners.set( renderTarget, renderTargetData.onDispose ); |
There was a problem hiding this comment.
To be clear: The tracking you are adding here is no ideal solution. Also add the same comment like in Geoemtries.js:
// see #34368 why tracking separate remove listeners is required right now
// TODO: Re-evaluate how onDispose() is managed in this componentThe problem is if an app never uses texture.dispose(), the new strong references stored in the Map prevent any GC that was previously possible with the WeakMap only approach. So a user error means the texture with its expensive image is stored for the renderer's lifetime now.
| * @private | ||
| * @type {Map<(Texture|RenderTarget),Function>} | ||
| */ | ||
| this._disposeListeners = new Map(); |
There was a problem hiding this comment.
Just curious, but would this work as well?
this._tracked = new Set(); // Set<WeakRef<Texture|RenderTarget>>
...
this._tracked.add( new WeakRef( texture ) );
...
dispose() {
for ( const ref of this._tracked ) {
const object = ref.deref();
if ( object !== undefined && this.has( object ) ) object.removeEventListener( 'dispose', this.get( object ).onDispose );
}
this._tracked.clear();
this._htmlTextures.clear();
super.dispose();
}- Track textures and render targets with a Set of WeakRefs instead of a strong Map so undisposed objects can still be garbage collected. - Textures.dispose() now runs _destroyRenderTarget()/_destroyTexture() for each tracked object and clears _htmlTextures. - Dispose info last in Renderer.dispose() so the destroy calls above do not decrement counters that were already reset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
This would be the first time we use |
Clean up.
If a texture or render target is garbage collected without an explicit dispose(), the registry removes its stale WeakRef from the tracking set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Haha by uncovering this issue, it looks like I may have pushed the use of FinalizationRegistry (which is fair?) but FinalizationRegistry is not an API supported in React Native. |
|
FYI: I've adapted this approach in |
This is a follow up from #34327 which was nicely found and reported in RN WebGPU: wcandillon/react-native-webgpu#445
this is the reproduction (use
?lib=cdnto try against the current release).repro-renderer-leak.html