diff --git a/examples/files.json b/examples/files.json index 065ccac7461dea..99d4bc835b326d 100644 --- a/examples/files.json +++ b/examples/files.json @@ -455,6 +455,7 @@ "webgpu_sprites", "webgpu_storage_buffer", "webgpu_struct_drawindirect", + "webgpu_test_memory", "webgpu_texturegrad", "webgpu_textures_2d-array", "webgpu_textures_2d-array_compressed", diff --git a/examples/screenshots/webgpu_test_memory.jpg b/examples/screenshots/webgpu_test_memory.jpg new file mode 100644 index 00000000000000..fbaa2cfd538661 Binary files /dev/null and b/examples/screenshots/webgpu_test_memory.jpg differ diff --git a/examples/webgpu_test_memory.html b/examples/webgpu_test_memory.html new file mode 100644 index 00000000000000..e604b93edae645 --- /dev/null +++ b/examples/webgpu_test_memory.html @@ -0,0 +1,301 @@ + + + + three.js webgpu - memory test I + + + + + + +
+ + +
+ three.jsWebGPU Memory Test I +
+ + + This example tests memory management with WebGPU renderer. +
Spheres are created, rendered, and disposed in each frame. +
+
+ + + + + + + diff --git a/src/lights/DirectionalLight.js b/src/lights/DirectionalLight.js index 6eb0da6b26c77c..ce9682fffec79b 100644 --- a/src/lights/DirectionalLight.js +++ b/src/lights/DirectionalLight.js @@ -80,6 +80,8 @@ class DirectionalLight extends Light { dispose() { + super.dispose(); + this.shadow.dispose(); } diff --git a/src/lights/Light.js b/src/lights/Light.js index 47b0b5004678f8..4d81b98fe10c5a 100644 --- a/src/lights/Light.js +++ b/src/lights/Light.js @@ -54,7 +54,7 @@ class Light extends Object3D { */ dispose() { - // Empty here in base class; some subclasses override. + this.dispatchEvent( { type: 'dispose' } ); } diff --git a/src/lights/PointLight.js b/src/lights/PointLight.js index 7e8255985cc769..d760abfa45f6ad 100644 --- a/src/lights/PointLight.js +++ b/src/lights/PointLight.js @@ -94,6 +94,8 @@ class PointLight extends Light { dispose() { + super.dispose(); + this.shadow.dispose(); } diff --git a/src/lights/SpotLight.js b/src/lights/SpotLight.js index eb93251abc651a..287437f654378a 100644 --- a/src/lights/SpotLight.js +++ b/src/lights/SpotLight.js @@ -147,6 +147,8 @@ class SpotLight extends Light { dispose() { + super.dispose(); + this.shadow.dispose(); } diff --git a/src/nodes/lighting/AnalyticLightNode.js b/src/nodes/lighting/AnalyticLightNode.js index 7fac36424e280a..46dc98d02560ef 100644 --- a/src/nodes/lighting/AnalyticLightNode.js +++ b/src/nodes/lighting/AnalyticLightNode.js @@ -96,6 +96,53 @@ class AnalyticLightNode extends LightingNode { */ this.updateType = NodeUpdateType.FRAME; + if ( light && light.shadow ) { + + this._shadowDisposeListener = () => { + + this.disposeShadow(); + + }; + + light.addEventListener( 'dispose', this._shadowDisposeListener ); + + } + + } + + dispose() { + + if ( this._shadowDisposeListener ) { + + this.light.removeEventListener( 'dispose', this._shadowDisposeListener ); + + } + + super.dispose(); + + } + + /** + * Frees internal resources related to shadows. + */ + disposeShadow() { + + if ( this.shadowNode !== null ) { + + this.shadowNode.dispose(); + this.shadowNode = null; + + } + + this.shadowColorNode = null; + + if ( this.baseColorNode !== null ) { + + this.colorNode = this.baseColorNode; + this.baseColorNode = null; + + } + } getHash() { diff --git a/src/nodes/lighting/ShadowFilterNode.js b/src/nodes/lighting/ShadowFilterNode.js index 37b477e0b47719..b41f42dc754dff 100644 --- a/src/nodes/lighting/ShadowFilterNode.js +++ b/src/nodes/lighting/ShadowFilterNode.js @@ -272,3 +272,21 @@ export const getShadowMaterial = ( light ) => { return material; }; + +/** + * Disposes the shadow material for the given light source. + * + * @param {Light} light - The light source. + */ +export const disposeShadowMaterial = ( light ) => { + + const material = shadowMaterialLib.get( light ); + + if ( material !== undefined ) { + + material.dispose(); + shadowMaterialLib.delete( light ); + + } + +}; diff --git a/src/nodes/lighting/ShadowNode.js b/src/nodes/lighting/ShadowNode.js index 03d4da5f4e7aaa..3ff442311ef53f 100644 --- a/src/nodes/lighting/ShadowNode.js +++ b/src/nodes/lighting/ShadowNode.js @@ -17,7 +17,7 @@ import { viewZToLogarithmicDepth } from '../display/ViewportDepthNode.js'; import { lightShadowMatrix } from '../accessors/Lights.js'; import { resetRendererAndSceneState, restoreRendererAndSceneState } from '../../renderers/common/RendererUtils.js'; import { getDataFromObject } from '../core/NodeUtils.js'; -import { getShadowMaterial, BasicShadowFilter, PCFShadowFilter, PCFSoftShadowFilter, VSMShadowFilter } from './ShadowFilterNode.js'; +import { getShadowMaterial, disposeShadowMaterial, BasicShadowFilter, PCFShadowFilter, PCFSoftShadowFilter, VSMShadowFilter } from './ShadowFilterNode.js'; import ChainMap from '../../renderers/common/ChainMap.js'; import { warn } from '../../utils.js'; import { textureSize } from '../accessors/TextureSizeNode.js'; @@ -748,6 +748,8 @@ class ShadowNode extends ShadowBaseNode { this._currentShadowType = null; + disposeShadowMaterial( this.light ); + if ( this.shadowMap ) { this.shadowMap.dispose(); diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index a909d55420b36b..356317afe559ca 100644 --- a/test/e2e/puppeteer.js +++ b/test/e2e/puppeteer.js @@ -35,6 +35,7 @@ const exceptionList = [ 'webgpu_postprocessing_sss', 'webgpu_postprocessing_traa', 'webgpu_reflection', + 'webgpu_test_memory', 'webgpu_texturegrad', 'webgpu_tsl_vfx_flames',