rayzee 5.4.2 → 5.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/rayzee.es.js +2147 -2124
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +42 -42
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/PathTracerApp.js +87 -13
- package/src/Processor/AssetLoader.js +50 -24
- package/src/Processor/createRenderTargetHelper.js +41 -14
- package/src/Stages/AdaptiveSampling.js +7 -0
package/package.json
CHANGED
package/src/PathTracerApp.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WebGPURenderer, RectAreaLightNode } from 'three/webgpu';
|
|
2
|
+
import { texture as _tslTexture, cubeTexture as _tslCubeTexture } from 'three/tsl';
|
|
2
3
|
import {
|
|
3
4
|
ACESFilmicToneMapping, Scene, EventDispatcher, TimestampQuery
|
|
4
5
|
} from 'three';
|
|
@@ -400,22 +401,100 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
400
401
|
|
|
401
402
|
this.stopAnimation();
|
|
402
403
|
clearTimeout( this._resizeDebounceTimer );
|
|
404
|
+
this._resizeDebounceTimer = null;
|
|
403
405
|
|
|
404
|
-
// Remove all tracked listeners (app-owned subscriptions across managers/DOM)
|
|
405
406
|
this._removeTrackedListeners();
|
|
406
|
-
|
|
407
407
|
setStatusCallback( null );
|
|
408
408
|
|
|
409
|
+
this.interactionManager?.deselect?.();
|
|
410
|
+
this.transformManager?.detach?.();
|
|
411
|
+
|
|
409
412
|
this.animationManager?.dispose();
|
|
410
413
|
this.transformManager?.dispose();
|
|
411
414
|
this.overlayManager?.dispose();
|
|
412
|
-
this._sceneHelpers?.clear();
|
|
413
415
|
this.lightManager?.dispose();
|
|
414
416
|
this.denoisingManager?.dispose();
|
|
415
|
-
this.pipeline?.dispose();
|
|
416
417
|
this.interactionManager?.dispose();
|
|
417
418
|
this.cameraManager?.dispose();
|
|
419
|
+
|
|
420
|
+
this.pipeline?.dispose();
|
|
421
|
+
|
|
422
|
+
// _sdf + assetLoader own the heaviest GPU allocations (material texture arrays,
|
|
423
|
+
// BVH/triangle buffers, loaded GLTF resources, BVH refit worker, loader caches).
|
|
424
|
+
// They are not referenced by the pipeline, so pipeline.dispose() does not reach them.
|
|
425
|
+
this._sdf?.dispose();
|
|
426
|
+
this._sdf = null;
|
|
427
|
+
|
|
428
|
+
this.assetLoader?.dispose();
|
|
429
|
+
this.assetLoader = null;
|
|
430
|
+
|
|
431
|
+
if ( this.meshScene ) {
|
|
432
|
+
|
|
433
|
+
this.meshScene.environment?.dispose();
|
|
434
|
+
this.meshScene.environment = null;
|
|
435
|
+
|
|
436
|
+
for ( const child of [ ...this.meshScene.children ] ) {
|
|
437
|
+
|
|
438
|
+
disposeObjectFromMemory( child );
|
|
439
|
+
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
this.meshScene.clear();
|
|
443
|
+
this.meshScene = null;
|
|
444
|
+
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
this._sceneHelpers?.clear();
|
|
448
|
+
this._sceneHelpers = null;
|
|
449
|
+
|
|
450
|
+
this.scene?.clear();
|
|
451
|
+
this.scene = null;
|
|
452
|
+
|
|
453
|
+
// Three.js 0.184 leaks (confirmed via heap-snapshot retainer analysis):
|
|
454
|
+
//
|
|
455
|
+
// 1) Renderer.dispose() does not remove the 'resize' listener it installs on
|
|
456
|
+
// _canvasTarget. The bound handler closes over the renderer, pinning the
|
|
457
|
+
// entire WebGPU graph (Backend, Nodes, Bindings, Pipelines, GPUDevice,
|
|
458
|
+
// every TSL node) alive indefinitely.
|
|
459
|
+
// See three/src/renderers/common/Renderer.js:292 (attach) and
|
|
460
|
+
// :2503 (dispose — missing removal).
|
|
461
|
+
//
|
|
462
|
+
// 2) Textures manager (one per renderer) registers a per-texture 'dispose'
|
|
463
|
+
// listener that closes over `this = Textures` — which transitively
|
|
464
|
+
// captures backend → renderer. These listeners are removed only when
|
|
465
|
+
// the texture itself is destroyed. For module-level singletons like
|
|
466
|
+
// EmptyTexture (new Texture in TextureNode.js) and its CubeTexture
|
|
467
|
+
// counterpart, the texture is never destroyed, so every renderer ever
|
|
468
|
+
// created leaks through the singleton's listener array.
|
|
469
|
+
//
|
|
470
|
+
// Both workarounds are safe when only a single PathTracerApp is active at a
|
|
471
|
+
// time. If you run multiple in parallel, reset listeners only on the renderer
|
|
472
|
+
// being disposed (not the shared singletons).
|
|
473
|
+
if ( this.renderer?._canvasTarget && this.renderer._onCanvasTargetResize ) {
|
|
474
|
+
|
|
475
|
+
this.renderer._canvasTarget.removeEventListener(
|
|
476
|
+
'resize',
|
|
477
|
+
this.renderer._onCanvasTargetResize
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
|
|
484
|
+
const emptyTex = _tslTexture().value;
|
|
485
|
+
const emptyCube = _tslCubeTexture().value;
|
|
486
|
+
if ( emptyTex?._listeners?.dispose ) emptyTex._listeners.dispose.length = 0;
|
|
487
|
+
if ( emptyCube?._listeners?.dispose ) emptyCube._listeners.dispose.length = 0;
|
|
488
|
+
|
|
489
|
+
} catch ( err ) {
|
|
490
|
+
|
|
491
|
+
console.warn( 'PathTracerApp: failed to clear TSL texture singleton listeners', err );
|
|
492
|
+
|
|
493
|
+
}
|
|
494
|
+
|
|
418
495
|
this.renderer?.dispose();
|
|
496
|
+
if ( this.renderer ) this.renderer._canvasTarget = null;
|
|
497
|
+
this.renderer = null;
|
|
419
498
|
|
|
420
499
|
if ( this._stats ) {
|
|
421
500
|
|
|
@@ -424,6 +503,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
424
503
|
|
|
425
504
|
}
|
|
426
505
|
|
|
506
|
+
this.stages = {};
|
|
427
507
|
this.isInitialized = false;
|
|
428
508
|
|
|
429
509
|
}
|
|
@@ -460,13 +540,9 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
460
540
|
this.interactionManager?.deselect();
|
|
461
541
|
this.transformManager?.detach?.();
|
|
462
542
|
|
|
463
|
-
//
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
disposeObjectFromMemory( this.assetLoader.targetModel );
|
|
467
|
-
this.assetLoader.targetModel = null;
|
|
468
|
-
|
|
469
|
-
}
|
|
543
|
+
// Release the loaded model. If loaded via loadObject3D(), the caller owns it —
|
|
544
|
+
// we only detach it from the scene. Otherwise dispose geometries/materials/textures.
|
|
545
|
+
this.assetLoader?.releaseTargetModel();
|
|
470
546
|
|
|
471
547
|
// Clear lights in the WebGPU light scene
|
|
472
548
|
this.lightManager?.clearLights?.();
|
|
@@ -1111,8 +1187,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1111
1187
|
}
|
|
1112
1188
|
} );
|
|
1113
1189
|
|
|
1114
|
-
window.renderer = this.renderer; // For debugging
|
|
1115
|
-
|
|
1116
1190
|
await this.renderer.init();
|
|
1117
1191
|
|
|
1118
1192
|
RectAreaLightNode.setLTC( RectAreaLightTexturesLib.init() );
|
|
@@ -37,6 +37,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
37
37
|
this.camera = camera;
|
|
38
38
|
this.controls = controls;
|
|
39
39
|
this.targetModel = null;
|
|
40
|
+
this._externalModel = null;
|
|
40
41
|
this.floorPlane = null;
|
|
41
42
|
this.sceneScale = 1.0;
|
|
42
43
|
this.loaderCache = {};
|
|
@@ -46,6 +47,31 @@ export class AssetLoader extends EventDispatcher {
|
|
|
46
47
|
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Releases the current targetModel. If it was supplied by the caller via
|
|
52
|
+
* loadObject3D(), we only detach it from its parent — the caller still owns
|
|
53
|
+
* that Object3D and may reuse it. Otherwise we disposeObjectFromMemory() to
|
|
54
|
+
* free geometry/material/texture GPU resources.
|
|
55
|
+
*/
|
|
56
|
+
releaseTargetModel() {
|
|
57
|
+
|
|
58
|
+
if ( ! this.targetModel ) return;
|
|
59
|
+
|
|
60
|
+
if ( this.targetModel === this._externalModel ) {
|
|
61
|
+
|
|
62
|
+
this.targetModel.parent?.remove( this.targetModel );
|
|
63
|
+
|
|
64
|
+
} else {
|
|
65
|
+
|
|
66
|
+
disposeObjectFromMemory( this.targetModel );
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.targetModel = null;
|
|
71
|
+
this._externalModel = null;
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
49
75
|
setRenderer( renderer ) {
|
|
50
76
|
|
|
51
77
|
this.renderer = renderer;
|
|
@@ -173,7 +199,11 @@ export class AssetLoader extends EventDispatcher {
|
|
|
173
199
|
|
|
174
200
|
} else {
|
|
175
201
|
|
|
176
|
-
|
|
202
|
+
// Strip query string + fragment before extracting extension, otherwise
|
|
203
|
+
// URLs like ".../foo.hdr?v=2" get mis-detected and fall through to the
|
|
204
|
+
// regular TextureLoader, which can't parse HDR/EXR binary data.
|
|
205
|
+
const cleanPath = envUrl.split( /[?#]/ )[ 0 ];
|
|
206
|
+
const extension = cleanPath.split( '.' ).pop().toLowerCase();
|
|
177
207
|
texture = await this.loadEnvironmentByExtension( envUrl, extension );
|
|
178
208
|
|
|
179
209
|
}
|
|
@@ -479,7 +509,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
479
509
|
loader.parse( gltfContent, '',
|
|
480
510
|
gltf => {
|
|
481
511
|
|
|
482
|
-
|
|
512
|
+
this.releaseTargetModel();
|
|
483
513
|
this.targetModel = gltf.scene;
|
|
484
514
|
this.onModelLoad( this.targetModel ).then( () => resolve( gltf ) );
|
|
485
515
|
|
|
@@ -522,7 +552,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
522
552
|
const object = objLoader.parse( objContent );
|
|
523
553
|
object.name = filePath;
|
|
524
554
|
|
|
525
|
-
|
|
555
|
+
this.releaseTargetModel();
|
|
526
556
|
this.targetModel = object;
|
|
527
557
|
await this.onModelLoad( this.targetModel );
|
|
528
558
|
return object;
|
|
@@ -603,7 +633,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
603
633
|
const objContent = strFromU8( objFile.content );
|
|
604
634
|
const object = objLoader.parse( objContent );
|
|
605
635
|
|
|
606
|
-
|
|
636
|
+
this.releaseTargetModel();
|
|
607
637
|
this.targetModel = object;
|
|
608
638
|
await this.onModelLoad( this.targetModel );
|
|
609
639
|
|
|
@@ -773,7 +803,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
773
803
|
const data = await loader.loadAsync( modelUrl );
|
|
774
804
|
updateLoading( { status: "Processing Data...", progress: 10 } );
|
|
775
805
|
|
|
776
|
-
|
|
806
|
+
this.releaseTargetModel();
|
|
777
807
|
|
|
778
808
|
this.targetModel = data.scene;
|
|
779
809
|
this.animations = data.animations || [];
|
|
@@ -806,7 +836,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
806
836
|
|
|
807
837
|
const data = await loader.parseAsync( arrayBuffer, '' );
|
|
808
838
|
|
|
809
|
-
|
|
839
|
+
this.releaseTargetModel();
|
|
810
840
|
|
|
811
841
|
this.targetModel = data.scene;
|
|
812
842
|
this.animations = data.animations || [];
|
|
@@ -845,7 +875,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
845
875
|
}
|
|
846
876
|
|
|
847
877
|
const object = this.loaderCache.fbx.parse( arrayBuffer );
|
|
848
|
-
|
|
878
|
+
this.releaseTargetModel();
|
|
849
879
|
this.targetModel = object;
|
|
850
880
|
|
|
851
881
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -882,7 +912,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
882
912
|
const object = this.loaderCache.obj.parse( contents );
|
|
883
913
|
object.name = filename;
|
|
884
914
|
|
|
885
|
-
|
|
915
|
+
this.releaseTargetModel();
|
|
886
916
|
this.targetModel = object;
|
|
887
917
|
|
|
888
918
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -920,7 +950,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
920
950
|
const mesh = new Mesh( geometry, material );
|
|
921
951
|
mesh.name = filename;
|
|
922
952
|
|
|
923
|
-
|
|
953
|
+
this.releaseTargetModel();
|
|
924
954
|
this.targetModel = mesh;
|
|
925
955
|
|
|
926
956
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -970,7 +1000,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
970
1000
|
}
|
|
971
1001
|
|
|
972
1002
|
object.name = filename;
|
|
973
|
-
|
|
1003
|
+
this.releaseTargetModel();
|
|
974
1004
|
this.targetModel = object;
|
|
975
1005
|
|
|
976
1006
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -1007,7 +1037,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1007
1037
|
const collada = this.loaderCache.collada.parse( contents );
|
|
1008
1038
|
collada.scene.name = filename;
|
|
1009
1039
|
|
|
1010
|
-
|
|
1040
|
+
this.releaseTargetModel();
|
|
1011
1041
|
this.targetModel = collada.scene;
|
|
1012
1042
|
|
|
1013
1043
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -1042,7 +1072,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1042
1072
|
|
|
1043
1073
|
const object = this.loaderCache.threemf.parse( arrayBuffer );
|
|
1044
1074
|
|
|
1045
|
-
|
|
1075
|
+
this.releaseTargetModel();
|
|
1046
1076
|
this.targetModel = object;
|
|
1047
1077
|
|
|
1048
1078
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -1078,7 +1108,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1078
1108
|
const object = this.loaderCache.usdz.parse( arrayBuffer );
|
|
1079
1109
|
object.name = filename;
|
|
1080
1110
|
|
|
1081
|
-
|
|
1111
|
+
this.releaseTargetModel();
|
|
1082
1112
|
this.targetModel = object;
|
|
1083
1113
|
|
|
1084
1114
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
@@ -1101,8 +1131,9 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1101
1131
|
|
|
1102
1132
|
object3d.name = object3d.name || name;
|
|
1103
1133
|
|
|
1104
|
-
|
|
1134
|
+
this.releaseTargetModel();
|
|
1105
1135
|
this.targetModel = object3d;
|
|
1136
|
+
this._externalModel = object3d;
|
|
1106
1137
|
|
|
1107
1138
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
1108
1139
|
await this.onModelLoad( this.targetModel );
|
|
@@ -1379,23 +1410,18 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1379
1410
|
}
|
|
1380
1411
|
|
|
1381
1412
|
this.loaderCache = {};
|
|
1382
|
-
super.dispose(); // Use EventDispatcher's dispose method
|
|
1383
|
-
|
|
1384
|
-
if ( this.targetModel ) {
|
|
1385
1413
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1414
|
+
// Three.js EventDispatcher exposes no dispose()/removeAllEventListeners().
|
|
1415
|
+
// Clear the internal listener map directly so handlers don't retain references.
|
|
1416
|
+
this._listeners = undefined;
|
|
1390
1417
|
|
|
1391
|
-
|
|
1418
|
+
this.releaseTargetModel();
|
|
1392
1419
|
|
|
1393
1420
|
}
|
|
1394
1421
|
|
|
1395
1422
|
removeAllEventListeners() {
|
|
1396
1423
|
|
|
1397
|
-
|
|
1398
|
-
super.dispose();
|
|
1424
|
+
this._listeners = undefined;
|
|
1399
1425
|
|
|
1400
1426
|
}
|
|
1401
1427
|
|
|
@@ -206,32 +206,33 @@ export function createRenderTargetHelper( renderer, renderTargetOrTexture, optio
|
|
|
206
206
|
|
|
207
207
|
} );
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
function onPointerMove( e ) {
|
|
210
210
|
|
|
211
211
|
if ( ! isDragging ) return;
|
|
212
212
|
|
|
213
213
|
const newLeft = e.clientX - dragOffsetX;
|
|
214
214
|
const newTop = e.clientY - dragOffsetY;
|
|
215
215
|
|
|
216
|
-
// Keep within window bounds
|
|
217
216
|
const maxX = window.innerWidth - container.offsetWidth;
|
|
218
217
|
const maxY = window.innerHeight - container.offsetHeight;
|
|
219
218
|
|
|
220
219
|
container.style.left = `${Math.max( 0, Math.min( newLeft, maxX ) )}px`;
|
|
221
220
|
container.style.top = `${Math.max( 0, Math.min( newTop, maxY ) )}px`;
|
|
222
221
|
|
|
223
|
-
// Reset position properties that would otherwise take precedence
|
|
224
222
|
container.style.bottom = 'auto';
|
|
225
223
|
container.style.right = 'auto';
|
|
226
224
|
|
|
227
|
-
}
|
|
225
|
+
}
|
|
228
226
|
|
|
229
|
-
|
|
227
|
+
function onPointerUp() {
|
|
230
228
|
|
|
231
229
|
isDragging = false;
|
|
232
230
|
document.body.style.userSelect = '';
|
|
233
231
|
|
|
234
|
-
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
window.addEventListener( 'pointermove', onPointerMove );
|
|
235
|
+
window.addEventListener( 'pointerup', onPointerUp );
|
|
235
236
|
|
|
236
237
|
// Optimize resize handling
|
|
237
238
|
function handleResize() {
|
|
@@ -353,19 +354,20 @@ export function createRenderTargetHelper( renderer, renderTargetOrTexture, optio
|
|
|
353
354
|
|
|
354
355
|
};
|
|
355
356
|
|
|
356
|
-
|
|
357
|
-
container.addEventListener( 'mousedown', () => {
|
|
357
|
+
function onContainerMouseDown() {
|
|
358
358
|
|
|
359
359
|
window.addEventListener( 'mousemove', handleResize );
|
|
360
360
|
|
|
361
|
-
}
|
|
361
|
+
}
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
function onMouseUp() {
|
|
364
364
|
|
|
365
365
|
window.removeEventListener( 'mousemove', handleResize );
|
|
366
366
|
|
|
367
|
-
}
|
|
367
|
+
}
|
|
368
368
|
|
|
369
|
+
container.addEventListener( 'mousedown', onContainerMouseDown );
|
|
370
|
+
window.addEventListener( 'mouseup', onMouseUp );
|
|
369
371
|
window.addEventListener( 'resize', handleResize );
|
|
370
372
|
|
|
371
373
|
// Auto-update animation frame
|
|
@@ -456,20 +458,45 @@ export function createRenderTargetHelper( renderer, renderTargetOrTexture, optio
|
|
|
456
458
|
*/
|
|
457
459
|
container.dispose = function dispose() {
|
|
458
460
|
|
|
459
|
-
if (
|
|
461
|
+
if ( animFrameId ) {
|
|
460
462
|
|
|
461
463
|
cancelAnimationFrame( animFrameId );
|
|
464
|
+
animFrameId = null;
|
|
462
465
|
|
|
463
466
|
}
|
|
464
467
|
|
|
465
|
-
// Remove
|
|
468
|
+
// Remove window listeners — these close over renderer/renderTarget/container
|
|
469
|
+
// and pin the entire helper graph alive until the page unloads if not cleaned up.
|
|
470
|
+
window.removeEventListener( 'pointermove', onPointerMove );
|
|
471
|
+
window.removeEventListener( 'pointerup', onPointerUp );
|
|
472
|
+
window.removeEventListener( 'mouseup', onMouseUp );
|
|
473
|
+
window.removeEventListener( 'mousemove', handleResize );
|
|
474
|
+
window.removeEventListener( 'resize', handleResize );
|
|
475
|
+
|
|
466
476
|
if ( container.parentNode ) {
|
|
467
477
|
|
|
468
478
|
container.parentNode.removeChild( container );
|
|
469
479
|
|
|
470
480
|
}
|
|
471
481
|
|
|
472
|
-
//
|
|
482
|
+
// Drop closures attached to the container. These close over `renderer`,
|
|
483
|
+
// `renderTargetOrTexture`, and the canvas — keeping them around after the
|
|
484
|
+
// owning stage has been disposed retains the entire Three.js WebGPU graph.
|
|
485
|
+
container.startAutoUpdate = null;
|
|
486
|
+
container.stopAutoUpdate = null;
|
|
487
|
+
container.show = null;
|
|
488
|
+
container.hide = null;
|
|
489
|
+
container.toggle = null;
|
|
490
|
+
container.update = null;
|
|
491
|
+
container.dispose = null;
|
|
492
|
+
|
|
493
|
+
if ( domCanvas ) {
|
|
494
|
+
|
|
495
|
+
domCanvas.width = 0;
|
|
496
|
+
domCanvas.height = 0;
|
|
497
|
+
|
|
498
|
+
}
|
|
499
|
+
|
|
473
500
|
clampedPixels = null;
|
|
474
501
|
|
|
475
502
|
};
|
|
@@ -485,6 +485,13 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
485
485
|
this.heatmapTarget?.dispose();
|
|
486
486
|
this.helper?.dispose();
|
|
487
487
|
|
|
488
|
+
this._computeNode = null;
|
|
489
|
+
this._heatmapComputeNode = null;
|
|
490
|
+
this._heatmapStorageTex = null;
|
|
491
|
+
this._outputStorageTex = null;
|
|
492
|
+
this.heatmapTarget = null;
|
|
493
|
+
this.helper = null;
|
|
494
|
+
|
|
488
495
|
}
|
|
489
496
|
|
|
490
497
|
}
|