rayzee 8.6.0 → 8.7.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/README.md +1 -53
- package/dist/{DLSSNeural-BhBCBgKf.js → NeuralRetouch-CYOJ21Wn.js} +31 -31
- package/dist/{DLSSSuperRes-BCA7F_k5.js → NeuralSuperRes-Dc6jRYke.js} +23 -23
- package/dist/{ToneMapGPU-BzcLheoR.js → ToneMapGPU-BVE852iN.js} +2 -2
- package/dist/rayzee.es.js +3552 -2990
- package/dist/rayzee.umd.js +17 -17
- package/package.json +1 -1
- package/src/AssetConfig.js +6 -5
- package/src/Passes/OIDNDenoiser.js +13 -5
- package/src/PathTracerApp.js +97 -2
- package/src/Processor/AssetLoader.js +14 -8
- package/src/Processor/EmissiveTriangleBuilder.js +70 -0
- package/src/Processor/PBRT/PBRTAnimation.js +646 -0
- package/src/Processor/PBRT/PBRTParser.js +179 -52
- package/src/Processor/PBRT/PBRTSceneBuilder.js +181 -25
- package/src/Processor/PBRT/index.js +73 -14
- package/src/Processor/SceneProcessor.js +22 -0
- package/src/Stages/PathTracerStage.js +16 -1
- package/src/index.js +3 -3
- package/src/managers/AnimationManager.js +202 -8
- package/src/managers/DenoisingManager.js +60 -44
- package/src/{dlss/DLSSNeural.js → neural/NeuralRetouch.js} +42 -41
- package/src/{dlss/DLSSSuperRes.js → neural/NeuralSuperRes.js} +27 -26
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Retouch — the neural-rendering (detail) pass.
|
|
3
3
|
*
|
|
4
4
|
* Runs over a finished, denoised image and adjusts its appearance: local tone and structure, and a
|
|
5
5
|
* skin-specific term. It does not change resolution and it does not denoise. Measured on path-traced
|
|
@@ -36,7 +36,7 @@ import { getAssetConfig } from '../AssetConfig.js';
|
|
|
36
36
|
* The model's own settings contract. Every field is numeric and validated hard by the runtime —
|
|
37
37
|
* passing a string throws `"<name> must be finite"`.
|
|
38
38
|
*/
|
|
39
|
-
export const
|
|
39
|
+
export const RETOUCH_DEFAULTS = Object.freeze( {
|
|
40
40
|
enabled: true,
|
|
41
41
|
intensity: 1,
|
|
42
42
|
localTone: 1,
|
|
@@ -52,7 +52,7 @@ export const DLSS_NR_DEFAULTS = Object.freeze( {
|
|
|
52
52
|
* How much of the network's own colour to take, 0..1. Ours, not the runtime's.
|
|
53
53
|
*
|
|
54
54
|
* ⚠️ It must be kept OUT of the object handed to the runtime: that one is validated by key and
|
|
55
|
-
* throws `Unknown
|
|
55
|
+
* throws `Unknown Retouch setting: colorStrength`. `splitSettings` does the separating.
|
|
56
56
|
*
|
|
57
57
|
* It is `color_strength` in the composition kernel, blending between the original chroma relit to
|
|
58
58
|
* the network's luminance (`original * ratio`, at 0) and the network's full colour (at 1). At 0 the
|
|
@@ -65,13 +65,13 @@ export const DLSS_NR_DEFAULTS = Object.freeze( {
|
|
|
65
65
|
* are the same at all three, so nothing the pass is actually for is given up. The renderer's chroma
|
|
66
66
|
* is ground truth here; the network's is a guess about a photograph.
|
|
67
67
|
*/
|
|
68
|
-
export const
|
|
68
|
+
export const RETOUCH_COLOR_STRENGTH = 0;
|
|
69
69
|
|
|
70
70
|
/** @returns {{runtime: object, colorStrength: number}} */
|
|
71
71
|
function splitSettings( settings = {} ) {
|
|
72
72
|
|
|
73
|
-
const { colorStrength =
|
|
74
|
-
return { runtime: { ...
|
|
73
|
+
const { colorStrength = RETOUCH_COLOR_STRENGTH, ...rest } = settings;
|
|
74
|
+
return { runtime: { ...RETOUCH_DEFAULTS, ...rest }, colorStrength };
|
|
75
75
|
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -95,12 +95,12 @@ function splitSettings( settings = {} ) {
|
|
|
95
95
|
* reserve (`MAX_STORAGE_TEXTURE_SIZE`, 2048) already caps that at exactly 4.19 MP. The check below
|
|
96
96
|
* is therefore unreachable today and exists as a floor under a raised reserve.
|
|
97
97
|
*/
|
|
98
|
-
const log = createLogger( '
|
|
98
|
+
const log = createLogger( 'neural' );
|
|
99
99
|
|
|
100
|
-
export const
|
|
100
|
+
export const RETOUCH_MAX_PIXELS = 2048 * 2048;
|
|
101
101
|
|
|
102
102
|
/** Ranges the runtime clamps to. `skinStructure: -1` means "follow localStructure". */
|
|
103
|
-
export const
|
|
103
|
+
export const RETOUCH_RANGES = Object.freeze( {
|
|
104
104
|
intensity: [ 0, 1 ],
|
|
105
105
|
localTone: [ 0, 2 ],
|
|
106
106
|
localStructure: [ 0, 2 ],
|
|
@@ -112,31 +112,32 @@ export const DLSS_NR_RANGES = Object.freeze( {
|
|
|
112
112
|
let _runtimePromise = null;
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
* The runtime is a plain script that installs `globalThis.
|
|
115
|
+
* The runtime is a plain script that installs `globalThis.NeuralRuntime`, not a module, so it has to
|
|
116
116
|
* be injected rather than imported. Cached: a second tag would re-run a 1 MB bundle.
|
|
117
117
|
*/
|
|
118
118
|
function loadRuntime() {
|
|
119
119
|
|
|
120
120
|
if ( _runtimePromise ) return _runtimePromise;
|
|
121
121
|
|
|
122
|
-
const {
|
|
123
|
-
|
|
122
|
+
const { neuralRuntimeUrl, neuralAssetBaseUrl } = getAssetConfig();
|
|
123
|
+
if ( ! neuralAssetBaseUrl ) return Promise.reject( new Error( 'No neural model location: set neuralAssetBaseUrl with configureAssets()' ) );
|
|
124
|
+
globalThis.__NEURAL_ASSET_BASE__ = neuralAssetBaseUrl;
|
|
124
125
|
|
|
125
126
|
_runtimePromise = new Promise( ( resolve, reject ) => {
|
|
126
127
|
|
|
127
|
-
if ( globalThis.
|
|
128
|
+
if ( globalThis.NeuralRuntime?.getSrNrChain ) return resolve( globalThis.NeuralRuntime.getSrNrChain() );
|
|
128
129
|
|
|
129
130
|
const el = document.createElement( 'script' );
|
|
130
|
-
el.src =
|
|
131
|
+
el.src = neuralRuntimeUrl;
|
|
131
132
|
el.onload = () => {
|
|
132
133
|
|
|
133
|
-
const factory = globalThis.
|
|
134
|
-
if ( ! factory ) return reject( new Error( '
|
|
134
|
+
const factory = globalThis.NeuralRuntime?.getSrNrChain;
|
|
135
|
+
if ( ! factory ) return reject( new Error( 'Neural runtime loaded but exposed no retouch entry' ) );
|
|
135
136
|
resolve( factory() );
|
|
136
137
|
|
|
137
138
|
};
|
|
138
139
|
|
|
139
|
-
el.onerror = () => reject( new Error( `Failed to load the
|
|
140
|
+
el.onerror = () => reject( new Error( `Failed to load the neural runtime from ${neuralRuntimeUrl}` ) );
|
|
140
141
|
document.head.appendChild( el );
|
|
141
142
|
|
|
142
143
|
} );
|
|
@@ -183,7 +184,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
|
183
184
|
* Holds a GPUDevice of its own (the runtime builds it) plus ~140 MB of weights, so it must be
|
|
184
185
|
* disposed rather than dropped.
|
|
185
186
|
*/
|
|
186
|
-
export class
|
|
187
|
+
export class NeuralRetouch {
|
|
187
188
|
|
|
188
189
|
constructor( chain, canvas ) {
|
|
189
190
|
|
|
@@ -232,15 +233,15 @@ export class DLSSNeural {
|
|
|
232
233
|
|
|
233
234
|
if ( ! Number.isInteger( width ) || ! Number.isInteger( height ) || width < 1 || height < 1 ) {
|
|
234
235
|
|
|
235
|
-
throw new Error( '
|
|
236
|
+
throw new Error( 'NeuralRetouch.create: width and height must be positive integers' );
|
|
236
237
|
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
if ( width * height >
|
|
240
|
+
if ( width * height > RETOUCH_MAX_PIXELS ) {
|
|
240
241
|
|
|
241
242
|
throw new Error(
|
|
242
|
-
`
|
|
243
|
-
`${(
|
|
243
|
+
`NeuralRetouch.create: ${width}x${height} is ${( width * height / 1e6 ).toFixed( 1 )} MP, above the ` +
|
|
244
|
+
`${( RETOUCH_MAX_PIXELS / 1e6 ).toFixed( 1 )} MP this pass survives. Past it the run takes minutes ` +
|
|
244
245
|
'and then loses every GPU device in the page.'
|
|
245
246
|
);
|
|
246
247
|
|
|
@@ -256,7 +257,7 @@ export class DLSSNeural {
|
|
|
256
257
|
surface, width, height, onProgress, splitSettings( settings ).runtime,
|
|
257
258
|
);
|
|
258
259
|
|
|
259
|
-
const instance = new
|
|
260
|
+
const instance = new NeuralRetouch( chain, surface );
|
|
260
261
|
instance._allocate();
|
|
261
262
|
return instance;
|
|
262
263
|
|
|
@@ -267,7 +268,7 @@ export class DLSSNeural {
|
|
|
267
268
|
const { validWidth, validHeight } = this.chain.pipeline.geometry;
|
|
268
269
|
|
|
269
270
|
this.source = this.device.createBuffer( {
|
|
270
|
-
label: 'rayzee:
|
|
271
|
+
label: 'rayzee:retouch-source',
|
|
271
272
|
size: validWidth * validHeight * 8,
|
|
272
273
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
273
274
|
} );
|
|
@@ -276,34 +277,34 @@ export class DLSSNeural {
|
|
|
276
277
|
|
|
277
278
|
const bytes = validWidth * validHeight * 4;
|
|
278
279
|
this._readbackStorage = this.device.createBuffer( {
|
|
279
|
-
label: 'rayzee:
|
|
280
|
+
label: 'rayzee:retouch-readback',
|
|
280
281
|
size: bytes,
|
|
281
282
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
|
|
282
283
|
} );
|
|
283
284
|
this._readbackMap = this.device.createBuffer( {
|
|
284
|
-
label: 'rayzee:
|
|
285
|
+
label: 'rayzee:retouch-readback-map',
|
|
285
286
|
size: bytes,
|
|
286
287
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
287
288
|
} );
|
|
288
289
|
this._readbackPipeline = this.device.createComputePipeline( {
|
|
289
|
-
label: 'rayzee:
|
|
290
|
+
label: 'rayzee:retouch-readback',
|
|
290
291
|
layout: 'auto',
|
|
291
292
|
compute: { module: this.device.createShaderModule( { code: READBACK_WGSL } ), entryPoint: 'main' },
|
|
292
293
|
} );
|
|
293
294
|
|
|
294
295
|
const hdrBytes = validWidth * validHeight * 8;
|
|
295
296
|
this._hdrStorage = this.device.createBuffer( {
|
|
296
|
-
label: 'rayzee:
|
|
297
|
+
label: 'rayzee:retouch-hdr',
|
|
297
298
|
size: hdrBytes,
|
|
298
299
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
|
|
299
300
|
} );
|
|
300
301
|
this._hdrMap = this.device.createBuffer( {
|
|
301
|
-
label: 'rayzee:
|
|
302
|
+
label: 'rayzee:retouch-hdr-map',
|
|
302
303
|
size: hdrBytes,
|
|
303
304
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
304
305
|
} );
|
|
305
306
|
this._hdrPipeline = this.device.createComputePipeline( {
|
|
306
|
-
label: 'rayzee:
|
|
307
|
+
label: 'rayzee:retouch-hdr',
|
|
307
308
|
layout: 'auto',
|
|
308
309
|
compute: { module: this.device.createShaderModule( { code: HDR_READBACK_WGSL } ), entryPoint: 'main' },
|
|
309
310
|
} );
|
|
@@ -317,8 +318,8 @@ export class DLSSNeural {
|
|
|
317
318
|
if ( ! texture ) {
|
|
318
319
|
|
|
319
320
|
throw new Error(
|
|
320
|
-
'
|
|
321
|
-
+ 'or
|
|
321
|
+
'NeuralRetouch: the runtime has no scene-referred output. Either no frame has been produced yet, '
|
|
322
|
+
+ 'or neural-runtime.js was re-vendored without its `rayzee-patch` edits.'
|
|
322
323
|
);
|
|
323
324
|
|
|
324
325
|
}
|
|
@@ -331,7 +332,7 @@ export class DLSSNeural {
|
|
|
331
332
|
],
|
|
332
333
|
} );
|
|
333
334
|
|
|
334
|
-
const encoder = this.device.createCommandEncoder( { label: 'rayzee:
|
|
335
|
+
const encoder = this.device.createCommandEncoder( { label: 'rayzee:retouch-hdr' } );
|
|
335
336
|
const pass = encoder.beginComputePass();
|
|
336
337
|
pass.setPipeline( this._hdrPipeline );
|
|
337
338
|
pass.setBindGroup( 0, group );
|
|
@@ -346,7 +347,7 @@ export class DLSSNeural {
|
|
|
346
347
|
* The pass's result as scene-referred light, packed the way the upscaler wants it.
|
|
347
348
|
*
|
|
348
349
|
* Reads `hdrOutput`, the `rgba16float` texture the vendored runtime gained in
|
|
349
|
-
* `
|
|
350
|
+
* its `rayzee-patch` edits — the value one statement before its own 8-bit store, so nothing
|
|
350
351
|
* has been clamped, quantised or pushed through the model's hardcoded ACES curve yet.
|
|
351
352
|
*
|
|
352
353
|
* This is what lets the pass run FIRST, at render size, where it is ~10x cheaper than on an
|
|
@@ -381,7 +382,7 @@ export class DLSSNeural {
|
|
|
381
382
|
const { validWidth: w, validHeight: h } = this.chain.pipeline.geometry;
|
|
382
383
|
await this._packHDR( w, h );
|
|
383
384
|
|
|
384
|
-
this._toneMapper ??= new PackedToneMapper( this.device, 'rayzee:
|
|
385
|
+
this._toneMapper ??= new PackedToneMapper( this.device, 'rayzee:retouch-tonemap' );
|
|
385
386
|
this._toneMapper.ensureSize( w, h );
|
|
386
387
|
|
|
387
388
|
const rgba8 = await this._toneMapper.toRGBA8( this._hdrStorage, {
|
|
@@ -403,7 +404,7 @@ export class DLSSNeural {
|
|
|
403
404
|
|
|
404
405
|
const { validWidth: w, validHeight: h } = this.chain.pipeline.geometry;
|
|
405
406
|
const texture = this.chain.pipeline.slots[ this.slot ]?.output;
|
|
406
|
-
if ( ! texture ) throw new Error( '
|
|
407
|
+
if ( ! texture ) throw new Error( 'NeuralRetouch.readOutput: the pass has not produced a frame yet' );
|
|
407
408
|
|
|
408
409
|
const group = this.device.createBindGroup( {
|
|
409
410
|
layout: this._readbackPipeline.getBindGroupLayout( 0 ),
|
|
@@ -413,7 +414,7 @@ export class DLSSNeural {
|
|
|
413
414
|
],
|
|
414
415
|
} );
|
|
415
416
|
|
|
416
|
-
const encoder = this.device.createCommandEncoder( { label: 'rayzee:
|
|
417
|
+
const encoder = this.device.createCommandEncoder( { label: 'rayzee:retouch-readback' } );
|
|
417
418
|
const pass = encoder.beginComputePass();
|
|
418
419
|
pass.setPipeline( this._readbackPipeline );
|
|
419
420
|
pass.setBindGroup( 0, group );
|
|
@@ -477,7 +478,7 @@ const MAX_EXPOSURE = 20;
|
|
|
477
478
|
* @param {{half: Uint16Array, width: number, height: number}} opts.source
|
|
478
479
|
* @param {number} [opts.exposure=1] clamped to `MAX_EXPOSURE`
|
|
479
480
|
* @param {{exposure: number, toneMapping: number, saturation: number}} [opts.tone] engine display state
|
|
480
|
-
* @param {
|
|
481
|
+
* @param {NeuralRetouch} [opts.instance] reuse a network already built for this size
|
|
481
482
|
* @returns {Promise<object>} `{ instance, width, height, ms }` plus `rgba8` or `half`
|
|
482
483
|
*/
|
|
483
484
|
export async function enhanceFrame( {
|
|
@@ -494,7 +495,7 @@ export async function enhanceFrame( {
|
|
|
494
495
|
|
|
495
496
|
if ( ! nr ) {
|
|
496
497
|
|
|
497
|
-
nr = await
|
|
498
|
+
nr = await NeuralRetouch.create( {
|
|
498
499
|
width: source.width, height: source.height, settings, onProgress,
|
|
499
500
|
} );
|
|
500
501
|
|
|
@@ -505,7 +506,7 @@ export async function enhanceFrame( {
|
|
|
505
506
|
|
|
506
507
|
if ( ! source.half || source.half.length !== dst.length ) {
|
|
507
508
|
|
|
508
|
-
throw new Error( `
|
|
509
|
+
throw new Error( `NeuralRetouch: expected ${dst.length} packed halfs, got ${source.half?.length}` );
|
|
509
510
|
|
|
510
511
|
}
|
|
511
512
|
|
|
@@ -519,7 +520,7 @@ export async function enhanceFrame( {
|
|
|
519
520
|
nr.chain.pipeline.colorStrength = colorStrength;
|
|
520
521
|
|
|
521
522
|
const started = performance.now();
|
|
522
|
-
const encoder = nr.device.createCommandEncoder( { label: 'rayzee:
|
|
523
|
+
const encoder = nr.device.createCommandEncoder( { label: 'rayzee:retouch' } );
|
|
523
524
|
await nr.chain.encode( encoder, nr.slot, { buffer: nr.source }, merged, () => {}, { reset: true } );
|
|
524
525
|
nr.device.queue.submit( [ encoder.finish() ] );
|
|
525
526
|
await nr.device.queue.onSubmittedWorkDone();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Neural super resolution — final-render output scaling.
|
|
3
3
|
*
|
|
4
4
|
* Traces at half resolution, denoises, then runs one neural upscale to reach the
|
|
5
5
|
* requested output size. On a 1.89M-triangle interior at 32 spp this matched a
|
|
@@ -35,7 +35,7 @@ import { getAssetConfig } from '../AssetConfig.js';
|
|
|
35
35
|
/** The network is a fixed 2x per axis. */
|
|
36
36
|
export const SR_SCALE = 2;
|
|
37
37
|
|
|
38
|
-
const log = createLogger( '
|
|
38
|
+
const log = createLogger( 'neural' );
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Largest input side the runtime actually survives — 2048, i.e. 4096 output.
|
|
@@ -63,24 +63,25 @@ function loadRuntime() {
|
|
|
63
63
|
|
|
64
64
|
if ( _runtimePromise ) return _runtimePromise;
|
|
65
65
|
|
|
66
|
-
const {
|
|
67
|
-
|
|
66
|
+
const { neuralRuntimeUrl, neuralAssetBaseUrl } = getAssetConfig();
|
|
67
|
+
if ( ! neuralAssetBaseUrl ) return Promise.reject( new Error( 'No neural model location: set neuralAssetBaseUrl with configureAssets()' ) );
|
|
68
|
+
globalThis.__NEURAL_ASSET_BASE__ = neuralAssetBaseUrl;
|
|
68
69
|
|
|
69
70
|
_runtimePromise = new Promise( ( resolve, reject ) => {
|
|
70
71
|
|
|
71
|
-
if ( globalThis.
|
|
72
|
+
if ( globalThis.NeuralRuntime?.getNativeSR ) return resolve( globalThis.NeuralRuntime.getNativeSR() );
|
|
72
73
|
|
|
73
74
|
const el = document.createElement( 'script' );
|
|
74
|
-
el.src =
|
|
75
|
+
el.src = neuralRuntimeUrl;
|
|
75
76
|
el.onload = () => {
|
|
76
77
|
|
|
77
|
-
const factory = globalThis.
|
|
78
|
-
if ( ! factory ) return reject( new Error( '
|
|
78
|
+
const factory = globalThis.NeuralRuntime?.getNativeSR;
|
|
79
|
+
if ( ! factory ) return reject( new Error( 'Neural runtime loaded but exposed no super-resolution entry' ) );
|
|
79
80
|
resolve( factory() );
|
|
80
81
|
|
|
81
82
|
};
|
|
82
83
|
|
|
83
|
-
el.onerror = () => reject( new Error( `Failed to load the
|
|
84
|
+
el.onerror = () => reject( new Error( `Failed to load the neural runtime from ${neuralRuntimeUrl}` ) );
|
|
84
85
|
document.head.appendChild( el );
|
|
85
86
|
|
|
86
87
|
} );
|
|
@@ -126,20 +127,20 @@ function ensureReader( device, width, height ) {
|
|
|
126
127
|
width,
|
|
127
128
|
height,
|
|
128
129
|
storage: device.createBuffer( {
|
|
129
|
-
label: 'rayzee:
|
|
130
|
+
label: 'rayzee:neural-sr-pack',
|
|
130
131
|
size: bytes,
|
|
131
132
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
|
|
132
133
|
} ),
|
|
133
134
|
map: device.createBuffer( {
|
|
134
|
-
label: 'rayzee:
|
|
135
|
+
label: 'rayzee:neural-sr-pack-map',
|
|
135
136
|
size: bytes,
|
|
136
137
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
137
138
|
} ),
|
|
138
139
|
pipeline: device.createComputePipeline( {
|
|
139
|
-
label: 'rayzee:
|
|
140
|
+
label: 'rayzee:neural-sr-pack',
|
|
140
141
|
layout: 'auto',
|
|
141
142
|
compute: {
|
|
142
|
-
module: device.createShaderModule( { label: 'rayzee:
|
|
143
|
+
module: device.createShaderModule( { label: 'rayzee:neural-sr-pack', code: DENOISED_PACK_WGSL } ),
|
|
143
144
|
entryPoint: 'main',
|
|
144
145
|
},
|
|
145
146
|
} ),
|
|
@@ -171,7 +172,7 @@ export function releaseDenoisedReader() {
|
|
|
171
172
|
export async function readDenoisedHalf( denoiser ) {
|
|
172
173
|
|
|
173
174
|
const texture = denoiser?._outGPUTexture;
|
|
174
|
-
if ( ! texture ) throw new Error( '
|
|
175
|
+
if ( ! texture ) throw new Error( 'NeuralSuperRes: no denoised frame — enable OIDN and finish a render first' );
|
|
175
176
|
|
|
176
177
|
const device = denoiser.gpuDevice;
|
|
177
178
|
const { width, height } = denoiser._outTexSize;
|
|
@@ -184,7 +185,7 @@ export async function readDenoisedHalf( denoiser ) {
|
|
|
184
185
|
],
|
|
185
186
|
} );
|
|
186
187
|
|
|
187
|
-
const encoder = device.createCommandEncoder( { label: 'rayzee:
|
|
188
|
+
const encoder = device.createCommandEncoder( { label: 'rayzee:neural-sr-pack' } );
|
|
188
189
|
const pass = encoder.beginComputePass();
|
|
189
190
|
pass.setPipeline( reader.pipeline );
|
|
190
191
|
pass.setBindGroup( 0, group );
|
|
@@ -201,7 +202,7 @@ export async function readDenoisedHalf( denoiser ) {
|
|
|
201
202
|
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
export class
|
|
205
|
+
export class NeuralSuperRes {
|
|
205
206
|
|
|
206
207
|
constructor( sr, canvas ) {
|
|
207
208
|
|
|
@@ -255,14 +256,14 @@ export class DLSSSuperRes {
|
|
|
255
256
|
|
|
256
257
|
if ( ! Number.isInteger( width ) || ! Number.isInteger( height ) || width < 1 || height < 1 ) {
|
|
257
258
|
|
|
258
|
-
throw new Error( '
|
|
259
|
+
throw new Error( 'NeuralSuperRes.create: width and height must be positive integers' );
|
|
259
260
|
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
if ( width > SR_MAX_INPUT || height > SR_MAX_INPUT ) {
|
|
263
264
|
|
|
264
265
|
throw new Error(
|
|
265
|
-
`
|
|
266
|
+
`NeuralSuperRes.create: input capped at ${SR_MAX_INPUT}px per side (${SR_MAX_INPUT * SR_SCALE}px output), ` +
|
|
266
267
|
`got ${width}x${height}. Above that the runtime's history plane exceeds maxTextureDimension2D ` +
|
|
267
268
|
'and it fails later with an unrelated bind-group error.'
|
|
268
269
|
);
|
|
@@ -287,7 +288,7 @@ export class DLSSSuperRes {
|
|
|
287
288
|
|
|
288
289
|
}
|
|
289
290
|
|
|
290
|
-
const instance = new
|
|
291
|
+
const instance = new NeuralSuperRes( sr, canvas );
|
|
291
292
|
instance._allocate();
|
|
292
293
|
return instance;
|
|
293
294
|
|
|
@@ -306,13 +307,13 @@ export class DLSSSuperRes {
|
|
|
306
307
|
/** Runs the network once over a frame already in its own layout, as `readDenoisedHalf` returns. */
|
|
307
308
|
async _run( source ) {
|
|
308
309
|
|
|
309
|
-
if ( this.disposed ) throw new Error( '
|
|
310
|
+
if ( this.disposed ) throw new Error( 'NeuralSuperRes: disposed' );
|
|
310
311
|
|
|
311
312
|
const w = this.inputWidth;
|
|
312
313
|
const h = this.inputHeight;
|
|
313
314
|
if ( source.width !== w || source.height !== h ) {
|
|
314
315
|
|
|
315
|
-
throw new Error( `
|
|
316
|
+
throw new Error( `NeuralSuperRes: expected a ${w}x${h} source, got ${source.width}x${source.height}` );
|
|
316
317
|
|
|
317
318
|
}
|
|
318
319
|
|
|
@@ -320,7 +321,7 @@ export class DLSSSuperRes {
|
|
|
320
321
|
if ( ! source.half || source.half.length !== color.length ) {
|
|
321
322
|
|
|
322
323
|
throw new Error(
|
|
323
|
-
`
|
|
324
|
+
`NeuralSuperRes: expected ${color.length} packed halfs from readDenoisedHalf, got ${source.half?.length}`
|
|
324
325
|
);
|
|
325
326
|
|
|
326
327
|
}
|
|
@@ -364,7 +365,7 @@ export class DLSSSuperRes {
|
|
|
364
365
|
const { result, ms } = await this._run( source );
|
|
365
366
|
const { outputWidth, outputHeight } = this.geometry;
|
|
366
367
|
|
|
367
|
-
this._toneMapper ??= new PackedToneMapper( this.sr.device, 'rayzee:
|
|
368
|
+
this._toneMapper ??= new PackedToneMapper( this.sr.device, 'rayzee:neural-sr-tonemap' );
|
|
368
369
|
this._toneMapper.ensureSize( outputWidth, outputHeight );
|
|
369
370
|
|
|
370
371
|
const started = performance.now();
|
|
@@ -436,7 +437,7 @@ function waitForDenoise( app, samples, timeoutMs ) {
|
|
|
436
437
|
}, 200 );
|
|
437
438
|
|
|
438
439
|
const timer = setTimeout(
|
|
439
|
-
() => finish( false, new Error( '
|
|
440
|
+
() => finish( false, new Error( 'NeuralSuperRes: timed out waiting for the render to finish' ) ),
|
|
440
441
|
timeoutMs,
|
|
441
442
|
);
|
|
442
443
|
|
|
@@ -453,7 +454,7 @@ function waitForDenoise( app, samples, timeoutMs ) {
|
|
|
453
454
|
* @param {number} opts.outputWidth final width; must be even
|
|
454
455
|
* @param {number} opts.outputHeight final height; must be even
|
|
455
456
|
* @param {number} [opts.samples] samples to accumulate at half resolution
|
|
456
|
-
* @param {
|
|
457
|
+
* @param {NeuralSuperRes} [opts.upscaler] reuse an upscaler already built for this size
|
|
457
458
|
* @param {boolean} [opts.present] draw the result over the viewport
|
|
458
459
|
* @returns {Promise<{rgba8: Uint8ClampedArray, width: number, height: number, timings: object}>}
|
|
459
460
|
*/
|
|
@@ -495,7 +496,7 @@ export async function renderUpscaled( app, {
|
|
|
495
496
|
|
|
496
497
|
const t = performance.now();
|
|
497
498
|
onProgress( 'Loading the super-resolution model' );
|
|
498
|
-
sr = await
|
|
499
|
+
sr = await NeuralSuperRes.create( { width: inW, height: inH, onProgress } );
|
|
499
500
|
timings.load = performance.now() - t;
|
|
500
501
|
|
|
501
502
|
} else if ( sr.inputWidth !== inW || sr.inputHeight !== inH ) {
|