rayzee 5.6.0 → 5.6.1
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 +71 -42
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +5 -5
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/Passes/AIUpscaler.js +22 -0
- package/src/Passes/OIDNDenoiser.js +93 -28
- package/src/Pipeline/RenderPipeline.js +3 -0
- package/src/Stages/ASVGF.js +18 -0
- package/src/Stages/AdaptiveSampling.js +2 -0
- package/src/Stages/AutoExposure.js +2 -0
- package/src/Stages/BilateralFilter.js +2 -0
- package/src/Stages/Display.js +1 -0
- package/src/Stages/EdgeFilter.js +1 -0
- package/src/Stages/MotionVector.js +1 -0
- package/src/Stages/SSRC.js +6 -0
- package/src/Stages/Variance.js +3 -0
- package/src/managers/DenoisingManager.js +68 -14
package/package.json
CHANGED
package/src/Passes/AIUpscaler.js
CHANGED
|
@@ -104,6 +104,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
104
104
|
this._worker = null;
|
|
105
105
|
this._currentModelUrl = null;
|
|
106
106
|
this._tileId = 0;
|
|
107
|
+
this._pendingWorkerHandlers = new Set();
|
|
107
108
|
|
|
108
109
|
// Alpha channel cache (bilinear-upscaled from source, applied per tile)
|
|
109
110
|
this._upscaledAlpha = null;
|
|
@@ -315,6 +316,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
315
316
|
|
|
316
317
|
this._capturedSource = null;
|
|
317
318
|
this._upscaledAlpha = null;
|
|
319
|
+
this._backupCanvas = null;
|
|
318
320
|
|
|
319
321
|
// Only clean up if abort() hasn't already done it
|
|
320
322
|
if ( this.state.isUpscaling ) {
|
|
@@ -334,6 +336,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
334
336
|
if ( ! this.state.isUpscaling ) return;
|
|
335
337
|
|
|
336
338
|
this.state.abortController?.abort();
|
|
339
|
+
this._cleanupPendingWorkerHandlers();
|
|
337
340
|
|
|
338
341
|
// Restore input visibility and canvas state
|
|
339
342
|
this.input.style.opacity = '1';
|
|
@@ -610,6 +613,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
610
613
|
|
|
611
614
|
if ( e.data.id !== id ) return;
|
|
612
615
|
this._worker.removeEventListener( 'message', handler );
|
|
616
|
+
this._pendingWorkerHandlers.delete( handler );
|
|
613
617
|
|
|
614
618
|
if ( e.data.type === 'inferred' ) {
|
|
615
619
|
|
|
@@ -623,6 +627,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
623
627
|
|
|
624
628
|
};
|
|
625
629
|
|
|
630
|
+
this._pendingWorkerHandlers.add( handler );
|
|
626
631
|
this._worker.addEventListener( 'message', handler );
|
|
627
632
|
this._worker.postMessage(
|
|
628
633
|
{ type: 'infer', tileData, width, height, id },
|
|
@@ -858,9 +863,26 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
858
863
|
|
|
859
864
|
// ─── Disposal ─────────────────────────────────────────────────────────────
|
|
860
865
|
|
|
866
|
+
_cleanupPendingWorkerHandlers() {
|
|
867
|
+
|
|
868
|
+
if ( this._worker ) {
|
|
869
|
+
|
|
870
|
+
for ( const handler of this._pendingWorkerHandlers ) {
|
|
871
|
+
|
|
872
|
+
this._worker.removeEventListener( 'message', handler );
|
|
873
|
+
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
this._pendingWorkerHandlers.clear();
|
|
879
|
+
|
|
880
|
+
}
|
|
881
|
+
|
|
861
882
|
async dispose() {
|
|
862
883
|
|
|
863
884
|
this.abort();
|
|
885
|
+
this._cleanupPendingWorkerHandlers();
|
|
864
886
|
|
|
865
887
|
if ( this._worker ) {
|
|
866
888
|
|
|
@@ -87,6 +87,7 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
87
87
|
// renderer.getArrayBufferAsync because the source is a raw GPUBuffer,
|
|
88
88
|
// not a Three.js BufferAttribute.
|
|
89
89
|
this._alphaReadbackBuffer = null;
|
|
90
|
+
this._alphaReadbackMapped = false;
|
|
90
91
|
|
|
91
92
|
// Cached alpha channel from the input color buffer (OIDN discards alpha)
|
|
92
93
|
this._cachedAlpha = null;
|
|
@@ -108,6 +109,9 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
108
109
|
abortController: null
|
|
109
110
|
};
|
|
110
111
|
|
|
112
|
+
// Track in-flight tile staging buffers so they can be destroyed on abort
|
|
113
|
+
this._pendingStagingBuffers = new Set();
|
|
114
|
+
|
|
111
115
|
this.currentTZAUrl = null;
|
|
112
116
|
this.unet = null;
|
|
113
117
|
|
|
@@ -514,7 +518,22 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
514
518
|
this._gpuInputBuffers.albedo?.destroy();
|
|
515
519
|
this._gpuInputBuffers.normal?.destroy();
|
|
516
520
|
this._gpuInputPadBuffer?.destroy();
|
|
521
|
+
|
|
522
|
+
// Unmap before destroying if a mapAsync resolved but unmap hasn't been called yet.
|
|
523
|
+
// If mapAsync is still pending, destroy() will reject it — _cacheInputAlpha's
|
|
524
|
+
// catch handler covers that case.
|
|
525
|
+
if ( this._alphaReadbackMapped && this._alphaReadbackBuffer ) {
|
|
526
|
+
|
|
527
|
+
try {
|
|
528
|
+
|
|
529
|
+
this._alphaReadbackBuffer.unmap();
|
|
530
|
+
|
|
531
|
+
} catch { /* already unmapped or destroyed */ }
|
|
532
|
+
|
|
533
|
+
}
|
|
534
|
+
|
|
517
535
|
this._alphaReadbackBuffer?.destroy();
|
|
536
|
+
this._alphaReadbackMapped = false;
|
|
518
537
|
this._gpuInputBuffers = { color: null, albedo: null, normal: null };
|
|
519
538
|
this._gpuInputPadBuffer = null;
|
|
520
539
|
this._gpuInputPaddedRowBytes = 0;
|
|
@@ -550,7 +569,19 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
550
569
|
enc.copyBufferToBuffer( this._gpuInputBuffers.color, 0, staging, 0, byteSize );
|
|
551
570
|
device.queue.submit( [ enc.finish() ] );
|
|
552
571
|
|
|
553
|
-
|
|
572
|
+
this._alphaReadbackMapped = true;
|
|
573
|
+
try {
|
|
574
|
+
|
|
575
|
+
await staging.mapAsync( GPUMapMode.READ );
|
|
576
|
+
|
|
577
|
+
} catch {
|
|
578
|
+
|
|
579
|
+
// Buffer was destroyed while mapAsync was pending (resize or dispose)
|
|
580
|
+
this._alphaReadbackMapped = false;
|
|
581
|
+
return;
|
|
582
|
+
|
|
583
|
+
}
|
|
584
|
+
|
|
554
585
|
const f32 = new Float32Array( staging.getMappedRange() );
|
|
555
586
|
|
|
556
587
|
// Extract alpha channel as uint8 (pre-multiplied is not needed — alpha is 0 or 1)
|
|
@@ -563,6 +594,7 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
563
594
|
}
|
|
564
595
|
|
|
565
596
|
staging.unmap();
|
|
597
|
+
this._alphaReadbackMapped = false;
|
|
566
598
|
|
|
567
599
|
this._cachedAlpha = alpha;
|
|
568
600
|
this._cachedAlphaWidth = width;
|
|
@@ -653,6 +685,8 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
653
685
|
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
|
654
686
|
} );
|
|
655
687
|
|
|
688
|
+
this._pendingStagingBuffers.add( staging );
|
|
689
|
+
|
|
656
690
|
// Copy each tile row from its position in the full output buffer
|
|
657
691
|
const enc = device.createCommandEncoder();
|
|
658
692
|
|
|
@@ -710,8 +744,15 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
710
744
|
|
|
711
745
|
staging.unmap();
|
|
712
746
|
staging.destroy();
|
|
747
|
+
this._pendingStagingBuffers.delete( staging );
|
|
713
748
|
this.ctx.putImageData( tileImageData, tile.x, tile.y );
|
|
714
749
|
|
|
750
|
+
} ).catch( () => {
|
|
751
|
+
|
|
752
|
+
// mapAsync rejected (abort or GPU lost) — destroy the buffer
|
|
753
|
+
staging.destroy();
|
|
754
|
+
this._pendingStagingBuffers.delete( staging );
|
|
755
|
+
|
|
715
756
|
} );
|
|
716
757
|
|
|
717
758
|
}
|
|
@@ -745,44 +786,50 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
745
786
|
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
|
746
787
|
} );
|
|
747
788
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
789
|
+
try {
|
|
790
|
+
|
|
791
|
+
// Queue a copy from the oidn output buffer (STORAGE|COPY_SRC) to staging
|
|
792
|
+
const encoder = device.createCommandEncoder( { label: 'oidn-readback' } );
|
|
793
|
+
encoder.copyBufferToBuffer( gpuBuffer, 0, stagingBuffer, 0, byteSize );
|
|
794
|
+
device.queue.submit( [ encoder.finish() ] );
|
|
795
|
+
|
|
796
|
+
await stagingBuffer.mapAsync( GPUMapMode.READ );
|
|
797
|
+
const float32 = new Float32Array( stagingBuffer.getMappedRange() );
|
|
798
|
+
|
|
799
|
+
const imageData = new ImageData( width, height );
|
|
800
|
+
const exposure = this.getExposure();
|
|
801
|
+
const saturation = this.getSaturation();
|
|
802
|
+
const tmFn = TONE_MAP_FNS.get( this.getToneMapping() ) || TONE_MAP_FNS.get( ACESFilmicToneMapping );
|
|
803
|
+
const alpha = this._cachedAlpha;
|
|
752
804
|
|
|
753
|
-
|
|
754
|
-
const float32 = new Float32Array( stagingBuffer.getMappedRange() );
|
|
805
|
+
for ( let i = 0, len = float32.length; i < len; i += 4 ) {
|
|
755
806
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
const tmFn = TONE_MAP_FNS.get( this.getToneMapping() ) || TONE_MAP_FNS.get( ACESFilmicToneMapping );
|
|
760
|
-
const alpha = this._cachedAlpha;
|
|
807
|
+
// Exposure + saturation (pre-tonemap, matching Display)
|
|
808
|
+
let er = float32[ i ] * exposure, eg = float32[ i + 1 ] * exposure, eb = float32[ i + 2 ] * exposure;
|
|
809
|
+
if ( saturation !== 1.0 ) {
|
|
761
810
|
|
|
762
|
-
|
|
811
|
+
_tmOut[ 0 ] = er; _tmOut[ 1 ] = eg; _tmOut[ 2 ] = eb;
|
|
812
|
+
applySaturation( _tmOut, saturation );
|
|
813
|
+
er = _tmOut[ 0 ]; eg = _tmOut[ 1 ]; eb = _tmOut[ 2 ];
|
|
763
814
|
|
|
764
|
-
|
|
765
|
-
let er = float32[ i ] * exposure, eg = float32[ i + 1 ] * exposure, eb = float32[ i + 2 ] * exposure;
|
|
766
|
-
if ( saturation !== 1.0 ) {
|
|
815
|
+
}
|
|
767
816
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
817
|
+
tmFn( er, eg, eb, 1.0, _tmOut );
|
|
818
|
+
imageData.data[ i ] = _tmOut[ 0 ] ** SRGB_GAMMA * 255 | 0;
|
|
819
|
+
imageData.data[ i + 1 ] = _tmOut[ 1 ] ** SRGB_GAMMA * 255 | 0;
|
|
820
|
+
imageData.data[ i + 2 ] = _tmOut[ 2 ] ** SRGB_GAMMA * 255 | 0;
|
|
821
|
+
imageData.data[ i + 3 ] = alpha ? alpha[ i >> 2 ] : 255;
|
|
771
822
|
|
|
772
823
|
}
|
|
773
824
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
imageData.data[ i + 1 ] = _tmOut[ 1 ] ** SRGB_GAMMA * 255 | 0;
|
|
777
|
-
imageData.data[ i + 2 ] = _tmOut[ 2 ] ** SRGB_GAMMA * 255 | 0;
|
|
778
|
-
imageData.data[ i + 3 ] = alpha ? alpha[ i >> 2 ] : 255;
|
|
825
|
+
stagingBuffer.unmap();
|
|
826
|
+
this.ctx.putImageData( imageData, 0, 0 );
|
|
779
827
|
|
|
780
|
-
}
|
|
828
|
+
} finally {
|
|
781
829
|
|
|
782
|
-
|
|
783
|
-
stagingBuffer.destroy();
|
|
830
|
+
stagingBuffer.destroy();
|
|
784
831
|
|
|
785
|
-
|
|
832
|
+
}
|
|
786
833
|
|
|
787
834
|
}
|
|
788
835
|
|
|
@@ -793,6 +840,9 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
793
840
|
// Signal abort to current operation
|
|
794
841
|
this.state.abortController?.abort();
|
|
795
842
|
|
|
843
|
+
// Destroy any in-flight tile staging buffers that mapAsync won't resolve
|
|
844
|
+
this._destroyPendingStagingBuffers();
|
|
845
|
+
|
|
796
846
|
// Restore input visibility
|
|
797
847
|
this.input.style.opacity = '1';
|
|
798
848
|
|
|
@@ -896,11 +946,26 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
896
946
|
|
|
897
947
|
}
|
|
898
948
|
|
|
949
|
+
_destroyPendingStagingBuffers() {
|
|
950
|
+
|
|
951
|
+
for ( const buf of this._pendingStagingBuffers ) {
|
|
952
|
+
|
|
953
|
+
buf.destroy();
|
|
954
|
+
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
this._pendingStagingBuffers.clear();
|
|
958
|
+
|
|
959
|
+
}
|
|
960
|
+
|
|
899
961
|
dispose() {
|
|
900
962
|
|
|
901
963
|
// Abort any ongoing operations
|
|
902
964
|
this.abort();
|
|
903
965
|
|
|
966
|
+
// Destroy any remaining staging buffers
|
|
967
|
+
this._destroyPendingStagingBuffers();
|
|
968
|
+
|
|
904
969
|
// Dispose resources
|
|
905
970
|
this.unet?.dispose();
|
|
906
971
|
this._destroyGPUInputBuffers();
|
package/src/Stages/ASVGF.js
CHANGED
|
@@ -832,6 +832,24 @@ export class ASVGF extends RenderStage {
|
|
|
832
832
|
this._heatmapComputeNode?.dispose();
|
|
833
833
|
this._heatmapStorageTex?.dispose();
|
|
834
834
|
this.heatmapTarget?.dispose();
|
|
835
|
+
|
|
836
|
+
// Dispose input TextureNode objects
|
|
837
|
+
this._colorTexNode?.dispose();
|
|
838
|
+
this._normalDepthTexNode?.dispose();
|
|
839
|
+
this._motionTexNode?.dispose();
|
|
840
|
+
this._readTemporalTexNode?.dispose();
|
|
841
|
+
this._readPrevNDTexNode?.dispose();
|
|
842
|
+
this._gradientReadTexNode?.dispose();
|
|
843
|
+
|
|
844
|
+
// Dispose heatmap TextureNode objects
|
|
845
|
+
this._heatmapRawColorTexNode?.dispose();
|
|
846
|
+
this._heatmapColorTexNode?.dispose();
|
|
847
|
+
this._heatmapTemporalTexNode?.dispose();
|
|
848
|
+
this._heatmapNDTexNode?.dispose();
|
|
849
|
+
this._heatmapMotionTexNode?.dispose();
|
|
850
|
+
this._heatmapGradientTexNode?.dispose();
|
|
851
|
+
|
|
852
|
+
// dispose() also removes the DOM node
|
|
835
853
|
this.heatmapHelper?.dispose();
|
|
836
854
|
|
|
837
855
|
}
|
|
@@ -483,6 +483,7 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
483
483
|
this._heatmapStorageTex?.dispose();
|
|
484
484
|
this._outputStorageTex?.dispose();
|
|
485
485
|
this.heatmapTarget?.dispose();
|
|
486
|
+
this._varianceTexNode?.dispose();
|
|
486
487
|
this.helper?.dispose();
|
|
487
488
|
|
|
488
489
|
this._computeNode = null;
|
|
@@ -490,6 +491,7 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
490
491
|
this._heatmapStorageTex = null;
|
|
491
492
|
this._outputStorageTex = null;
|
|
492
493
|
this.heatmapTarget = null;
|
|
494
|
+
this._varianceTexNode = null;
|
|
493
495
|
this.helper = null;
|
|
494
496
|
|
|
495
497
|
}
|
|
@@ -698,6 +698,8 @@ export class AutoExposure extends RenderStage {
|
|
|
698
698
|
this._reductionStorageTex?.dispose();
|
|
699
699
|
this._reductionReadTarget?.dispose();
|
|
700
700
|
this._readbackBuffer?.dispose();
|
|
701
|
+
this._inputTexNode?.dispose();
|
|
702
|
+
this._reductionReadTexNode?.dispose();
|
|
701
703
|
|
|
702
704
|
}
|
|
703
705
|
|
package/src/Stages/Display.js
CHANGED
package/src/Stages/EdgeFilter.js
CHANGED
package/src/Stages/SSRC.js
CHANGED
|
@@ -194,6 +194,12 @@ export class SSRC extends RenderStage {
|
|
|
194
194
|
this._prevNDTexA.dispose();
|
|
195
195
|
this._prevNDTexB.dispose();
|
|
196
196
|
this._outputTex.dispose();
|
|
197
|
+
this._colorTexNode?.dispose();
|
|
198
|
+
this._ndTexNode?.dispose();
|
|
199
|
+
this._motionTexNode?.dispose();
|
|
200
|
+
this._readCacheTexNode?.dispose();
|
|
201
|
+
this._readPrevNDTexNode?.dispose();
|
|
202
|
+
this._readPass1CacheTexNode?.dispose();
|
|
197
203
|
|
|
198
204
|
}
|
|
199
205
|
|
package/src/Stages/Variance.js
CHANGED
|
@@ -371,6 +371,9 @@ export class Variance extends RenderStage {
|
|
|
371
371
|
this._computeNodeB?.dispose();
|
|
372
372
|
this._storageTexA?.dispose();
|
|
373
373
|
this._storageTexB?.dispose();
|
|
374
|
+
this._colorTexNode?.dispose();
|
|
375
|
+
this._readTexNodeA?.dispose();
|
|
376
|
+
this._readTexNodeB?.dispose();
|
|
374
377
|
|
|
375
378
|
}
|
|
376
379
|
|
|
@@ -54,6 +54,17 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
54
54
|
this._lastRenderWidth = 0;
|
|
55
55
|
this._lastRenderHeight = 0;
|
|
56
56
|
|
|
57
|
+
// Track the current completion-chain listener so it can be removed on re-trigger
|
|
58
|
+
this._pendingStartUpscaler = null;
|
|
59
|
+
|
|
60
|
+
// Bound event forwarding handlers (stored for removal on re-setup / dispose)
|
|
61
|
+
this._denoiserStartHandler = null;
|
|
62
|
+
this._denoiserEndHandler = null;
|
|
63
|
+
this._upscalerResChangedHandler = null;
|
|
64
|
+
this._upscalerStartHandler = null;
|
|
65
|
+
this._upscalerProgressHandler = null;
|
|
66
|
+
this._upscalerEndHandler = null;
|
|
67
|
+
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
_createDenoiserCanvas( mainCanvas ) {
|
|
@@ -146,11 +157,13 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
146
157
|
|
|
147
158
|
this.denoiser.enabled = DEFAULT_STATE.enableOIDN;
|
|
148
159
|
|
|
149
|
-
// Forward lifecycle events
|
|
150
|
-
this.
|
|
151
|
-
this.dispatchEvent( { type: EngineEvents.DENOISING_START } )
|
|
152
|
-
this.
|
|
153
|
-
this.dispatchEvent( { type: EngineEvents.DENOISING_END } )
|
|
160
|
+
// Forward lifecycle events (store refs for removal on re-setup / dispose)
|
|
161
|
+
this._denoiserStartHandler = () =>
|
|
162
|
+
this.dispatchEvent( { type: EngineEvents.DENOISING_START } );
|
|
163
|
+
this._denoiserEndHandler = () =>
|
|
164
|
+
this.dispatchEvent( { type: EngineEvents.DENOISING_END } );
|
|
165
|
+
this.denoiser.addEventListener( 'start', this._denoiserStartHandler );
|
|
166
|
+
this.denoiser.addEventListener( 'end', this._denoiserEndHandler );
|
|
154
167
|
|
|
155
168
|
}
|
|
156
169
|
|
|
@@ -189,15 +202,19 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
189
202
|
|
|
190
203
|
this.upscaler.enabled = DEFAULT_STATE.enableUpscaler || false;
|
|
191
204
|
|
|
192
|
-
// Forward lifecycle events
|
|
193
|
-
this.
|
|
194
|
-
this.dispatchEvent( { type: 'resolution_changed', width: e.width, height: e.height } )
|
|
195
|
-
this.
|
|
196
|
-
this.dispatchEvent( { type: EngineEvents.UPSCALING_START } )
|
|
197
|
-
this.
|
|
198
|
-
this.dispatchEvent( { type: EngineEvents.UPSCALING_PROGRESS, progress: e.progress } )
|
|
199
|
-
this.
|
|
200
|
-
this.dispatchEvent( { type: EngineEvents.UPSCALING_END } )
|
|
205
|
+
// Forward lifecycle events (store refs for removal on re-setup / dispose)
|
|
206
|
+
this._upscalerResChangedHandler = ( e ) =>
|
|
207
|
+
this.dispatchEvent( { type: 'resolution_changed', width: e.width, height: e.height } );
|
|
208
|
+
this._upscalerStartHandler = () =>
|
|
209
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_START } );
|
|
210
|
+
this._upscalerProgressHandler = ( e ) =>
|
|
211
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_PROGRESS, progress: e.progress } );
|
|
212
|
+
this._upscalerEndHandler = () =>
|
|
213
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_END } );
|
|
214
|
+
this.upscaler.addEventListener( 'resolution_changed', this._upscalerResChangedHandler );
|
|
215
|
+
this.upscaler.addEventListener( 'start', this._upscalerStartHandler );
|
|
216
|
+
this.upscaler.addEventListener( 'progress', this._upscalerProgressHandler );
|
|
217
|
+
this.upscaler.addEventListener( 'end', this._upscalerEndHandler );
|
|
201
218
|
|
|
202
219
|
}
|
|
203
220
|
|
|
@@ -353,8 +370,23 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
353
370
|
* @param {Function} params.isStillComplete - () => boolean, guard for async race
|
|
354
371
|
* @param {import('../Pipeline/PipelineContext.js').PipelineContext} params.context
|
|
355
372
|
*/
|
|
373
|
+
_cleanupCompletionListener() {
|
|
374
|
+
|
|
375
|
+
if ( this._pendingStartUpscaler && this.denoiser ) {
|
|
376
|
+
|
|
377
|
+
this.denoiser.removeEventListener( 'end', this._pendingStartUpscaler );
|
|
378
|
+
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
this._pendingStartUpscaler = null;
|
|
382
|
+
|
|
383
|
+
}
|
|
384
|
+
|
|
356
385
|
onRenderComplete( { isStillComplete, context } ) {
|
|
357
386
|
|
|
387
|
+
// Remove any stale completion-chain listener from a previous render cycle
|
|
388
|
+
this._cleanupCompletionListener();
|
|
389
|
+
|
|
358
390
|
// Show post-process canvas if any post-process is enabled
|
|
359
391
|
if ( ( this.denoiser?.enabled || this.upscaler?.enabled ) && this.denoiserCanvas ) {
|
|
360
392
|
|
|
@@ -365,6 +397,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
365
397
|
// Chain: denoise first (if enabled), then upscale (if enabled)
|
|
366
398
|
const startUpscaler = () => {
|
|
367
399
|
|
|
400
|
+
this._pendingStartUpscaler = null;
|
|
401
|
+
|
|
368
402
|
if ( ! isStillComplete() ) return;
|
|
369
403
|
|
|
370
404
|
if ( this.upscaler?.enabled ) {
|
|
@@ -377,6 +411,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
377
411
|
|
|
378
412
|
if ( this.denoiser?.enabled ) {
|
|
379
413
|
|
|
414
|
+
this._pendingStartUpscaler = startUpscaler;
|
|
380
415
|
this.denoiser.addEventListener( 'end', startUpscaler, { once: true } );
|
|
381
416
|
this.denoiser.start();
|
|
382
417
|
|
|
@@ -401,6 +436,9 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
401
436
|
*/
|
|
402
437
|
abort( mainCanvas ) {
|
|
403
438
|
|
|
439
|
+
// Remove stale completion-chain listener before aborting
|
|
440
|
+
this._cleanupCompletionListener();
|
|
441
|
+
|
|
404
442
|
if ( mainCanvas ) mainCanvas.style.opacity = '1';
|
|
405
443
|
|
|
406
444
|
if ( this.upscaler ) this.upscaler.abort();
|
|
@@ -416,8 +454,13 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
416
454
|
|
|
417
455
|
dispose() {
|
|
418
456
|
|
|
457
|
+
// Remove pending completion-chain listener
|
|
458
|
+
this._cleanupCompletionListener();
|
|
459
|
+
|
|
419
460
|
if ( this.denoiser ) {
|
|
420
461
|
|
|
462
|
+
if ( this._denoiserStartHandler ) this.denoiser.removeEventListener( 'start', this._denoiserStartHandler );
|
|
463
|
+
if ( this._denoiserEndHandler ) this.denoiser.removeEventListener( 'end', this._denoiserEndHandler );
|
|
421
464
|
this.denoiser.dispose();
|
|
422
465
|
this.denoiser = null;
|
|
423
466
|
|
|
@@ -425,11 +468,22 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
425
468
|
|
|
426
469
|
if ( this.upscaler ) {
|
|
427
470
|
|
|
471
|
+
if ( this._upscalerResChangedHandler ) this.upscaler.removeEventListener( 'resolution_changed', this._upscalerResChangedHandler );
|
|
472
|
+
if ( this._upscalerStartHandler ) this.upscaler.removeEventListener( 'start', this._upscalerStartHandler );
|
|
473
|
+
if ( this._upscalerProgressHandler ) this.upscaler.removeEventListener( 'progress', this._upscalerProgressHandler );
|
|
474
|
+
if ( this._upscalerEndHandler ) this.upscaler.removeEventListener( 'end', this._upscalerEndHandler );
|
|
428
475
|
this.upscaler.dispose();
|
|
429
476
|
this.upscaler = null;
|
|
430
477
|
|
|
431
478
|
}
|
|
432
479
|
|
|
480
|
+
this._denoiserStartHandler = null;
|
|
481
|
+
this._denoiserEndHandler = null;
|
|
482
|
+
this._upscalerResChangedHandler = null;
|
|
483
|
+
this._upscalerStartHandler = null;
|
|
484
|
+
this._upscalerProgressHandler = null;
|
|
485
|
+
this._upscalerEndHandler = null;
|
|
486
|
+
|
|
433
487
|
if ( this.denoiserCanvas?.parentNode ) {
|
|
434
488
|
|
|
435
489
|
this.denoiserCanvas.parentNode.removeChild( this.denoiserCanvas );
|