rayzee 6.5.0 → 7.1.0

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.
Files changed (51) hide show
  1. package/README.md +24 -5
  2. package/dist/rayzee.es.js +7624 -7063
  3. package/dist/rayzee.es.js.map +1 -1
  4. package/dist/rayzee.umd.js +157 -236
  5. package/dist/rayzee.umd.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/EngineDefaults.js +26 -9
  8. package/src/PathTracerApp.js +118 -26
  9. package/src/Pipeline/PipelineContext.js +1 -2
  10. package/src/Pipeline/RenderPipeline.js +1 -1
  11. package/src/Pipeline/RenderStage.js +1 -1
  12. package/src/Processor/CameraOptimizer.js +0 -5
  13. package/src/Processor/GeometryExtractor.js +6 -0
  14. package/src/Processor/KernelManager.js +277 -0
  15. package/src/Processor/PackedRayBuffer.js +291 -0
  16. package/src/Processor/QueueManager.js +173 -0
  17. package/src/Processor/SceneProcessor.js +1 -0
  18. package/src/Processor/ShaderBuilder.js +11 -317
  19. package/src/Processor/StorageTexturePool.js +29 -15
  20. package/src/Processor/VRAMTracker.js +169 -0
  21. package/src/Processor/utils.js +11 -110
  22. package/src/RenderSettings.js +0 -3
  23. package/src/Stages/ASVGF.js +151 -78
  24. package/src/Stages/BilateralFilter.js +34 -10
  25. package/src/Stages/EdgeFilter.js +2 -3
  26. package/src/Stages/MotionVector.js +16 -9
  27. package/src/Stages/NormalDepth.js +17 -5
  28. package/src/Stages/PathTracer.js +671 -1456
  29. package/src/Stages/PathTracerStage.js +1451 -0
  30. package/src/Stages/SSRC.js +32 -15
  31. package/src/Stages/Variance.js +35 -12
  32. package/src/TSL/CompactKernel.js +110 -0
  33. package/src/TSL/DebugKernel.js +98 -0
  34. package/src/TSL/Environment.js +13 -11
  35. package/src/TSL/ExtendKernel.js +75 -0
  36. package/src/TSL/FinalWriteKernel.js +121 -0
  37. package/src/TSL/GenerateKernel.js +111 -0
  38. package/src/TSL/LightsSampling.js +2 -2
  39. package/src/TSL/PathTracerCore.js +43 -1039
  40. package/src/TSL/ShadeKernel.js +876 -0
  41. package/src/TSL/patches.js +81 -4
  42. package/src/index.js +3 -0
  43. package/src/managers/CameraManager.js +1 -1
  44. package/src/managers/DenoisingManager.js +40 -75
  45. package/src/managers/EnvironmentManager.js +30 -39
  46. package/src/managers/OverlayManager.js +7 -22
  47. package/src/managers/UniformManager.js +0 -3
  48. package/src/managers/helpers/TileHelper.js +2 -2
  49. package/src/Stages/AdaptiveSampling.js +0 -483
  50. package/src/TSL/PathTracer.js +0 -384
  51. package/src/managers/TileManager.js +0 -298
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Rayzee patches for Three.js / TSL.
3
3
  *
