rayzee 11.4.0 → 11.6.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 (28) hide show
  1. package/README.md +1 -0
  2. package/dist/{ArchiveImporter-Cgyx5AF7.js → ArchiveImporter-iofnC8zB.js} +2 -2
  3. package/dist/{BidirectionalIntegrator-BKF-cwH2.js → BidirectionalIntegrator-CqAilt2I.js} +289 -289
  4. package/dist/{EngineEvents-Ci8dVWCP.js → EngineEvents-CLckhYbL.js} +1 -0
  5. package/dist/{NeuralRetouch-C3Q2XqbF.js → NeuralRetouch-DFI4pOjO.js} +2 -2
  6. package/dist/{NeuralSuperRes-DV5TtURP.js → NeuralSuperRes-ChCiiilS.js} +3 -3
  7. package/dist/{PathTracer-D3MvEaZK.js → PathTracer-BX4sJLx7.js} +3194 -3162
  8. package/dist/{PhysicalSky-CY4jiz6a.js → PhysicalSky-Biv39eTA.js} +1 -1
  9. package/dist/{RayzeeRenderer-D23m6NqG.js → RayzeeRenderer-D8XRKU3A.js} +406 -437
  10. package/dist/{ToneMapGPU-CTDBvmmS.js → ToneMapGPU-sAs8wdgI.js} +14 -1
  11. package/dist/addons/archives.es.js +1 -1
  12. package/dist/addons/bidirectional.es.js +1 -1
  13. package/dist/addons/physical-sky.es.js +1 -1
  14. package/dist/rayzee-core.es.js +5 -5
  15. package/dist/rayzee.es.js +544 -460
  16. package/dist/rayzee.umd.js +63 -55
  17. package/dist/{sceneCachePolicy-AKOEu2lf.js → sceneCachePolicy-W5PLbzuR.js} +2 -2
  18. package/package.json +2 -2
  19. package/src/EngineEvents.js +2 -0
  20. package/src/Passes/AIUpscaler.js +15 -12
  21. package/src/Passes/OIDNDenoiser.js +106 -98
  22. package/src/PathTracerApp.js +2 -2
  23. package/src/Processor/TextureReadback.js +18 -1
  24. package/src/Processor/ToneMapGPU.js +22 -3
  25. package/src/RayzeeRenderer.js +4 -4
  26. package/src/Stages/LocalExposure.js +25 -2
  27. package/src/Stages/PathTracer.js +2 -10
  28. package/src/managers/DenoisingManager.js +97 -19
