rayzee 8.3.0 → 8.4.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/README.md CHANGED
@@ -287,6 +287,11 @@ configureAssets({
287
287
  oidnWeightsBaseUrl: '/oidn-tzas/',
288
288
  upscalerModelBaseUrl: '/upscaler-onnx/',
289
289
 
290
+ // DLSS. The runtime is a plain script the host must serve — it installs
291
+ // `globalThis.DLSSRuntime` as a side effect and cannot be imported.
292
+ dlssRuntimeUrl: '/dlss/dlss-runtime.js',
293
+ dlssAssetBaseUrl: '/dlss/',
294
+
290
295
  // Prefix for engine-managed IndexedDB stores. Set to a unique value if multiple
291
296
  // apps embed the engine on the same origin to avoid cache collisions.
292
297
  cacheNamespace: 'my-app',
@@ -504,7 +509,11 @@ engine.lightManager.sync() // Re-upload light data to GPU
504
509
  engine.lightManager.showHelpers(true) // Toggle visual helpers
505
510
  ```
506
511
 
507
- Light `intensity` follows Blender: radiant power in watts for point, spot and area lights, irradiance in W/m² for directional. Dividing power by area only means something in metres, so the engine assumes **one world unit is one metre**; scenes authored in cm or mm must carry that scale in their node transforms, as glTF exporters do. glTF `RectAreaLightPlaceholder` nodes author `intensity` as three.js radiance (their `power` field is `intensity · width · height · π`); the importer converts it to power through the light's world area so the authored radiance is reproduced exactly, then applies the profile's `areaLightIntensityScale`.
512
+ Every light in the scene graph is a **valid three.js light**: it carries the quantity three.js expects for its type, in radiometric (watt-based) units — W/sr for point and spot, W/m² for directional, radiance for area, with `width`/`height` in world metres at unit scale. three.js' own shading maths is unit-agnostic, so the raster fallback, the light helpers and any host reading `sceneModel` are all correct without knowing anything about the path tracer. The rule lives in one module, `LightUnits.js`.
513
+
514
+ The **Lights panel edits Blender-style Power** — watts for point, spot and area, W/m² for the Sun — which is a view of that, converted by `lightPower()`/`setLightPower()` and nowhere else; `LightSerializer` converts the other way when it fills the GPU buffer. Dividing power by area only means something in metres, so the engine assumes **one world unit is one metre**; scenes authored in cm or mm must carry that scale in their node transforms, as glTF exporters do.
515
+
516
+ glTF punctual lights are photometric (candela, lux) and are divided by 683 lm/W on import, exactly as Blender's own glTF importer does. glTF `RectAreaLightPlaceholder` nodes author `intensity` as three.js radiance, which is already the stored quantity, so it is used verbatim. The record's own `power` field is ignored: it is written from the light's unscaled dimensions and disagrees with the world area whenever an ancestor scale is non-uniform.
508
517
 
509
518
  ### engine.animationManager
510
519
 
@@ -588,8 +597,10 @@ engine.denoisingManager.setOIDNQuality('high')
588
597
  engine.denoisingManager.setStrategy('oidn') // OIDN owns the live view; see below
589
598
  engine.denoisingManager.continuousDenoiseInterval = 250 // cap refreshes at 4/sec (default 8 = uncapped)
590
599
  engine.denoisingManager.setUpscalerEnabled(true)
591
- engine.denoisingManager.setUpscalerScaleFactor(2)
592
- engine.denoisingManager.setUpscalerQuality('high')
600
+ engine.denoisingManager.setUpscalerBackend('dlss') // 'esrgan' (default) | 'dlss'
601
+ engine.denoisingManager.setUpscalerScaleFactor(2) // ESRGAN only; DLSS is fixed at 2x
602
+ engine.denoisingManager.setUpscalerQuality('high') // ESRGAN only
603
+ engine.denoisingManager.setNeuralRendering(true, { intensity: 1, colorStrength: 0 })
593
604
  ```
594
605
 
595
606
  ### engine.interactionManager
@@ -1197,6 +1208,51 @@ engine.addEventListener(EngineEvents.UPSCALING_END, () => console.log('Upsc
1197
1208
 
1198
1209
  **Chaining with OIDN:** Upscaling and OIDN **can** run together — on render completion, OIDN runs first, then its denoised output is fed into the upscaler. Enable both; no manual coordination required.
1199
1210
 
1211
+ ### The DLSS passes
1212
+
1213
+ Two further neural passes, ported from NVIDIA's DLSS weights. Both run once when a render finishes
1214
+ and both need a denoised frame — on raw Monte-Carlo noise they measurably lose to a plain resize, so
1215
+ enable OIDN alongside them.
1216
+
1217
+ Unlike the ONNX upscaler these are **not fetched from a CDN**: `dlss-runtime.js` installs
1218
+ `globalThis.DLSSRuntime` as a side effect and cannot be imported, so the host serves it and points
1219
+ `configureAssets` at it (see above). It lives at `app/public/dlss/` in this repo, and the local edits
1220
+ it carries are written up in `app/public/dlss/PATCHES.md` — re-vendoring it without re-applying them
1221
+ breaks the retouch pass with an explanatory throw.
1222
+
1223
+ ```js
1224
+ // Super resolution, as a second AI Upscaler backend
1225
+ engine.denoisingManager.setUpscalerEnabled(true);
1226
+ engine.denoisingManager.setUpscalerBackend('dlss');
1227
+
1228
+ // The retouch pass — appearance, not resolution. Independent of the upscaler.
1229
+ engine.denoisingManager.setNeuralRendering(true, {
1230
+ intensity: 1, // 0..1 how much of the pass reaches the image
1231
+ localTone: 1, // 0..2 local light shaping; 1 is neutral
1232
+ localStructure: 1, // 0..2 fine surface detail; 1 is neutral
1233
+ colorStrength: 0, // 0..1 0 keeps the render's chroma, 1 takes the model's
1234
+ });
1235
+ ```
1236
+
1237
+ The chain is **denoise → retouch (at render size) → super resolution (2x) → tone map**, scene-referred
1238
+ throughout. The retouch pass runs *before* the upscale deliberately: it sees a quarter of the pixels
1239
+ there, and above `DLSS_NR_MAX_PIXELS` (4.19 MP) it takes minutes and then loses every GPU device in
1240
+ the page. `DenoisingManager` skips it above that size rather than letting it run.
1241
+
1242
+ | | Super resolution | Retouch |
1243
+ |---|---|---|
1244
+ | What it changes | Resolution, fixed 2x | Appearance only |
1245
+ | Weights | 3.2 MB | 141 MB, fetched on first use |
1246
+ | Cost (1.9M-tri interior, warm) | 260 ms at 1024²→2048², 947 ms at 2048²→4096² | ~6 s at 2048² |
1247
+ | Against a native render | Ties Real-ESRGAN on accuracy, 3x faster, sharper than native | Close to a no-op on path-traced images |
1248
+
1249
+ Exported for building UI against: `SR_SCALE`, `SR_MAX_INPUT`, `DLSS_NR_DEFAULTS`, `DLSS_NR_RANGES`,
1250
+ `DLSS_NR_MAX_PIXELS`, and `renderUpscaled( app, { outputWidth, outputHeight, samples } )` — the
1251
+ offline entry point that drives a half-size render, denoise and upscale in one call.
1252
+
1253
+ ⚠️ Each model brings **its own `GPUDevice`**, built by the runtime and not shareable. With both on,
1254
+ the page holds three: the renderer's and one each.
1255
+
1200
1256
  ## Troubleshooting
1201
1257
 
1202
1258
  **OIDN: `Cannot find module './tza'` (webpack)**
@@ -0,0 +1,202 @@
1
+ import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
2
+ import { o as t, r as n, t as r } from "./ToneMapGPU-DzqSyzTx.js";
3
+ //#region src/dlss/DLSSNeural.js
4
+ var i = /* @__PURE__ */ e({
5
+ DLSSNeural: () => m,
6
+ DLSS_NR_COLOR_STRENGTH: () => 0,
7
+ DLSS_NR_DEFAULTS: () => a,
8
+ DLSS_NR_MAX_PIXELS: () => c,
9
+ DLSS_NR_RANGES: () => l,
10
+ enhanceFrame: () => g
11
+ }), a = Object.freeze({
12
+ enabled: !0,
13
+ intensity: 1,
14
+ localTone: 1,
15
+ localStructure: 1,
16
+ skinStructure: -1,
17
+ autoMask: !0,
18
+ style: 0,
19
+ preset: 0,
20
+ uiCorrection: !1
21
+ });
22
+ function o(e = {}) {
23
+ let { colorStrength: t = 0, ...n } = e;
24
+ return {
25
+ runtime: {
26
+ ...a,
27
+ ...n
28
+ },
29
+ colorStrength: t
30
+ };
31
+ }
32
+ var s = t("dlss"), c = 2048 * 2048, l = Object.freeze({
33
+ intensity: [0, 1],
34
+ localTone: [0, 2],
35
+ localStructure: [0, 2],
36
+ skinStructure: [-1, 2],
37
+ style: [0, 2],
38
+ preset: [0, 3]
39
+ }), u = null;
40
+ function d() {
41
+ if (u) return u;
42
+ let { dlssRuntimeUrl: e, dlssAssetBaseUrl: t } = n();
43
+ return globalThis.__DLSS5_ASSET_BASE__ = t, u = new Promise((t, n) => {
44
+ if (globalThis.DLSSRuntime?.getSrNrChain) return t(globalThis.DLSSRuntime.getSrNrChain());
45
+ let r = document.createElement("script");
46
+ r.src = e, r.onload = () => {
47
+ let e = globalThis.DLSSRuntime?.getSrNrChain;
48
+ if (!e) return n(/* @__PURE__ */ Error("DLSS runtime loaded but exposed no neural-rendering entry"));
49
+ t(e());
50
+ }, r.onerror = () => n(/* @__PURE__ */ Error(`Failed to load the DLSS runtime from ${e}`)), document.head.appendChild(r);
51
+ }), u;
52
+ }
53
+ var f = "\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var<storage, read_write> dst: array<u32>;\n\n@compute @workgroup_size(8, 8)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let size = textureDimensions(src);\n if ( gid.x >= size.x || gid.y >= size.y ) { return; }\n let c = textureLoad(src, vec2<i32>(gid.xy), 0);\n let r = u32(clamp(c.r, 0.0, 1.0) * 255.0 + 0.5);\n let g = u32(clamp(c.g, 0.0, 1.0) * 255.0 + 0.5);\n let b = u32(clamp(c.b, 0.0, 1.0) * 255.0 + 0.5);\n dst[gid.y * size.x + gid.x] = r | (g << 8u) | (b << 16u) | (255u << 24u);\n}\n", p = "\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var<storage, read_write> dst: array<u32>;\n\n@compute @workgroup_size(8, 8)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let size = textureDimensions(src);\n if ( gid.x >= size.x || gid.y >= size.y ) { return; }\n let c = textureLoad(src, vec2<i32>(gid.xy), 0);\n let a = (gid.y * size.x + gid.x) * 2u;\n dst[a] = pack2x16float(c.rg);\n dst[a + 1u] = pack2x16float(vec2<f32>(c.b, 1.0));\n}\n", m = class e {
54
+ constructor(e, t) {
55
+ this.chain = e, this.canvas = t, this.slot = 0, this.source = null, this.staging = null, this._readbackPipeline = null, this._readbackStorage = null, this._readbackMap = null, this._hdrPipeline = null, this._hdrStorage = null, this._hdrMap = null, this._toneMapper = null, this.disposed = !1;
56
+ }
57
+ get device() {
58
+ return this.chain.device;
59
+ }
60
+ get width() {
61
+ return this.chain.pipeline.geometry.validWidth;
62
+ }
63
+ get height() {
64
+ return this.chain.pipeline.geometry.validHeight;
65
+ }
66
+ static async create({ width: t, height: n, canvas: r = null, settings: i = {}, onProgress: a = () => {} }) {
67
+ if (!Number.isInteger(t) || !Number.isInteger(n) || t < 1 || n < 1) throw Error("DLSSNeural.create: width and height must be positive integers");
68
+ if (t * n > 4194304) throw Error(`DLSSNeural.create: ${t}x${n} is ${(t * n / 1e6).toFixed(1)} MP, above the ${(c / 1e6).toFixed(1)} MP this pass survives. Past it the run takes minutes and then loses every GPU device in the page.`);
69
+ let s = r ?? document.createElement("canvas");
70
+ r || (s.style.cssText = "position:fixed;left:-10000px;top:0;pointer-events:none");
71
+ let l = new e(await (await d()).create(s, t, n, a, o(i).runtime), s);
72
+ return l._allocate(), l;
73
+ }
74
+ _allocate() {
75
+ let { validWidth: e, validHeight: t } = this.chain.pipeline.geometry;
76
+ this.source = this.device.createBuffer({
77
+ label: "rayzee:dlss-nr-source",
78
+ size: e * t * 8,
79
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
80
+ }), this.staging = new Uint16Array(e * t * 4);
81
+ let n = e * t * 4;
82
+ this._readbackStorage = this.device.createBuffer({
83
+ label: "rayzee:dlss-nr-readback",
84
+ size: n,
85
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
86
+ }), this._readbackMap = this.device.createBuffer({
87
+ label: "rayzee:dlss-nr-readback-map",
88
+ size: n,
89
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
90
+ }), this._readbackPipeline = this.device.createComputePipeline({
91
+ label: "rayzee:dlss-nr-readback",
92
+ layout: "auto",
93
+ compute: {
94
+ module: this.device.createShaderModule({ code: f }),
95
+ entryPoint: "main"
96
+ }
97
+ });
98
+ let r = e * t * 8;
99
+ this._hdrStorage = this.device.createBuffer({
100
+ label: "rayzee:dlss-nr-hdr",
101
+ size: r,
102
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
103
+ }), this._hdrMap = this.device.createBuffer({
104
+ label: "rayzee:dlss-nr-hdr-map",
105
+ size: r,
106
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
107
+ }), this._hdrPipeline = this.device.createComputePipeline({
108
+ label: "rayzee:dlss-nr-hdr",
109
+ layout: "auto",
110
+ compute: {
111
+ module: this.device.createShaderModule({ code: p }),
112
+ entryPoint: "main"
113
+ }
114
+ });
115
+ }
116
+ async _packHDR(e, t, n = !1) {
117
+ let r = this.chain.pipeline.slots[this.slot]?.hdrOutput;
118
+ if (!r) throw Error("DLSSNeural: the runtime has no scene-referred output. Either no frame has been produced yet, or dlss-runtime.js was re-vendored without the patch described in its PATCHES.md.");
119
+ let i = this.device.createBindGroup({
120
+ layout: this._hdrPipeline.getBindGroupLayout(0),
121
+ entries: [{
122
+ binding: 0,
123
+ resource: r.createView()
124
+ }, {
125
+ binding: 1,
126
+ resource: { buffer: this._hdrStorage }
127
+ }]
128
+ }), a = this.device.createCommandEncoder({ label: "rayzee:dlss-nr-hdr" }), o = a.beginComputePass();
129
+ o.setPipeline(this._hdrPipeline), o.setBindGroup(0, i), o.dispatchWorkgroups(Math.ceil(e / 8), Math.ceil(t / 8)), o.end(), n && a.copyBufferToBuffer(this._hdrStorage, 0, this._hdrMap, 0, e * t * 8), this.device.queue.submit([a.finish()]);
130
+ }
131
+ async readOutputHDR() {
132
+ let { validWidth: e, validHeight: t } = this.chain.pipeline.geometry;
133
+ await this._packHDR(e, t, !0), await this._hdrMap.mapAsync(GPUMapMode.READ);
134
+ let n = new Uint16Array(this._hdrMap.getMappedRange().slice(0));
135
+ return this._hdrMap.unmap(), {
136
+ half: n,
137
+ width: e,
138
+ height: t
139
+ };
140
+ }
141
+ async readOutputToned(e) {
142
+ let { validWidth: t, validHeight: n } = this.chain.pipeline.geometry;
143
+ return await this._packHDR(t, n), this._toneMapper ??= new r(this.device, "rayzee:dlss-nr-tonemap"), this._toneMapper.ensureSize(t, n), {
144
+ rgba8: await this._toneMapper.toRGBA8(this._hdrStorage, {
145
+ exposure: e.exposure ?? 1,
146
+ toneMapping: e.toneMapping ?? 0,
147
+ saturation: e.saturation ?? 1
148
+ }),
149
+ width: t,
150
+ height: n
151
+ };
152
+ }
153
+ async readOutput() {
154
+ let { validWidth: e, validHeight: t } = this.chain.pipeline.geometry, n = this.chain.pipeline.slots[this.slot]?.output;
155
+ if (!n) throw Error("DLSSNeural.readOutput: the pass has not produced a frame yet");
156
+ let r = this.device.createBindGroup({
157
+ layout: this._readbackPipeline.getBindGroupLayout(0),
158
+ entries: [{
159
+ binding: 0,
160
+ resource: n.createView()
161
+ }, {
162
+ binding: 1,
163
+ resource: { buffer: this._readbackStorage }
164
+ }]
165
+ }), i = this.device.createCommandEncoder({ label: "rayzee:dlss-nr-readback" }), a = i.beginComputePass();
166
+ a.setPipeline(this._readbackPipeline), a.setBindGroup(0, r), a.dispatchWorkgroups(Math.ceil(e / 8), Math.ceil(t / 8)), a.end(), i.copyBufferToBuffer(this._readbackStorage, 0, this._readbackMap, 0, e * t * 4), this.device.queue.submit([i.finish()]), await this._readbackMap.mapAsync(GPUMapMode.READ);
167
+ let o = new Uint8ClampedArray(this._readbackMap.getMappedRange().slice(0));
168
+ return this._readbackMap.unmap(), o;
169
+ }
170
+ dispose() {
171
+ if (!this.disposed) {
172
+ this.disposed = !0, this.source?.destroy(), this._readbackStorage?.destroy(), this._readbackMap?.destroy(), this._hdrStorage?.destroy(), this._hdrMap?.destroy(), this._toneMapper?.dispose();
173
+ try {
174
+ this.chain.destroy();
175
+ } catch (e) {
176
+ s.warn("retouch teardown:", e);
177
+ }
178
+ }
179
+ }
180
+ }, h = 20;
181
+ async function g({ source: e, settings: t = {}, instance: n = null, exposure: r = 1, tone: i = null, onProgress: a = () => {} }) {
182
+ let s = n;
183
+ s && (s.width !== e.width || s.height !== e.height) && (s.dispose(), s = null), s ||= await m.create({
184
+ width: e.width,
185
+ height: e.height,
186
+ settings: t,
187
+ onProgress: a
188
+ });
189
+ let { runtime: c, colorStrength: l } = o(t), u = s.staging;
190
+ if (!e.half || e.half.length !== u.length) throw Error(`DLSSNeural: expected ${u.length} packed halfs, got ${e.half?.length}`);
191
+ u.set(e.half), s.device.queue.writeBuffer(s.source, 0, u), s.chain.pipeline.paperWhite = 1 / Math.min(Math.max(r, 1 / h), h), s.chain.pipeline.colorStrength = l;
192
+ let d = performance.now(), f = s.device.createCommandEncoder({ label: "rayzee:dlss-nr" });
193
+ await s.chain.encode(f, s.slot, { buffer: s.source }, c, () => {}, { reset: !0 }), s.device.queue.submit([f.finish()]), await s.device.queue.onSubmittedWorkDone();
194
+ let p = i ? await s.readOutputToned(i) : await s.readOutputHDR();
195
+ return {
196
+ instance: s,
197
+ ...p,
198
+ ms: performance.now() - d
199
+ };
200
+ }
201
+ //#endregion
202
+ export { l as i, a as n, c as r, i as t };
@@ -0,0 +1,266 @@
1
+ import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
2
+ import { o as t, r as n, t as r } from "./ToneMapGPU-DzqSyzTx.js";
3
+ //#region src/EngineEvents.js
4
+ var i = {
5
+ RENDER_COMPLETE: "engine:renderComplete",
6
+ RENDER_RESET: "engine:renderReset",
7
+ FRAME: "engine:frame",
8
+ DENOISING_START: "engine:denoisingStart",
9
+ DENOISING_END: "engine:denoisingEnd",
10
+ UPSCALING_START: "engine:upscalingStart",
11
+ UPSCALING_PROGRESS: "engine:upscalingProgress",
12
+ UPSCALING_END: "engine:upscalingEnd",
13
+ LOADING_UPDATE: "engine:loadingUpdate",
14
+ LOADING_RESET: "engine:loadingReset",
15
+ STATS_UPDATE: "engine:statsUpdate",
16
+ OBJECT_SELECTED: "engine:objectSelected",
17
+ OBJECT_DESELECTED: "engine:objectDeselected",
18
+ OBJECT_DOUBLE_CLICKED: "engine:objectDoubleClicked",
19
+ SELECT_MODE_CHANGED: "engine:selectModeChanged",
20
+ OBJECT_TRANSFORM_START: "engine:objectTransformStart",
21
+ OBJECT_TRANSFORM_END: "engine:objectTransformEnd",
22
+ TRANSFORM_MODE_CHANGED: "engine:transformModeChanged",
23
+ AUTO_FOCUS_UPDATED: "engine:autoFocusUpdated",
24
+ AUTO_EXPOSURE_UPDATED: "engine:autoExposureUpdated",
25
+ AF_POINT_PLACED: "engine:afPointPlaced",
26
+ SETTING_CHANGED: "engine:settingChanged",
27
+ ANIMATION_STARTED: "engine:animationStarted",
28
+ ANIMATION_PAUSED: "engine:animationPaused",
29
+ ANIMATION_STOPPED: "engine:animationStopped",
30
+ ANIMATION_FINISHED: "engine:animationFinished",
31
+ VIDEO_RENDER_PROGRESS: "engine:videoRenderProgress",
32
+ VIDEO_RENDER_COMPLETE: "engine:videoRenderComplete",
33
+ DISPOSE: "engine:dispose",
34
+ DEVICE_LOST: "engine:deviceLost",
35
+ ISSUE: "engine:issue",
36
+ SCENE_METADATA_APPLIED: "engine:sceneMetadataApplied"
37
+ }, a = /* @__PURE__ */ e({
38
+ DLSSSuperRes: () => g,
39
+ SR_MAX_INPUT: () => c,
40
+ SR_SCALE: () => 2,
41
+ presentRGBA8: () => b,
42
+ readDenoisedHalf: () => h,
43
+ releaseDenoisedReader: () => m,
44
+ renderUpscaled: () => v
45
+ }), o = 2, s = t("dlss"), c = 2048, l = null;
46
+ function u() {
47
+ if (l) return l;
48
+ let { dlssRuntimeUrl: e, dlssAssetBaseUrl: t } = n();
49
+ return globalThis.__DLSS5_ASSET_BASE__ = t, l = new Promise((t, n) => {
50
+ if (globalThis.DLSSRuntime?.getNativeSR) return t(globalThis.DLSSRuntime.getNativeSR());
51
+ let r = document.createElement("script");
52
+ r.src = e, r.onload = () => {
53
+ let e = globalThis.DLSSRuntime?.getNativeSR;
54
+ if (!e) return n(/* @__PURE__ */ Error("DLSS runtime loaded but exposed no super-resolution entry"));
55
+ t(e());
56
+ }, r.onerror = () => n(/* @__PURE__ */ Error(`Failed to load the DLSS runtime from ${e}`)), document.head.appendChild(r);
57
+ }), l;
58
+ }
59
+ var d = "\n@group(0) @binding(0) var src: texture_2d<f32>;\n@group(0) @binding(1) var<storage, read_write> dst: array<u32>;\n\n@compute @workgroup_size(8, 8)\nfn main( @builtin(global_invocation_id) gid: vec3<u32> ) {\n let size = textureDimensions( src );\n if ( gid.x >= size.x || gid.y >= size.y ) { return; }\n let c = textureLoad( src, vec2<i32>( gid.xy ), 0 ).rgb;\n let a = ( gid.y * size.x + gid.x ) * 2u;\n dst[ a ] = pack2x16float( c.rg );\n dst[ a + 1u ] = pack2x16float( vec2<f32>( c.b, 1.0 ) );\n}\n", f = null;
60
+ function p(e, t, n) {
61
+ if (f && (f.device !== e || f.width !== t || f.height !== n) && m(), f) return f;
62
+ let r = t * n * 8;
63
+ return f = {
64
+ device: e,
65
+ width: t,
66
+ height: n,
67
+ storage: e.createBuffer({
68
+ label: "rayzee:dlss-sr-pack",
69
+ size: r,
70
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
71
+ }),
72
+ map: e.createBuffer({
73
+ label: "rayzee:dlss-sr-pack-map",
74
+ size: r,
75
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
76
+ }),
77
+ pipeline: e.createComputePipeline({
78
+ label: "rayzee:dlss-sr-pack",
79
+ layout: "auto",
80
+ compute: {
81
+ module: e.createShaderModule({
82
+ label: "rayzee:dlss-sr-pack",
83
+ code: d
84
+ }),
85
+ entryPoint: "main"
86
+ }
87
+ })
88
+ }, f;
89
+ }
90
+ function m() {
91
+ f?.storage.destroy(), f?.map.destroy(), f = null;
92
+ }
93
+ async function h(e) {
94
+ let t = e?._outGPUTexture;
95
+ if (!t) throw Error("DLSSSuperRes: no denoised frame — enable OIDN and finish a render first");
96
+ let n = e.gpuDevice, { width: r, height: i } = e._outTexSize, a = p(n, r, i), o = n.createBindGroup({
97
+ layout: a.pipeline.getBindGroupLayout(0),
98
+ entries: [{
99
+ binding: 0,
100
+ resource: t.createView()
101
+ }, {
102
+ binding: 1,
103
+ resource: { buffer: a.storage }
104
+ }]
105
+ }), s = n.createCommandEncoder({ label: "rayzee:dlss-sr-pack" }), c = s.beginComputePass();
106
+ c.setPipeline(a.pipeline), c.setBindGroup(0, o), c.dispatchWorkgroups(Math.ceil(r / 8), Math.ceil(i / 8)), c.end(), s.copyBufferToBuffer(a.storage, 0, a.map, 0, r * i * 8), n.queue.submit([s.finish()]), await a.map.mapAsync(GPUMapMode.READ);
107
+ let l = new Uint16Array(a.map.getMappedRange().slice(0));
108
+ return a.map.unmap(), {
109
+ half: l,
110
+ width: r,
111
+ height: i
112
+ };
113
+ }
114
+ var g = class e {
115
+ constructor(e, t) {
116
+ this.sr = e, this.canvas = t, this.slot = 0, this._color = null, this._depth = null, this._motion = null, this._toneMapper = null, this.disposed = !1;
117
+ }
118
+ get geometry() {
119
+ return this.sr.geometry;
120
+ }
121
+ get inputWidth() {
122
+ return this.sr.geometry.width;
123
+ }
124
+ get inputHeight() {
125
+ return this.sr.geometry.height;
126
+ }
127
+ get outputWidth() {
128
+ return this.sr.geometry.outputWidth;
129
+ }
130
+ get outputHeight() {
131
+ return this.sr.geometry.outputHeight;
132
+ }
133
+ static async create({ width: t, height: n, onProgress: r = () => {} } = {}) {
134
+ if (!Number.isInteger(t) || !Number.isInteger(n) || t < 1 || n < 1) throw Error("DLSSSuperRes.create: width and height must be positive integers");
135
+ if (t > 2048 || n > 2048) throw Error(`DLSSSuperRes.create: input capped at ${c}px per side (${c * 2}px output), got ${t}x${n}. Above that the runtime's history plane exceeds maxTextureDimension2D and it fails later with an unrelated bind-group error.`);
136
+ let i = await u(), a = document.createElement("canvas");
137
+ a.style.cssText = "position:fixed;left:-10000px;top:0;pointer-events:none", document.body.appendChild(a);
138
+ let o;
139
+ try {
140
+ o = await i.create(a, {
141
+ width: t,
142
+ height: n,
143
+ onProgress: r,
144
+ timestamps: !0,
145
+ nrSettings: null
146
+ });
147
+ } catch (e) {
148
+ throw a.remove(), e;
149
+ }
150
+ let s = new e(o, a);
151
+ return s._allocate(), s;
152
+ }
153
+ _allocate() {
154
+ let e = this.inputWidth, t = this.inputHeight;
155
+ this._color = new Uint16Array(e * t * 4), this._depth = new Float32Array(e * t), this._motion = new Uint16Array(e * t * 2);
156
+ }
157
+ async _run(e) {
158
+ if (this.disposed) throw Error("DLSSSuperRes: disposed");
159
+ let t = this.inputWidth, n = this.inputHeight;
160
+ if (e.width !== t || e.height !== n) throw Error(`DLSSSuperRes: expected a ${t}x${n} source, got ${e.width}x${e.height}`);
161
+ let r = this._color;
162
+ if (!e.half || e.half.length !== r.length) throw Error(`DLSSSuperRes: expected ${r.length} packed halfs from readDenoisedHalf, got ${e.half?.length}`);
163
+ r.set(e.half), this._depth.fill(.5), this._motion.fill(0);
164
+ let i = performance.now(), a = await this.sr.render(this.slot, {
165
+ color: r,
166
+ depth: this._depth,
167
+ motion: this._motion,
168
+ reset: !0,
169
+ jitter: [0, 0]
170
+ });
171
+ return await this.sr.device.queue.onSubmittedWorkDone(), {
172
+ result: a,
173
+ ms: performance.now() - i
174
+ };
175
+ }
176
+ async upscaleToRGBA8(e, t = {}) {
177
+ let { result: n, ms: i } = await this._run(e), { outputWidth: a, outputHeight: o } = this.geometry;
178
+ this._toneMapper ??= new r(this.sr.device, "rayzee:dlss-sr-tonemap"), this._toneMapper.ensureSize(a, o);
179
+ let s = performance.now();
180
+ return {
181
+ rgba8: await this._toneMapper.toRGBA8(n.hdr.buffer ?? n.hdr, {
182
+ exposure: t.exposure ?? 1,
183
+ toneMapping: t.toneMapping ?? 0,
184
+ saturation: t.saturation ?? 1,
185
+ flipY: !0
186
+ }),
187
+ width: a,
188
+ height: o,
189
+ ms: i,
190
+ toneMs: performance.now() - s
191
+ };
192
+ }
193
+ dispose() {
194
+ if (!this.disposed) {
195
+ this.disposed = !0, this._toneMapper?.dispose(), this._toneMapper = null, this.canvas.remove();
196
+ try {
197
+ this.sr.destroy();
198
+ } catch (e) {
199
+ s.warn("super resolution teardown:", e);
200
+ }
201
+ }
202
+ }
203
+ };
204
+ function _(e, t, n) {
205
+ return new Promise((r, a) => {
206
+ let o = !1, s = (t, n) => {
207
+ o || (o = !0, e.removeEventListener(i.DENOISING_END, l), clearInterval(u), clearTimeout(d), t ? r() : a(n));
208
+ }, c = !1, l = () => {
209
+ (e.stages?.pathTracer?.frameCount ?? 0) >= t && (c = !0);
210
+ };
211
+ e.addEventListener(i.DENOISING_END, l);
212
+ let u = setInterval(() => {
213
+ c && s(!0);
214
+ }, 200), d = setTimeout(() => s(!1, /* @__PURE__ */ Error("DLSSSuperRes: timed out waiting for the render to finish")), n);
215
+ });
216
+ }
217
+ async function v(e, { outputWidth: t, outputHeight: n, samples: r = 64, upscaler: i = null, timeoutMs: a = 6e5, present: o = !0, onProgress: s = () => {} } = {}) {
218
+ if (t % 2 || n % 2) throw Error(`renderUpscaled: output must be divisible by 2, got ${t}x${n}`);
219
+ let c = t / 2, l = n / 2, u = e.stages?.pathTracer, d = {
220
+ width: u?.width,
221
+ height: u?.height,
222
+ maxSamples: e.settings?.get?.("maxSamples"),
223
+ finalDenoise: e.denoisingManager?.finalDenoise,
224
+ paused: e.pauseRendering
225
+ }, f = !i, p = i, m = {};
226
+ try {
227
+ if (!p) {
228
+ let e = performance.now();
229
+ s("Loading the super-resolution model"), p = await g.create({
230
+ width: c,
231
+ height: l,
232
+ onProgress: s
233
+ }), m.load = performance.now() - e;
234
+ } else if (p.inputWidth !== c || p.inputHeight !== l) throw Error(`renderUpscaled: upscaler is ${p.inputWidth}x${p.inputHeight}, need ${c}x${l}`);
235
+ let i = performance.now();
236
+ s(`Tracing ${c}x${l} at ${r} spp`), e.pauseRendering = !1, e.setCanvasSize(c, l), await new Promise((e) => setTimeout(e, 300)), e.denoisingManager.applyOIDNEnabled(!0), e.settings.set("maxSamples", r);
237
+ let u = _(e, r, a);
238
+ e.reset(), await u, e.pauseRendering = !0, m.render = performance.now() - i, s("Reading the denoised frame");
239
+ let d = performance.now(), f = await h(e.denoisingManager?.denoiser);
240
+ m.read = performance.now() - d, s(`Upscaling to ${t}x${n}`);
241
+ let v = {
242
+ exposure: e.renderer.toneMappingExposure,
243
+ toneMapping: e.renderer.toneMapping,
244
+ saturation: e.settings.get("saturation") ?? 1
245
+ }, { rgba8: b, width: x, height: S, ms: C, toneMs: w } = await p.upscaleToRGBA8(f, v);
246
+ return m.upscale = C, m.tonemap = w, o && y(e.denoisingManager?.upscalerCanvas, b, x, S), {
247
+ rgba8: b,
248
+ width: x,
249
+ height: S,
250
+ timings: m,
251
+ upscaler: p
252
+ };
253
+ } catch (e) {
254
+ throw f && p?.dispose(), e;
255
+ } finally {
256
+ d.width && d.height && e.setCanvasSize(d.width, d.height), d.maxSamples !== void 0 && e.settings?.set?.("maxSamples", d.maxSamples), d.finalDenoise !== void 0 && e.denoisingManager?.applyOIDNEnabled(d.finalDenoise), e.pauseRendering = d.paused;
257
+ }
258
+ }
259
+ function y(e, t, n, r) {
260
+ return e ? (e.width = n, e.height = r, e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(t.buffer ?? t), n, r), 0, 0), e.style.display = "block", !0) : !1;
261
+ }
262
+ function b(e, t, n, r) {
263
+ return y(e, t, n, r);
264
+ }
265
+ //#endregion
266
+ export { i as a, v as i, c as n, o as r, a as t };