rayzee 11.4.0 → 11.5.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.
- package/dist/{ArchiveImporter-Cgyx5AF7.js → ArchiveImporter-29Nl1567.js} +2 -2
- package/dist/{BidirectionalIntegrator-BKF-cwH2.js → BidirectionalIntegrator-CJay8tXK.js} +2 -2
- package/dist/{NeuralRetouch-C3Q2XqbF.js → NeuralRetouch-KqGVPV7u.js} +2 -2
- package/dist/{NeuralSuperRes-DV5TtURP.js → NeuralSuperRes-BHxhYzlD.js} +2 -2
- package/dist/{PathTracer-D3MvEaZK.js → PathTracer-Cldt0mXi.js} +1 -1
- package/dist/{PhysicalSky-CY4jiz6a.js → PhysicalSky-CUWrTcmT.js} +1 -1
- package/dist/{RayzeeRenderer-D23m6NqG.js → RayzeeRenderer-Dd-Dit0v.js} +3 -3
- package/dist/{ToneMapGPU-CTDBvmmS.js → ToneMapGPU-sAs8wdgI.js} +14 -1
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/rayzee-core.es.js +3 -3
- package/dist/rayzee.es.js +102 -61
- package/dist/rayzee.umd.js +5 -5
- package/dist/{sceneCachePolicy-AKOEu2lf.js → sceneCachePolicy--ta6moJu.js} +1 -1
- package/package.json +1 -1
- package/src/Passes/AIUpscaler.js +15 -12
- package/src/Processor/ToneMapGPU.js +22 -3
- package/src/Stages/LocalExposure.js +25 -2
- package/src/managers/DenoisingManager.js +82 -19
package/package.json
CHANGED
package/src/Passes/AIUpscaler.js
CHANGED
|
@@ -123,7 +123,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
123
123
|
this._worker = null;
|
|
124
124
|
this._currentModelUrl = null;
|
|
125
125
|
this._tileId = 0;
|
|
126
|
-
this._pendingWorkerHandlers = new
|
|
126
|
+
this._pendingWorkerHandlers = new Map();
|
|
127
127
|
|
|
128
128
|
// Alpha channel cache (bilinear-upscaled from source, applied per tile)
|
|
129
129
|
this._upscaledAlpha = null;
|
|
@@ -227,7 +227,8 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
227
227
|
|
|
228
228
|
// ─── Public Lifecycle ─────────────────────────────────────────────────────
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
/** @param {ImageData} [source] - what to enlarge instead of the render canvas */
|
|
231
|
+
async start( source = null ) {
|
|
231
232
|
|
|
232
233
|
if ( ! this.enabled || this.state.isUpscaling || this.state.isLoading ) {
|
|
233
234
|
|
|
@@ -238,7 +239,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
238
239
|
this.dispatchEvent( { type: 'start' } );
|
|
239
240
|
|
|
240
241
|
const startTime = performance.now();
|
|
241
|
-
const success = await this.execute();
|
|
242
|
+
const success = await this.execute( source );
|
|
242
243
|
|
|
243
244
|
if ( success ) {
|
|
244
245
|
|
|
@@ -260,7 +261,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
260
261
|
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
async execute() {
|
|
264
|
+
async execute( source = null ) {
|
|
264
265
|
|
|
265
266
|
if ( ! this.enabled ) return false;
|
|
266
267
|
|
|
@@ -270,7 +271,11 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
270
271
|
// Capture source image SYNCHRONOUSLY before any async work.
|
|
271
272
|
// WebGPU canvas textures expire after each compositor frame,
|
|
272
273
|
// so we must grab the pixels before awaiting model load.
|
|
273
|
-
if (
|
|
274
|
+
if ( source ) {
|
|
275
|
+
|
|
276
|
+
this._capturedSource = source;
|
|
277
|
+
|
|
278
|
+
} else if ( this.hdr && this.getGPUTextures ) {
|
|
274
279
|
|
|
275
280
|
// HDR path: float planes tone-mapped on the GPU, no 8-bit step before the network
|
|
276
281
|
this._capturedSource = await this._captureSourceHDR();
|
|
@@ -586,7 +591,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
586
591
|
|
|
587
592
|
};
|
|
588
593
|
|
|
589
|
-
this._pendingWorkerHandlers.
|
|
594
|
+
this._pendingWorkerHandlers.set( handler, reject );
|
|
590
595
|
this._worker.addEventListener( 'message', handler );
|
|
591
596
|
this._worker.postMessage(
|
|
592
597
|
{ type: 'infer', tileData, width, height, id },
|
|
@@ -814,15 +819,13 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
814
819
|
|
|
815
820
|
// ─── Disposal ─────────────────────────────────────────────────────────────
|
|
816
821
|
|
|
822
|
+
// Rejected, not dropped: an aborted run has to end before the next one starts.
|
|
817
823
|
_cleanupPendingWorkerHandlers() {
|
|
818
824
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
for ( const handler of this._pendingWorkerHandlers ) {
|
|
825
|
+
for ( const [ handler, reject ] of this._pendingWorkerHandlers ) {
|
|
822
826
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
}
|
|
827
|
+
this._worker?.removeEventListener( 'message', handler );
|
|
828
|
+
reject( new DOMException( 'Aborted', 'AbortError' ) );
|
|
826
829
|
|
|
827
830
|
}
|
|
828
831
|
|
|
@@ -275,8 +275,9 @@ export class PackedToneMapper {
|
|
|
275
275
|
* @param {number} [tone.saturation=1]
|
|
276
276
|
* @param {boolean} [tone.flipY=false]
|
|
277
277
|
* @param {boolean} [tone.preserveAlpha=false] - the source's alpha, rounded as the CPU rounds it; else 255
|
|
278
|
-
* @param {{key: string, wgsl: string, entries: GPUBindGroupEntry[]}} [tone.gain] - a
|
|
279
|
-
* curve: WGSL defining `rayzee_gain( uv, linear, exposure ) -> f32` over bind group 1
|
|
278
|
+
* @param {{key: string, wgsl: string, entries: GPUBindGroupEntry[], device?: GPUDevice, copyTo?: Function}} [tone.gain] - a
|
|
279
|
+
* per-pixel gain before the curve: WGSL defining `rayzee_gain( uv, linear, exposure ) -> f32` over bind group 1.
|
|
280
|
+
* One on another device is copied to this one for the run (`copyTo`).
|
|
280
281
|
* @returns {Promise<Uint8ClampedArray>} RGBA bytes, `width * height * 4`
|
|
281
282
|
*/
|
|
282
283
|
async toRGBA8( src, tone ) {
|
|
@@ -298,7 +299,25 @@ export class PackedToneMapper {
|
|
|
298
299
|
|
|
299
300
|
}
|
|
300
301
|
|
|
301
|
-
async _run( src,
|
|
302
|
+
async _run( src, tone = {} ) {
|
|
303
|
+
|
|
304
|
+
const gain = tone.gain;
|
|
305
|
+
if ( ! gain?.device || gain.device === this.device ) return this._dispatch( src, tone );
|
|
306
|
+
|
|
307
|
+
const copy = await gain.copyTo( this.device );
|
|
308
|
+
try {
|
|
309
|
+
|
|
310
|
+
return await this._dispatch( src, { ...tone, gain: copy } );
|
|
311
|
+
|
|
312
|
+
} finally {
|
|
313
|
+
|
|
314
|
+
copy.destroy();
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async _dispatch( src, { exposure = 1, toneMapping = 0, saturation = 1, flipY = false, preserveAlpha = false, gain = null } = {} ) {
|
|
302
321
|
|
|
303
322
|
if ( this.disposed ) throw new Error( 'PackedToneMapper: disposed' );
|
|
304
323
|
if ( ! this._storage ) throw new Error( 'PackedToneMapper: call ensureSize() first' );
|
|
@@ -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
|
|
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
|
}
|
|
@@ -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).
|
|
@@ -1148,6 +1153,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1148
1153
|
/** Turns the neural-rendering (detail) pass on or off, and sets its appearance controls. */
|
|
1149
1154
|
setNeuralRendering( enabled, settings = null ) {
|
|
1150
1155
|
|
|
1156
|
+
const changed = this.neuralRendering !== !! enabled;
|
|
1151
1157
|
this.neuralRendering = !! enabled;
|
|
1152
1158
|
if ( settings ) this.neuralRenderingSettings = { ...this.neuralRenderingSettings, ...settings };
|
|
1153
1159
|
if ( ! this.neuralRendering ) {
|
|
@@ -1157,6 +1163,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1157
1163
|
|
|
1158
1164
|
}
|
|
1159
1165
|
|
|
1166
|
+
if ( changed ) this._redoPostPasses();
|
|
1167
|
+
|
|
1160
1168
|
}
|
|
1161
1169
|
|
|
1162
1170
|
_releaseNeuralRetouch() {
|
|
@@ -1167,14 +1175,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1167
1175
|
}
|
|
1168
1176
|
|
|
1169
1177
|
/**
|
|
1170
|
-
* The
|
|
1171
|
-
*
|
|
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.
|
|
1178
|
+
* The post-chain over the picture the closing denoise just produced: the detail pass at render size,
|
|
1179
|
+
* then the chosen upscaler (neural super resolution or the ONNX one), either or both.
|
|
1178
1180
|
*
|
|
1179
1181
|
* Deliberately fire-and-forget: the render is already finished and a failure must not break it.
|
|
1180
1182
|
*/
|
|
@@ -1182,6 +1184,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1182
1184
|
|
|
1183
1185
|
const wantSR = this.upscaler?.enabled && this.upscalerBackend === 'neural';
|
|
1184
1186
|
const wantNR = this.neuralRendering;
|
|
1187
|
+
const wantESRGAN = this.upscaler?.enabled && ! wantSR;
|
|
1185
1188
|
if ( ! wantSR && ! wantNR ) return;
|
|
1186
1189
|
|
|
1187
1190
|
// A second completion can land while the first pass is still in flight — the models are
|
|
@@ -1230,7 +1233,12 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1230
1233
|
|
|
1231
1234
|
}
|
|
1232
1235
|
|
|
1233
|
-
if ( ! wantSR && ! runNR )
|
|
1236
|
+
if ( ! wantSR && ! runNR ) {
|
|
1237
|
+
|
|
1238
|
+
if ( wantESRGAN && ! this._postCancelled ) await this.upscaler.start();
|
|
1239
|
+
return;
|
|
1240
|
+
|
|
1241
|
+
}
|
|
1234
1242
|
|
|
1235
1243
|
// Must be the effective value, not the settings one: AutoExposure overwrites
|
|
1236
1244
|
// `renderer.toneMappingExposure` every frame and never touches settings, so reading
|
|
@@ -1291,6 +1299,15 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1291
1299
|
|
|
1292
1300
|
}
|
|
1293
1301
|
|
|
1302
|
+
if ( this._postRedo || this._postCancelled ) return;
|
|
1303
|
+
|
|
1304
|
+
if ( wantESRGAN ) {
|
|
1305
|
+
|
|
1306
|
+
await this.upscaler.start( new ImageData( image.rgba8, image.width, image.height ) );
|
|
1307
|
+
return;
|
|
1308
|
+
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1294
1311
|
sr.presentRGBA8( this.upscalerCanvas, image.rgba8, image.width, image.height );
|
|
1295
1312
|
|
|
1296
1313
|
if ( ! isStillComplete() ) {
|
|
@@ -1360,10 +1377,61 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1360
1377
|
|
|
1361
1378
|
}
|
|
1362
1379
|
|
|
1380
|
+
/** The neural chain or the ONNX upscaler over the finished render, as the switches ask. */
|
|
1381
|
+
_runPostPasses( isStillComplete ) {
|
|
1382
|
+
|
|
1383
|
+
this._postIsStillComplete = isStillComplete;
|
|
1384
|
+
this._postCancelled = false;
|
|
1385
|
+
|
|
1386
|
+
// The neural chain owns the overlay canvas when it runs, and hands its result to the ONNX upscaler.
|
|
1387
|
+
if ( this.neuralRendering || ( this.upscaler?.enabled && this.upscalerBackend === 'neural' ) ) {
|
|
1388
|
+
|
|
1389
|
+
this._postRun = this._runNeuralPost( isStillComplete );
|
|
1390
|
+
|
|
1391
|
+
} else {
|
|
1392
|
+
|
|
1393
|
+
this._postRun = this.upscaler?.enabled ? this.upscaler.start() : null;
|
|
1394
|
+
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/** Runs the post passes again on a finished render whose switches changed, once the run in flight ends. */
|
|
1400
|
+
_redoPostPasses() {
|
|
1401
|
+
|
|
1402
|
+
const isStillComplete = this._postIsStillComplete;
|
|
1403
|
+
// No chain yet: the closing denoise is still running and the chain reads the switches when it starts.
|
|
1404
|
+
if ( ! isStillComplete?.() || this._postRedo ) return;
|
|
1405
|
+
|
|
1406
|
+
this.upscaler?.abort();
|
|
1407
|
+
this._postCancelled = false;
|
|
1408
|
+
this._postRedo = Promise.resolve( this._postRun ).catch( () => {} ).then( () => {
|
|
1409
|
+
|
|
1410
|
+
this._postRedo = null;
|
|
1411
|
+
if ( this._postCancelled || this._postIsStillComplete !== isStillComplete || ! isStillComplete() ) return;
|
|
1412
|
+
this._restoreRenderDisplay();
|
|
1413
|
+
this._runPostPasses( isStillComplete );
|
|
1414
|
+
|
|
1415
|
+
} );
|
|
1416
|
+
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/** Stops the post passes on the finished render, which stays on screen as rendered. The switches stay as they are. */
|
|
1420
|
+
cancelPostPasses() {
|
|
1421
|
+
|
|
1422
|
+
this._postCancelled = true;
|
|
1423
|
+
this.upscaler?.abort();
|
|
1424
|
+
this._restoreRenderDisplay();
|
|
1425
|
+
// A retouch in flight cannot be stopped, only discarded, and ends later.
|
|
1426
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_END } );
|
|
1427
|
+
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1363
1430
|
onRenderComplete( { isStillComplete } ) {
|
|
1364
1431
|
|
|
1365
1432
|
// Remove any stale completion-chain listener from a previous render cycle
|
|
1366
1433
|
this._cleanupCompletionListener();
|
|
1434
|
+
this._postIsStillComplete = null;
|
|
1367
1435
|
|
|
1368
1436
|
// What closes the render, if anything. A full pass at the chosen tier when that switch is
|
|
1369
1437
|
// on; otherwise, if OIDN owns the live view, one last cheap refresh — the cadence's last
|
|
@@ -1383,16 +1451,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1383
1451
|
// denoise. Ordered after `_denoiserEndHandler`, which publishes the picture.
|
|
1384
1452
|
if ( closing ) this._onDisplayRefresh?.();
|
|
1385
1453
|
|
|
1386
|
-
|
|
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();
|
|
1454
|
+
this._runPostPasses( isStillComplete );
|
|
1396
1455
|
|
|
1397
1456
|
};
|
|
1398
1457
|
|
|
@@ -1467,6 +1526,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1467
1526
|
|
|
1468
1527
|
// Remove stale completion-chain listener before aborting
|
|
1469
1528
|
this._cleanupCompletionListener();
|
|
1529
|
+
this._postIsStillComplete = null;
|
|
1470
1530
|
|
|
1471
1531
|
// The 2D canvas is the upscaler's alone: a reset means its enlarged result no longer
|
|
1472
1532
|
// describes anything, so the render canvas comes back.
|
|
@@ -1509,6 +1569,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1509
1569
|
|
|
1510
1570
|
// Remove pending completion-chain listener
|
|
1511
1571
|
this._cleanupCompletionListener();
|
|
1572
|
+
this._postIsStillComplete = null;
|
|
1512
1573
|
// Owns a second GPUDevice plus 3 MB of weights, so it must not outlive the manager.
|
|
1513
1574
|
this._releaseNeuralUpscaler();
|
|
1514
1575
|
this._releaseNeuralRetouch();
|
|
@@ -1737,10 +1798,12 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
1737
1798
|
/** Enables or disables the AI upscaler. */
|
|
1738
1799
|
setUpscalerEnabled( enabled ) {
|
|
1739
1800
|
|
|
1801
|
+
const changed = !! this.upscaler && this.upscaler.enabled !== !! enabled;
|
|
1740
1802
|
if ( this.upscaler ) this.upscaler.enabled = enabled;
|
|
1741
1803
|
else if ( enabled ) this._recordNoOverlayCanvas( 'upscaler' );
|
|
1742
1804
|
// The enlarged picture describes a setting that is no longer on.
|
|
1743
1805
|
if ( ! enabled ) this._restoreRenderDisplay();
|
|
1806
|
+
if ( changed ) this._redoPostPasses();
|
|
1744
1807
|
|
|
1745
1808
|
}
|
|
1746
1809
|
|