@@ -495,7 +495,8 @@ export class LocalExposure extends RenderStage {
495
495
 
496
496
  /**
497
497
  * The gain for a tone-mapping pass outside three.js (`PackedToneMapper`'s `gain`), or null before the
498
- * first build. `cpu()` reads the grid back for the CPU tone map.
498
+ * first build. `cpu()` reads the grid back for the CPU tone map; `copyTo( device )` copies it to another
499
+ * GPU device (the neural models' own).
499
500
  */
500
501
  toneGain() {
501
502
 
@@ -508,10 +509,12 @@ export class LocalExposure extends RenderStage {
508
509
  this._paramsBuffer ??= backend.device.createBuffer( { label: 'local-exposure-params', size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST } );
509
510
  backend.device.queue.writeBuffer( this._paramsBuffer, 0, this._params );
510
511
  const params = this._params.slice();
512
+ const read = () => Promise.all( [ this.renderer.getArrayBufferAsync( this._grid.value ), this.renderer.getArrayBufferAsync( this._blur.value ) ] );
511
513
 
512
514
  return {
513
515
  key: 'local-exposure',
514
516
  wgsl: LOCAL_EXPOSURE_WGSL,
517
+ device: backend.device,
515
518
  entries: [
516
519
  { binding: 0, resource: { buffer: grid } },
517
520
  { binding: 1, resource: { buffer: blur } },
@@ -519,11 +522,31 @@ export class LocalExposure extends RenderStage {
519
522
  ],
520
523
  cpu: async () => {
521
524
 
522
- const [ g, b ] = await Promise.all( [ this.renderer.getArrayBufferAsync( this._grid.value ), this.renderer.getArrayBufferAsync( this._blur.value ) ] );
525
+ const [ g, b ] = await read();
523
526
  const gridData = new Float32Array( g ), blurData = new Float32Array( b );
524
527
  return ( u, v, lum, logExposure ) => localExposureGain( params, gridData, blurData, u, v, lum, logExposure );
525
528
 
526
529
  },
530
+ copyTo: async device => {
531
+
532
+ const [ g, b ] = await read();
533
+ const buffers = [[ g, GPUBufferUsage.STORAGE ], [ b, GPUBufferUsage.STORAGE ], [ params, GPUBufferUsage.UNIFORM ]].map( ( [ data, usage ] ) => {
534
+
535
+ const buffer = device.createBuffer( { label: 'local-exposure-copy', size: data.byteLength, usage: usage | GPUBufferUsage.COPY_DST } );
536
+ device.queue.writeBuffer( buffer, 0, data );
537
+ return buffer;
538
+
539
+ } );
540
+
541
+ return {
542
+ key: 'local-exposure',
543
+ wgsl: LOCAL_EXPOSURE_WGSL,
544
+ device,
545
+ entries: buffers.map( ( buffer, binding ) => ( { binding, resource: { buffer } } ) ),
546
+ destroy: () => buffers.forEach( buffer => buffer.destroy() ),
547
+ };
548
+
549
+ },
527
550
  };
528
551
 
529
552
  }
@@ -28,6 +28,7 @@ import {
28
28
  } from '../TSL/SortGlobalKernels.js';
29
29
  import { ENGINE_DEFAULTS } from '../EngineDefaults.js';
30
30
  import { MAX_STORAGE_TEXTURE_SIZE } from '../Processor/StorageTexturePool.js';
31
+ import { readPixels } from '../Processor/TextureReadback.js';
31
32
  import { ISSUE_CODES } from '../EngineIssues.js';
32
33
  import { createLogger, fmt } from '../utils/Logger.js';
33
34
  import { deviceMemoryGB } from '../Processor/HostMemory.js';
@@ -758,16 +759,7 @@ export class PathTracer extends PathTracerStage {
758
759
  const height = pool.renderHeight;
759
760
  const pixels = width * height;
760
761
 
761
- const readAttachment = async index => {
762
-
763
- const padded = await this.renderer.readRenderTargetPixelsAsync( pool.readTarget, 0, 0, width, height, index );
764
- const stride = Math.ceil( width * 16 / 256 ) * 64;
765
- if ( stride === width * 4 ) return new Float32Array( padded.buffer, padded.byteOffset, pixels * 4 ).slice();
766
- const tight = new Float32Array( pixels * 4 );
767
- for ( let y = 0; y < height; y ++ ) tight.set( padded.subarray( y * stride, y * stride + width * 4 ), y * width * 4 );
768
- return tight;
769
-
770
- };
762
+ const readAttachment = index => readPixels( this.renderer, pool.readTarget, width, height, index );
771
763
 
772
764
  const readBuffer = async ( attr, Type ) => attr ? new Type( await this.renderer.getArrayBufferAsync( attr, null, 0, pixels * 4 ) ) : null;
773
765
  const aux = this._auxGBufferEnabled && this._auxSamples > 0;
@@ -137,6 +137,11 @@ export class DenoisingManager extends EventDispatcher {
137
137
  this._retouch = null;
138
138
  this._superResModule = null;
139
139
  this._neuralPostBusy = false;
140
+ // The finished render's post passes: its guard, the run in flight, a queued rerun, and a cancel.
141
+ this._postIsStillComplete = null;
142
+ this._postRun = null;
143
+ this._postRedo = null;
144
+ this._postCancelled = false;
140
145
 
141
146
  // The tier the finished image uses. The loaded tier is not always this one: while the
142
147
  // image is still accumulating we run a cheaper model (see previewQuality).
@@ -192,6 +197,7 @@ export class DenoisingManager extends EventDispatcher {
192
197
  this._denoiserEndHandler = null;
193
198
  this._denoiserTileHandler = null;
194
199
  this._denoiserErrorHandler = null;
200
+ this._denoiserLoadHandler = null;
195
201
  this._upscalerResChangedHandler = null;
196
202
  this._upscalerStartHandler = null;
197
203
  this._upscalerProgressHandler = null;
@@ -372,6 +378,12 @@ export class DenoisingManager extends EventDispatcher {
372
378
  this.denoiser.addEventListener( 'tileProgress', this._denoiserTileHandler );
373
379
  this.denoiser.addEventListener( 'error', this._denoiserErrorHandler );
374
380
 
381
+ // 'error' too: a failed load never sends 'loaded'. The first load starts inside the constructor, before these listen.
382
+ this._denoiserLoadHandler = e =>
383
+ this.dispatchEvent( { type: EngineEvents.DENOISER_LOADING, loading: e.type === 'loading' } );
384
+ for ( const type of [ 'loading', 'loaded', 'error' ] ) this.denoiser.addEventListener( type, this._denoiserLoadHandler );
385
+ if ( this.denoiser.state?.isLoading ) this.dispatchEvent( { type: EngineEvents.DENOISER_LOADING, loading: true } );
386
+
375
387
  }
376
388
 
377
389
  /**
@@ -1148,6 +1160,7 @@ export class DenoisingManager extends EventDispatcher {
1148
1160
  /** Turns the neural-rendering (detail) pass on or off, and sets its appearance controls. */
1149
1161
  setNeuralRendering( enabled, settings = null ) {
1150
1162
 
1163
+ const changed = this.neuralRendering !== !! enabled;
1151
1164
  this.neuralRendering = !! enabled;
1152
1165
  if ( settings ) this.neuralRenderingSettings = { ...this.neuralRenderingSettings, ...settings };
1153
1166
  if ( ! this.neuralRendering ) {
@@ -1157,6 +1170,8 @@ export class DenoisingManager extends EventDispatcher {
1157
1170
 
1158
1171
  }
1159
1172
 
1173
+ if ( changed ) this._redoPostPasses();
1174
+
1160
1175
  }
1161
1176
 
1162
1177
  _releaseNeuralRetouch() {
@@ -1167,14 +1182,8 @@ export class DenoisingManager extends EventDispatcher {
1167
1182
  }
1168
1183
 
1169
1184
  /**
1170
- * The neural post-chain over the picture the closing denoise just produced: super resolution
1171
- * first, then the detail pass, either or both.
1172
- *
1173
- * Order is forced by the models. Super resolution hands back light that can be fed onward; the
1174
- * detail pass writes an `rgba8unorm` texture with no `COPY_SRC`, so nothing can read its result
1175
- * and it has to be last — and it presents in its own display space rather than the engine's tone
1176
- * curve. Which shape super resolution returns therefore depends on what follows it: packed
1177
- * halves when the detail pass is next, display bytes when it is the end of the chain.
1185
+ * The post-chain over the picture the closing denoise just produced: the detail pass at render size,
1186
+ * then the chosen upscaler (neural super resolution or the ONNX one), either or both.
1178
1187
  *
1179
1188
  * Deliberately fire-and-forget: the render is already finished and a failure must not break it.
1180
1189
  */
@@ -1182,6 +1191,7 @@ export class DenoisingManager extends EventDispatcher {
1182
1191
 
1183
1192
  const wantSR = this.upscaler?.enabled && this.upscalerBackend === 'neural';
1184
1193
  const wantNR = this.neuralRendering;
1194
+ const wantESRGAN = this.upscaler?.enabled && ! wantSR;
1185
1195
  if ( ! wantSR && ! wantNR ) return;
1186
1196
 
1187
1197
  // A second completion can land while the first pass is still in flight — the models are
@@ -1230,7 +1240,12 @@ export class DenoisingManager extends EventDispatcher {
1230
1240
 
1231
1241
  }
1232
1242
 
1233
- if ( ! wantSR && ! runNR ) return;
1243
+ if ( ! wantSR && ! runNR ) {
1244
+
1245
+ if ( wantESRGAN && ! this._postCancelled ) await this.upscaler.start();
1246
+ return;
1247
+
1248
+ }
1234
1249
 
1235
1250
  // Must be the effective value, not the settings one: AutoExposure overwrites
1236
1251
  // `renderer.toneMappingExposure` every frame and never touches settings, so reading
@@ -1291,6 +1306,15 @@ export class DenoisingManager extends EventDispatcher {
1291
1306
 
1292
1307
  }
1293
1308
 
1309
+ if ( this._postRedo || this._postCancelled ) return;
1310
+
1311
+ if ( wantESRGAN ) {
1312
+
1313
+ await this.upscaler.start( new ImageData( image.rgba8, image.width, image.height ) );
1314
+ return;
1315
+
1316
+ }
1317
+
1294
1318
  sr.presentRGBA8( this.upscalerCanvas, image.rgba8, image.width, image.height );
1295
1319
 
1296
1320
  if ( ! isStillComplete() ) {
@@ -1360,10 +1384,61 @@ export class DenoisingManager extends EventDispatcher {
1360
1384
 
1361
1385
  }
1362
1386
 
1387
+ /** The neural chain or the ONNX upscaler over the finished render, as the switches ask. */
1388
+ _runPostPasses( isStillComplete ) {
1389
+
1390
+ this._postIsStillComplete = isStillComplete;
1391
+ this._postCancelled = false;
1392
+
1393
+ // The neural chain owns the overlay canvas when it runs, and hands its result to the ONNX upscaler.
1394
+ if ( this.neuralRendering || ( this.upscaler?.enabled && this.upscalerBackend === 'neural' ) ) {
1395
+
1396
+ this._postRun = this._runNeuralPost( isStillComplete );
1397
+
1398
+ } else {
1399
+
1400
+ this._postRun = this.upscaler?.enabled ? this.upscaler.start() : null;
1401
+
1402
+ }
1403
+
1404
+ }
1405
+
1406
+ /** Runs the post passes again on a finished render whose switches changed, once the run in flight ends. */
1407
+ _redoPostPasses() {
1408
+
1409
+ const isStillComplete = this._postIsStillComplete;
1410
+ // No chain yet: the closing denoise is still running and the chain reads the switches when it starts.
1411
+ if ( ! isStillComplete?.() || this._postRedo ) return;
1412
+
1413
+ this.upscaler?.abort();
1414
+ this._postCancelled = false;
1415
+ this._postRedo = Promise.resolve( this._postRun ).catch( () => {} ).then( () => {
1416
+
1417
+ this._postRedo = null;
1418
+ if ( this._postCancelled || this._postIsStillComplete !== isStillComplete || ! isStillComplete() ) return;
1419
+ this._restoreRenderDisplay();
1420
+ this._runPostPasses( isStillComplete );
1421
+
1422
+ } );
1423
+
1424
+ }
1425
+
1426
+ /** Stops the post passes on the finished render, which stays on screen as rendered. The switches stay as they are. */
1427
+ cancelPostPasses() {
1428
+
1429
+ this._postCancelled = true;
1430
+ this.upscaler?.abort();
1431
+ this._restoreRenderDisplay();
1432
+ // A retouch in flight cannot be stopped, only discarded, and ends later.
1433
+ this.dispatchEvent( { type: EngineEvents.UPSCALING_END } );
1434
+
1435
+ }
1436
+
1363
1437
  onRenderComplete( { isStillComplete } ) {
1364
1438
 
1365
1439
  // Remove any stale completion-chain listener from a previous render cycle
1366
1440
  this._cleanupCompletionListener();
1441
+ this._postIsStillComplete = null;
1367
1442
 
1368
1443
  // What closes the render, if anything. A full pass at the chosen tier when that switch is
1369
1444
  // on; otherwise, if OIDN owns the live view, one last cheap refresh — the cadence's last
@@ -1383,16 +1458,7 @@ export class DenoisingManager extends EventDispatcher {
1383
1458
  // denoise. Ordered after `_denoiserEndHandler`, which publishes the picture.
1384
1459
  if ( closing ) this._onDisplayRefresh?.();
1385
1460
 
1386
- // One owner of the overlay canvas, so the neural chain and the ONNX upscaler are
1387
- // exclusive. The neural chain also runs on its own when only its detail pass is on.
1388
- if ( this.neuralRendering || ( this.upscaler?.enabled && this.upscalerBackend === 'neural' ) ) {
1389
-
1390
- this._runNeuralPost( isStillComplete );
1391
- return;
1392
-
1393
- }
1394
-
1395
- if ( this.upscaler?.enabled ) this.upscaler.start();
1461
+ this._runPostPasses( isStillComplete );
1396
1462
 
1397
1463
  };
1398
1464
 
@@ -1467,6 +1533,7 @@ export class DenoisingManager extends EventDispatcher {
1467
1533
 
1468
1534
  // Remove stale completion-chain listener before aborting
1469
1535
  this._cleanupCompletionListener();
1536
+ this._postIsStillComplete = null;
1470
1537
 
1471
1538
  // The 2D canvas is the upscaler's alone: a reset means its enlarged result no longer
1472
1539
  // describes anything, so the render canvas comes back.
@@ -1509,6 +1576,7 @@ export class DenoisingManager extends EventDispatcher {
1509
1576
 
1510
1577
  // Remove pending completion-chain listener
1511
1578
  this._cleanupCompletionListener();
1579
+ this._postIsStillComplete = null;
1512
1580
  // Owns a second GPUDevice plus 3 MB of weights, so it must not outlive the manager.
1513
1581
  this._releaseNeuralUpscaler();
1514
1582
  this._releaseNeuralRetouch();
@@ -1523,6 +1591,13 @@ export class DenoisingManager extends EventDispatcher {
1523
1591
  if ( this._denoiserEndHandler ) this.denoiser.removeEventListener( 'end', this._denoiserEndHandler );
1524
1592
  if ( this._denoiserTileHandler ) this.denoiser.removeEventListener( 'tileProgress', this._denoiserTileHandler );
1525
1593
  if ( this._denoiserErrorHandler ) this.denoiser.removeEventListener( 'error', this._denoiserErrorHandler );
1594
+ if ( this._denoiserLoadHandler ) {
1595
+
1596
+ for ( const type of [ 'loading', 'loaded', 'error' ] ) this.denoiser.removeEventListener( type, this._denoiserLoadHandler );
1597
+ if ( this.denoiser.state?.isLoading ) this.dispatchEvent( { type: EngineEvents.DENOISER_LOADING, loading: false } );
1598
+
1599
+ }
1600
+
1526
1601
  this.denoiser.dispose();
1527
1602
  this.denoiser = null;
1528
1603
 
@@ -1547,6 +1622,7 @@ export class DenoisingManager extends EventDispatcher {
1547
1622
  this._denoiserEndHandler = null;
1548
1623
  this._denoiserTileHandler = null;
1549
1624
  this._denoiserErrorHandler = null;
1625
+ this._denoiserLoadHandler = null;
1550
1626
  this._upscalerResChangedHandler = null;
1551
1627
  this._upscalerStartHandler = null;
1552
1628
  this._upscalerProgressHandler = null;
@@ -1737,10 +1813,12 @@ export class DenoisingManager extends EventDispatcher {
1737
1813
  /** Enables or disables the AI upscaler. */
1738
1814
  setUpscalerEnabled( enabled ) {
1739
1815
 
1816
+ const changed = !! this.upscaler && this.upscaler.enabled !== !! enabled;
1740
1817
  if ( this.upscaler ) this.upscaler.enabled = enabled;
1741
1818
  else if ( enabled ) this._recordNoOverlayCanvas( 'upscaler' );
1742
1819
  // The enlarged picture describes a setting that is no longer on.
1743
1820
  if ( ! enabled ) this._restoreRenderDisplay();
1821
+ if ( changed ) this._redoPostPasses();
1744
1822
 
1745
1823
  }
1746
1824