4
- * Side-effect on import: installs `WebGPUBackend.createNodeBuilder` override
5
- * (restores r183 function-scoped `var` emission for compute shaders — prevents
6
- * a register-allocation regression in the path tracer's hot loop).
4
+ * Side-effect on import: installs two `WebGPUBackend.prototype` overrides —
5
+ * `createNodeBuilder` (restores r183 function-scoped `var` emission for compute
6
+ * shaders, preventing a register-allocation regression in the path tracer's hot
7
+ * loop) and `initTimestampQuery` (enlarges the stats-gl timestamp query pool so
8
+ * the wavefront tracer's high per-frame compute-pass count doesn't overflow it).
7
9
  *
8
10
  * Export: `struct()` — drop-in replacement for TSL's `struct()` returning
9
11
  * a proxy factory that supports GLSL-style dot-notation field access.
@@ -64,7 +66,82 @@ WebGPUBackend.prototype.createNodeBuilder = function ( object, renderer ) {
64
66
  };
65
67
 
66
68
  // ---------------------------------------------------------------------------
67
- // 2. TSL struct proxy enables GLSL-style dot-notation field access
69
+ // 2. Larger timestamp query pool (stats-gl GPU/compute timing)
70
+ // ---------------------------------------------------------------------------
71
+ // Three.js lazily creates each timestamp query pool with a hardcoded 2048
72
+ // queries (= 1024 passes) — `WebGPUBackend.initTimestampQuery` / the upstream
73
+ // `// TODO: Variable maxQueries?`. The wavefront tracer issues hundreds of
74
+ // compute passes per frame (peak right after a maxBounces change: the survivor
75
+ // curve is invalid, so the bounce loop runs the full `loopBound` at full
76
+ // dispatch with no early-exit — ~560 passes / 1124 queries at production
77
+ // settings). stats-gl resolves once per frame, but the resolve is async and
78
+ // `mapAsync` lags several frames under that GPU load, so the counter isn't reset
79
+ // before it overflows → "Maximum number of queries exceeded" + dropped timings.
80
+ //
81
+ // Two parts:
82
+ // a) Grow the pool to 4096 queries on first use. 4096 is the WebGPU hard cap on
83
+ // a query set's count ("Query count exceeds the maximum query count (4096)")
84
+ // — 2× the upstream default, the most a single pool can hold (~2048 passes).
85
+ // That alone fully covers the interactive case (~200 passes/frame); anything
86
+ // larger fails CreateQuerySet validation.
87
+ // b) When even 4096 isn't enough (production spike), degrade gracefully: skip
88
+ // tracking the overflow passes silently instead of `warnOnce` + an invalid
89
+ // descriptor. The reported compute ms briefly undercounts during the spike;
90
+ // rendering is never affected (timestamps don't gate compute correctness).
91
+ const TIMESTAMP_POOL_MAX_QUERIES = 4096;
92
+
93
+ // Drop-in for the pool's allocateQueriesForContext minus the warnOnce on overflow
94
+ // — returns null silently when full so the pass is cleanly skipped (see below).
95
+ function _allocateQueriesSilently( uid ) {
96
+
97
+ if ( ! this.trackTimestamp || this.isDisposed ) return null;
98
+ if ( this.currentQueryIndex + 2 > this.maxQueries ) return null; // full: skip, no warn
99
+ const baseOffset = this.currentQueryIndex;
100
+ this.currentQueryIndex += 2;
101
+ this.queryOffsets.set( uid, baseOffset );
102
+ return baseOffset;
103
+
104
+ }
105
+
106
+ const _origInitTimestampQuery = WebGPUBackend.prototype.initTimestampQuery;
107
+
108
+ WebGPUBackend.prototype.initTimestampQuery = function ( type, uid, descriptor ) {
109
+
110
+ const poolWasMissing = this.trackTimestamp && ! this.timestampQueryPool[ type ];
111
+
112
+ _origInitTimestampQuery.call( this, type, uid, descriptor );
113
+
114
+ // (a) First use: replace the fresh 2048 pool with a 4096 one of the same class,
115
+ // migrating the single allocation just made (offset 0) and re-pointing this
116
+ // pass's descriptor. Safe — first pass of the first tracked frame, nothing in
117
+ // flight. Swap in the silent allocator so future overflows don't warn.
118
+ if ( poolWasMissing ) {
119
+
120
+ const pool = this.timestampQueryPool[ type ];
121
+ if ( pool && pool.maxQueries < TIMESTAMP_POOL_MAX_QUERIES && descriptor.timestampWrites ) {
122
+
123
+ const Pool = pool.constructor;
124
+ const bigPool = new Pool( this.device, type, TIMESTAMP_POOL_MAX_QUERIES );
125
+ bigPool.allocateQueriesForContext = _allocateQueriesSilently;
126
+ bigPool.allocateQueriesForContext( uid ); // re-take offset 0 for this pass
127
+ this.timestampQueryPool[ type ] = bigPool;
128
+ descriptor.timestampWrites.querySet = bigPool.querySet; // offsets 0/1 unchanged
129
+ pool.dispose(); // nothing in flight — first pass of the first tracked frame
130
+
131
+ }
132
+
133
+ }
134
+
135
+ // (b) On overflow the (silent) allocator returns null, but upstream still wrote
136
+ // a descriptor with a null begin index — which both collides on slot 1 and is
137
+ // invalid. Drop it so the pass is cleanly untimed.
138
+ const tw = descriptor.timestampWrites;
139
+ if ( tw && tw.beginningOfPassWriteIndex == null ) descriptor.timestampWrites = undefined;
140
+
141
+ };
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // 3. TSL struct proxy — enables GLSL-style dot-notation field access
68
145
  // ---------------------------------------------------------------------------
69
146
  // TSL structs require `.get('fieldName')` for member access, but GLSL-style
70
147
  // dot notation (`.fieldName`) is more natural and matches ported code.
package/src/index.js CHANGED
@@ -44,6 +44,9 @@ export { IESManager } from './managers/IESManager.js';
44
44
  export { DenoisingManager } from './managers/DenoisingManager.js';
45
45
  export { OverlayManager } from './managers/OverlayManager.js';
46
46
 
47
+ // VRAM accounting
48
+ export { VRAMTracker, bufferBytes, textureBytes } from './Processor/VRAMTracker.js';
49
+
47
50
  // Pipeline infrastructure (for advanced consumers building custom stages)
48
51
  export { RenderPipeline } from './Pipeline/RenderPipeline.js';
49
52
  export { RenderStage, StageExecutionMode } from './Pipeline/RenderStage.js';
@@ -261,7 +261,7 @@ export class CameraManager extends EventDispatcher {
261
261
  * @param {Object} params.assetLoader
262
262
  * @param {import('three').Mesh} params.floorPlane
263
263
  * @param {number} params.currentFocusDistance
264
- * @param {import('./PathTracer.js').PathTracer} params.pathTracer
264
+ * @param {import('../Stages/PathTracer.js').PathTracer} params.pathTracer
265
265
  * @param {Function} params.setFocusDistance - Callback to update uniform + settings
266
266
  * @param {Function} params.softReset - Callback for soft accumulation reset
267
267
  * @param {Function} params.hardReset - Callback for hard accumulation reset
@@ -41,7 +41,7 @@ export class DenoisingManager extends EventDispatcher {
41
41
  this.pipeline = pipeline;
42
42
 
43
43
  // Stage references — only used internally for orchestration
44
- this._stages = stages; // { pathTracer, asvgf, variance, bilateralFilter, adaptiveSampling, edgeFilter, ssrc, autoExposure, compositor }
44
+ this._stages = stages; // { pathTracer, asvgf, variance, bilateralFilter, edgeFilter, ssrc, autoExposure, compositor }
45
45
 
46
46
  this._getExposure = getExposure;
47
47
  this._getSaturation = getSaturation;
@@ -231,7 +231,7 @@ export class DenoisingManager extends EventDispatcher {
231
231
 
232
232
  // Disable all real-time denoisers first
233
233
  if ( s.asvgf ) s.asvgf.enabled = false;
234
- if ( s.variance && ! this._isAdaptiveSamplingActive() ) s.variance.enabled = false;
234
+ if ( s.variance ) s.variance.enabled = false;
235
235
  if ( s.bilateralFilter ) s.bilateralFilter.enabled = false;
236
236
  if ( s.edgeFilter ) s.edgeFilter.setFilteringEnabled( false );
237
237
  if ( s.ssrc ) s.ssrc.enabled = false;
@@ -258,6 +258,8 @@ export class DenoisingManager extends EventDispatcher {
258
258
 
259
259
  }
260
260
 
261
+ this._syncGBufferStages();
262
+
261
263
  }
262
264
 
263
265
  /**
@@ -282,6 +284,8 @@ export class DenoisingManager extends EventDispatcher {
282
284
  // Coordinate with EdgeAware filtering
283
285
  if ( s.edgeFilter ) s.edgeFilter.setFilteringEnabled( ! enabled );
284
286
 
287
+ this._syncGBufferStages();
288
+
285
289
  }
286
290
 
287
291
  /**
@@ -315,35 +319,49 @@ export class DenoisingManager extends EventDispatcher {
315
319
  }
316
320
 
317
321
  /**
318
- * Enables/disables adaptive sampling with proper stage and context cleanup.
319
- * @param {boolean} enabled
322
+ * Gate the G-buffer stages (NormalDepth, MotionVector) on demand: they only
323
+ * need to run when a real-time denoiser consumes their output. Idling them
324
+ * otherwise skips MotionVector's per-frame compute + copies during preview
325
+ * navigation and frees their textures. Call after any consumer toggle.
326
+ *
327
+ * MotionVector requires NormalDepth (reads pathtracer:normalDepth) and its
328
+ * consumers (ASVGF, SSRC) are a subset of NormalDepth's, so NormalDepth is
329
+ * always enabled whenever MotionVector is. Adaptive sampling / Variance / OIDN
330
+ * do NOT read these signals, so they don't keep the G-buffer alive.
320
331
  */
321
- setAdaptiveSamplingEnabled( enabled ) {
332
+ _syncGBufferStages() {
322
333
 
323
334
  const s = this._stages;
335
+ const nd = s.normalDepth;
336
+ const mv = s.motionVector;
324
337
 
325
- if ( s.adaptiveSampling ) {
326
-
327
- s.adaptiveSampling.enabled = enabled;
328
- s.adaptiveSampling.setHeatmapEnabled( false );
338
+ // motionVector:* consumed by ASVGF + SSRC
339
+ const motionNeeded = !! ( s.asvgf?.enabled || s.ssrc?.enabled );
340
+ // pathtracer:normalDepth consumed by ASVGF, SSRC, EdgeFilter, BilateralFilter
341
+ const normalNeeded = motionNeeded || !! ( s.edgeFilter?.enabled || s.bilateralFilter?.enabled );
329
342
 
330
- }
343
+ if ( nd ) {
331
344
 
332
- // Variance stage is shared by both ASVGF and adaptive sampling
333
- if ( enabled ) {
345
+ // On disabled→enabled, re-arm dirty/history so the first frame recomputes
346
+ // (not the stale static fast-path) and seeds prev = current.
347
+ if ( normalNeeded && ! nd.enabled ) nd.reset();
348
+ nd.enabled = normalNeeded;
334
349
 
335
- if ( s.variance ) s.variance.enabled = true;
350
+ }
336
351
 
337
- } else if ( ! s.asvgf?.enabled ) {
352
+ if ( mv ) {
338
353
 
339
- if ( s.variance ) s.variance.enabled = false;
354
+ // On re-enable, force a camera-history reseed (matricesInitialized survives
355
+ // normal resets) so the first frame reports zero motion, not a spike.
356
+ if ( motionNeeded && ! mv.enabled ) {
340
357
 
341
- }
358
+ mv.matricesInitialized = false;
359
+ mv.isFirstFrame = true;
360
+ mv.frameCount = 0;
342
361
 
343
- // Clean up stale variance context when disabling
344
- if ( ! enabled && this.pipeline?.context && ! s.asvgf?.enabled ) {
362
+ }
345
363
 
346
- this.pipeline.context.removeTexture( 'variance:output' );
364
+ mv.enabled = motionNeeded;
347
365
 
348
366
  }
349
367
 
@@ -546,6 +564,8 @@ export class DenoisingManager extends EventDispatcher {
546
564
 
547
565
  }
548
566
 
567
+ this._syncGBufferStages();
568
+
549
569
  }
550
570
 
551
571
  /** Updates SSRC stage parameters. */
@@ -569,29 +589,6 @@ export class DenoisingManager extends EventDispatcher {
569
589
 
570
590
  }
571
591
 
572
- /**
573
- * Updates adaptive sampling parameters (with settings bridge).
574
- * @param {Object} params
575
- */
576
- setAdaptiveSamplingParams( params ) {
577
-
578
- if ( params.min !== undefined ) this._stages.pathTracer?.setUniform( 'adaptiveSamplingMin', params.min );
579
- if ( params.adaptiveSamplingMax !== undefined ) this._settings?.set( 'adaptiveSamplingMax', params.adaptiveSamplingMax );
580
- this._stages.adaptiveSampling?.setAdaptiveSamplingParameters( params );
581
-
582
- }
583
-
584
- /**
585
- * Toggle the AdaptiveSampling heatmap compute pass. When enabled, the
586
- * stage writes the heatmap to its public `heatmapTarget` RenderTarget —
587
- * the host is responsible for rendering it.
588
- */
589
- toggleAdaptiveSamplingHelper( enabled ) {
590
-
591
- this._stages.adaptiveSampling?.setHeatmapEnabled( enabled );
592
-
593
- }
594
-
595
592
  // ── OIDN ─────────────────────────────────────────────────────
596
593
 
597
594
  /** Enables or disables Intel OIDN denoiser. */
@@ -608,27 +605,13 @@ export class DenoisingManager extends EventDispatcher {
608
605
 
609
606
  }
610
607
 
611
- /** Enables or disables the OIDN tile helper overlay. */
612
- setOIDNTileHelper( enabled ) {
613
-
614
- this._setTileHelper( enabled );
615
-
616
- }
617
-
618
- /** Enables or disables the tile helper overlay. */
608
+ /** Enables or disables the denoise/upscale progress overlay. */
619
609
  setTileHelperEnabled( enabled ) {
620
610
 
621
611
  this._setTileHelper( enabled );
622
612
 
623
613
  }
624
614
 
625
- /** Enables or disables tile highlight. */
626
- setTileHighlightEnabled( enabled ) {
627
-
628
- this._setTileHelper( enabled );
629
-
630
- }
631
-
632
615
  // ── AI Upscaler ──────────────────────────────────────────────
633
616
 
634
617
  /** Enables or disables the AI upscaler. */
@@ -665,17 +648,6 @@ export class DenoisingManager extends EventDispatcher {
665
648
 
666
649
  }
667
650
 
668
- /**
669
- * Enables or disables adaptive sampling (convenience wrapper with settings bridge).
670
- * @param {boolean} enabled
671
- */
672
- setAdaptiveSampling( enabled ) {
673
-
674
- this._settings?.set( 'useAdaptiveSampling', enabled );
675
- this.setAdaptiveSamplingEnabled( enabled );
676
-
677
- }
678
-
679
651
  /**
680
652
  * Switches strategy with automatic reset (convenience wrapper).
681
653
  * @param {'none'|'asvgf'|'ssrc'|'edgeaware'} strategy
@@ -702,7 +674,6 @@ export class DenoisingManager extends EventDispatcher {
702
674
 
703
675
  }
704
676
 
705
-
706
677
  _getEffectiveExposure() {
707
678
 
708
679
  return this._stages.autoExposure?.enabled
@@ -717,12 +688,6 @@ export class DenoisingManager extends EventDispatcher {
717
688
 
718
689
  }
719
690
 
720
- _isAdaptiveSamplingActive() {
721
-
722
- return this._stages.adaptiveSampling?.enabled ?? false;
723
-
724
- }
725
-
726
691
  _clearDenoiserTextures() {
727
692
 
728
693
  const ctx = this.pipeline?.context;
@@ -7,10 +7,8 @@
7
7
  * is mutated to preserve TSL shader graph references after compilation.
8
8
  */
9
9
 
10
- import { StorageInstancedBufferAttribute } from 'three/webgpu';
11
- import { storage } from 'three/tsl';
12
10
  import {
13
- RGBAFormat, FloatType, Vector2, Vector3, Color, Matrix4, DataTexture,
11
+ RGBAFormat, RedFormat, FloatType, Vector2, Vector3, Color, Matrix4, DataTexture,
14
12
  } from 'three';
15
13
  import { EquirectHDRInfo } from '../Processor/EquirectHDRInfo.js';
16
14
  import { ProceduralSky } from '../Processor/ProceduralSky.js';
@@ -43,12 +41,10 @@ export class EnvironmentManager {
43
41
  this.environmentTexture = this._envPlaceholder;
44
42
  this.envTexSize = new Vector2();
45
43
 
46
- // CDF storage buffer (marginal + conditional packed into one buffer).
47
- // Layout: [ marginal (envResolution.y floats) | conditional (envResolution.x * envResolution.y floats) ]
48
- // Conditional offset is the marginal length, which equals envResolution.y at runtime.
49
- this.envCDFStorageAttr = null;
50
- this.envCDFStorageNode = null;
51
- this._initCDFStorageBuffers();
44
+ // Importance-sampling CDF as an R32F texture (frees a Shade storage-buffer binding the limit is 10).
45
+ // Layout: (envResolution.x + 1) × envResolution.y; conditional in columns [0,W), marginal in column W.
46
+ this.envCDFTexture = null;
47
+ this._initCDFTexture();
52
48
 
53
49
  // Environment rotation
54
50
  this.environmentRotationMatrix = new Matrix4();
@@ -183,46 +179,42 @@ export class EnvironmentManager {
183
179
  * Initialize the packed CDF storage buffer with placeholder data.
184
180
  * Must be called before shader compilation so the node exists in the graph.
185
181
  *
186
- * Layout: [ marginal (size = envResolution.y) | conditional (size = envResolution.x * envResolution.y) ]
187
- * Placeholder shape is a 1x2 env map: marginal=[0,1], conditional=[0,0,1,1].
182
+ * 1×1 R32F placeholder until a real env CDF is built (env IS is off meanwhile).
188
183
  * @private
189
184
  */
190
- _initCDFStorageBuffers() {
185
+ _initCDFTexture() {
191
186
 
192
- const placeholder = new Float32Array( [ 0, 1, 0, 0, 1, 1 ] );
193
- this.envCDFStorageAttr = new StorageInstancedBufferAttribute( placeholder, 1 );
194
- this.envCDFStorageNode = storage( this.envCDFStorageAttr, 'float', placeholder.length ).toReadOnly();
187
+ this.envCDFTexture = new DataTexture( new Float32Array( [ 0 ] ), 1, 1, RedFormat, FloatType );
188
+ this.envCDFTexture.needsUpdate = true;
195
189
 
196
190
  }
197
191
 
198
192
  /**
199
- * Update the packed CDF storage buffer from equirectHdrInfo.
200
- * Concatenates marginal + conditional into one buffer.
193
+ * Rebuild the CDF texture from equirectHdrInfo. Packs the 2D conditional + 1D marginal into one
194
+ * R32F texture of (W+1)×H: conditional[cy*W+cx] at texel (cx, cy); marginal[cy] at texel (W, cy).
201
195
  * @private
202
196
  */
203
- _updateCDFStorageBuffers() {
197
+ _updateCDFTexture() {
204
198
 
205
199
  const marginal = this.equirectHdrInfo.marginalData;
206
200
  const conditional = this.equirectHdrInfo.conditionalData;
207
201
  if ( ! marginal || ! conditional ) return;
208
202
 
209
- const combined = new Float32Array( marginal.length + conditional.length );
210
- combined.set( marginal, 0 );
211
- combined.set( conditional, marginal.length );
203
+ const W = this.equirectHdrInfo.width;
204
+ const H = this.equirectHdrInfo.height;
205
+ const texW = W + 1;
206
+ const data = new Float32Array( texW * H );
207
+ for ( let cy = 0; cy < H; cy ++ ) {
212
208
 
213
- this.envCDFStorageAttr = new StorageInstancedBufferAttribute( combined, 1 );
214
- this.envCDFStorageNode.value = this.envCDFStorageAttr;
215
- this.envCDFStorageNode.bufferCount = combined.length;
209
+ const dstRow = cy * texW;
210
+ data.set( conditional.subarray( cy * W, cy * W + W ), dstRow ); // conditional → columns [0,W)
211
+ data[ dstRow + W ] = marginal[ cy ]; // marginal → column W
216
212
 
217
- }
218
-
219
- /**
220
- * Get the packed CDF storage node for shader graph.
221
- * @returns {{ cdfNode: StorageNode }}
222
- */
223
- getCDFStorageNodes() {
213
+ }
224
214
 
225
- return { cdfNode: this.envCDFStorageNode };
215
+ this.envCDFTexture?.dispose?.();
216
+ this.envCDFTexture = new DataTexture( data, texW, H, RedFormat, FloatType );
217
+ this.envCDFTexture.needsUpdate = true;
226
218
 
227
219
  }
228
220
 
@@ -278,7 +270,7 @@ export class EnvironmentManager {
278
270
 
279
271
  if ( ! this.scene.environment ) {
280
272
 
281
- this._updateCDFStorageBuffers();
273
+ this._updateCDFTexture();
282
274
  this.uniforms.set( 'envTotalSum', 0.0 );
283
275
  this.uniforms.set( 'envCompensationDelta', 0.0 );
284
276
  this.uniforms.set( 'useEnvMapIS', 0 );
@@ -293,7 +285,7 @@ export class EnvironmentManager {
293
285
 
294
286
  if ( ! textureForCDF.image ) {
295
287
 
296
- this._updateCDFStorageBuffers();
288
+ this._updateCDFTexture();
297
289
  this.uniforms.set( 'envTotalSum', 0.0 );
298
290
  this.uniforms.set( 'envCompensationDelta', 0.0 );
299
291
  this.uniforms.set( 'useEnvMapIS', 0 );
@@ -313,7 +305,7 @@ export class EnvironmentManager {
313
305
 
314
306
  this.cdfBuildTime = performance.now() - startTime;
315
307
 
316
- this._updateCDFStorageBuffers();
308
+ this._updateCDFTexture();
317
309
  this.uniforms.set( 'envTotalSum', this.equirectHdrInfo.totalSum );
318
310
  this.uniforms.set( 'envCompensationDelta', this.equirectHdrInfo.compensationDelta );
319
311
  this.uniforms.set( 'useEnvMapIS', 1 );
@@ -377,7 +369,7 @@ export class EnvironmentManager {
377
369
 
378
370
  } else {
379
371
 
380
- this._updateCDFStorageBuffers();
372
+ this._updateCDFTexture();
381
373
  this.uniforms.set( 'envTotalSum', 0.0 );
382
374
  this.uniforms.set( 'envCompensationDelta', 0.0 );
383
375
  this.uniforms.set( 'useEnvMapIS', 0 );
@@ -543,9 +535,8 @@ export class EnvironmentManager {
543
535
  this.proceduralSkyRenderer = null;
544
536
  this.simpleSkyRenderer = null;
545
537
 
546
- this.envCDFStorageAttr?.dispose?.();
547
- this.envCDFStorageAttr = null;
548
- this.envCDFStorageNode = null;
538
+ this.envCDFTexture?.dispose?.();
539
+ this.envCDFTexture = null;
549
540
 
550
541
  // Dispose the HDRI environment texture unless it's the shared placeholder
551
542
  // (the placeholder is handled separately just below).
@@ -1,6 +1,5 @@
1
1
  import { TileHelper } from './helpers/TileHelper.js';
2
2
  import { OutlineHelper } from './helpers/OutlineHelper.js';
3
- import { EngineEvents } from '../EngineEvents.js';
4
3
 
5
4
  /**
6
5
  * OverlayManager — Unified overlay system for visual helpers.
@@ -9,7 +8,7 @@ import { EngineEvents } from '../EngineEvents.js';
9
8
  * 1. **HelperScene** — A Three.js Scene rendered on top of the WebGPU backbuffer
10
9
  * (light gizmos, bounding boxes, outlines). Renders at display resolution.
11
10
  * 2. **HUDCanvas** — A 2D `<canvas>` element overlaid via CSS for screen-space
12
- * elements (tile progress, AF points, debug labels). Completely separate
11
+ * elements (AF points, debug labels). Completely separate
13
12
  * from the WebGPU canvas, so it is never captured in saved images.
14
13
  *
15
14
  * Helpers are registered by name and implement a simple interface:
@@ -17,8 +16,8 @@ import { EngineEvents } from '../EngineEvents.js';
17
16
  *
18
17
  * @example
19
18
  * const overlay = new OverlayManager( renderer, camera );
20
- * overlay.register( 'tiles', new TileHelper() );
21
- * overlay.show( 'tiles' );
19
+ * overlay.register( 'outline', new OutlineHelper() );
20
+ * overlay.show( 'outline' );
22
21
  * // in animate():
23
22
  * overlay.render();
24
23
  */
@@ -84,7 +83,7 @@ export class OverlayManager {
84
83
  // ═══════════════════════════════════════════════════════════════
85
84
 
86
85
  /**
87
- * Creates and wires the default overlay helpers (tile progress, outline).
86
+ * Creates and wires the default overlay helpers (denoise/upscale progress, outline).
88
87
  * Call once during app init after pipeline and managers are ready.
89
88
  *
90
89
  * @param {Object} config
@@ -92,7 +91,7 @@ export class OverlayManager {
92
91
  * @param {import('three').Scene} config.meshScene
93
92
  * @param {import('../Pipeline/RenderPipeline.js').RenderPipeline} config.pipeline
94
93
  * @param {import('./DenoisingManager.js').DenoisingManager} config.denoisingManager
95
- * @param {import('three').EventDispatcher} config.app - App instance for resize/render-complete events
94
+ * @param {import('three').EventDispatcher} config.app - App instance for resize events
96
95
  * @param {number} config.renderWidth
97
96
  * @param {number} config.renderHeight
98
97
  */
@@ -100,7 +99,7 @@ export class OverlayManager {
100
99
 
101
100
  this.setHelperScene( helperScene );
102
101
 
103
- // ── Tile helper (shared across path tracer, OIDN, upscaler) ──
102
+ // ── Tile helper shows OIDN denoise + AI upscale progress ──
104
103
  const tileHelper = new TileHelper();
105
104
  this.register( 'tiles', tileHelper );
106
105
 
@@ -112,22 +111,8 @@ export class OverlayManager {
112
111
 
113
112
  } );
114
113
 
115
- // Path tracer tile events
116
- pipeline.eventBus.on( 'tile:changed', ( e ) => {
117
-
118
- if ( e.renderMode === 1 && e.tileBounds ) {
119
-
120
- tileHelper.setActiveTile( e.tileBounds );
121
- tileHelper.show();
122
-
123
- }
124
-
125
- } );
126
-
127
114
  pipeline.eventBus.on( 'pipeline:reset', () => tileHelper.hide() );
128
- app.addEventListener( EngineEvents.RENDER_COMPLETE, () => tileHelper.hide() );
129
115
 
130
- // OIDN/upscaler tile events
131
116
  this._wireDenoiserTileEvents( tileHelper, denoisingManager );
132
117
 
133
118
  // ── Outline helper ──
@@ -137,7 +122,7 @@ export class OverlayManager {
137
122
  }
138
123
 
139
124
  /**
140
- * Wires denoiser/upscaler tile progress events to the tile helper.
125
+ * Wires denoiser/upscaler tile-progress events to the tile helper.
141
126
  * These fire while the animation loop is stopped, so we trigger manual HUD redraws.
142
127
  */
143
128
  _wireDenoiserTileEvents( tileHelper, denoisingManager ) {
@@ -231,9 +231,6 @@ export class UniformManager {
231
231
  this._uniforms.set( 'samplingTechnique', samplingTechniqueUniform );
232
232
  samplingTechniqueUniform.value = DEFAULT_STATE.samplingTechnique;
233
233
 
234
- ub( 'useAdaptiveSampling', DEFAULT_STATE.adaptiveSampling );
235
- u( 'adaptiveSamplingMin', DEFAULT_STATE.adaptiveSamplingMin ?? 1, 'int' );
236
- u( 'adaptiveSamplingMax', DEFAULT_STATE.adaptiveSamplingMax, 'int' );
237
234
  u( 'fireflyThreshold', DEFAULT_STATE.fireflyThreshold, 'float' );
238
235
 
239
236
  // Emissive
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * TileHelper — 2D canvas overlay showing the active tile border.
3
3
  *
4
- * Universal: works for path tracer tiled rendering, OIDN denoiser,
5
- * and AI upscaler. Callers provide pixel-space tile bounds via
4
+ * Shows progress of the OIDN denoiser and AI upscaler (both process the
5
+ * image in tiles). Callers provide pixel-space tile bounds via
6
6
  * `setActiveTile()` and control visibility via `show()` / `hide()`.
7
7
  *
8
8
  * Layer: 'hud' (rendered by OverlayManager's 2D canvas pass).