oidn-webgpu 0.0.0-stage → 0.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 (95) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/LICENSE +21 -0
  3. package/README.md +89 -2
  4. package/dist/oidn.js +6140 -0
  5. package/dist/oidn.umd.cjs +1513 -0
  6. package/lib/UNet.d.ts +161 -0
  7. package/lib/UNet.js +536 -0
  8. package/lib/UNet.js.map +1 -0
  9. package/lib/WGPUComputePass.d.ts +62 -0
  10. package/lib/WGPUComputePass.js +278 -0
  11. package/lib/WGPUComputePass.js.map +1 -0
  12. package/lib/WGPUFullQuadPass.d.ts +50 -0
  13. package/lib/WGPUFullQuadPass.js +261 -0
  14. package/lib/WGPUFullQuadPass.js.map +1 -0
  15. package/lib/backend.d.ts +1 -0
  16. package/lib/backend.js +39 -0
  17. package/lib/backend.js.map +1 -0
  18. package/lib/finalRgbShader.d.ts +13 -0
  19. package/lib/finalRgbShader.js +160 -0
  20. package/lib/finalRgbShader.js.map +1 -0
  21. package/lib/gpuTypes.d.ts +158 -0
  22. package/lib/gpuTypes.js +27 -0
  23. package/lib/gpuTypes.js.map +1 -0
  24. package/lib/graphOptimizer.d.ts +54 -0
  25. package/lib/graphOptimizer.js +216 -0
  26. package/lib/graphOptimizer.js.map +1 -0
  27. package/lib/hdr.d.ts +31 -0
  28. package/lib/hdr.js +340 -0
  29. package/lib/hdr.js.map +1 -0
  30. package/lib/hdrTransfer.d.ts +22 -0
  31. package/lib/hdrTransfer.js +128 -0
  32. package/lib/hdrTransfer.js.map +1 -0
  33. package/lib/main.d.ts +57 -0
  34. package/lib/main.js +24 -0
  35. package/lib/main.js.map +1 -0
  36. package/lib/modelSpec.d.ts +80 -0
  37. package/lib/modelSpec.js +270 -0
  38. package/lib/modelSpec.js.map +1 -0
  39. package/lib/native/denoise.d.ts +25 -0
  40. package/lib/native/denoise.js +223 -0
  41. package/lib/native/denoise.js.map +1 -0
  42. package/lib/native/program.d.ts +53 -0
  43. package/lib/native/program.js +310 -0
  44. package/lib/native/program.js.map +1 -0
  45. package/lib/native/recordingDevice.d.ts +125 -0
  46. package/lib/native/recordingDevice.js +247 -0
  47. package/lib/native/recordingDevice.js.map +1 -0
  48. package/lib/nativeUNet.d.ts +25 -0
  49. package/lib/nativeUNet.js +184 -0
  50. package/lib/nativeUNet.js.map +1 -0
  51. package/lib/nativeUNetCore.d.ts +202 -0
  52. package/lib/nativeUNetCore.js +2945 -0
  53. package/lib/nativeUNetCore.js.map +1 -0
  54. package/lib/process.d.ts +37 -0
  55. package/lib/process.js +328 -0
  56. package/lib/process.js.map +1 -0
  57. package/lib/resourceTracker.d.ts +26 -0
  58. package/lib/resourceTracker.js +65 -0
  59. package/lib/resourceTracker.js.map +1 -0
  60. package/lib/sanitize.d.ts +15 -0
  61. package/lib/sanitize.js +28 -0
  62. package/lib/sanitize.js.map +1 -0
  63. package/lib/tileScheduler.d.ts +61 -0
  64. package/lib/tileScheduler.js +199 -0
  65. package/lib/tileScheduler.js.map +1 -0
  66. package/lib/tza.d.ts +15 -0
  67. package/lib/tza.js +114 -0
  68. package/lib/tza.js.map +1 -0
  69. package/lib/webnnUNet.d.ts +52 -0
  70. package/lib/webnnUNet.js +535 -0
  71. package/lib/webnnUNet.js.map +1 -0
  72. package/package.json +43 -4
  73. package/src/UNet.ts +861 -0
  74. package/src/WGPUComputePass.ts +382 -0
  75. package/src/WGPUFullQuadPass.ts +348 -0
  76. package/src/backend.ts +42 -0
  77. package/src/finalRgbShader.ts +186 -0
  78. package/src/gpuTypes.ts +179 -0
  79. package/src/graphOptimizer.ts +301 -0
  80. package/src/hdr.ts +398 -0
  81. package/src/hdrTransfer.ts +169 -0
  82. package/src/main.ts +126 -0
  83. package/src/modelSpec.ts +414 -0
  84. package/src/native/core-profile.json +40 -0
  85. package/src/native/denoise.ts +268 -0
  86. package/src/native/program.ts +326 -0
  87. package/src/native/recordingDevice.ts +341 -0
  88. package/src/nativeUNet.ts +269 -0
  89. package/src/nativeUNetCore.ts +3735 -0
  90. package/src/process.ts +368 -0
  91. package/src/resourceTracker.ts +94 -0
  92. package/src/sanitize.ts +29 -0
  93. package/src/tileScheduler.ts +330 -0
  94. package/src/tza.ts +136 -0
  95. package/src/webnnUNet.ts +812 -0
package/src/hdr.ts ADDED
@@ -0,0 +1,398 @@
1
+ // https://github.com/bbbbx/hdr.js/blob/main/src/hdr.ts
2
+ function frexp(v: number): { f: number; e: number } {
3
+ v = Number(v);
4
+ const result = { f: v, e: 0 };
5
+
6
+ if (v !== 0 && Number.isFinite(v)) {
7
+ const absV = Math.abs(v);
8
+ const log2 =
9
+ Math.log2 ||
10
+ function log2(n) {
11
+ return Math.log(n) * Math.LOG2E;
12
+ };
13
+
14
+ // Math.pow(2, -exp) === Infinity when exp <= -1024
15
+ let e = Math.max(-1023, Math.floor(log2(absV)) + 1);
16
+ let f = absV * Math.pow(2.0, -e);
17
+
18
+ while (f >= 1.0) {
19
+ f *= 0.5;
20
+ e++;
21
+ }
22
+ while (f < 0.5) {
23
+ f *= 2;
24
+ e--;
25
+ }
26
+
27
+ if (v < 0) {
28
+ f = -f;
29
+ }
30
+
31
+ result.f = f;
32
+ result.e = e;
33
+ }
34
+
35
+ return result;
36
+ }
37
+
38
+ function ldexp(f: number, e: number) {
39
+ return f * Math.pow(2.0, e);
40
+ }
41
+
42
+ /**
43
+ * Convert 4 byte uint8 buffer to 3 channels float data
44
+ * @param rgbe input uint8 buffer
45
+ * @param float output float data
46
+ */
47
+ function rgbe2float(rgbe: Uint8Array, float: Float32Array) {
48
+ if (rgbe[3] !== 0) {
49
+ const f1 = ldexp(1.0, rgbe[3] - (128 + 8));
50
+ float[0] = rgbe[0] * f1;
51
+ float[1] = rgbe[1] * f1;
52
+ float[2] = rgbe[2] * f1;
53
+ } else {
54
+ float[0] = float[1] = float[2] = 0.0;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Convert 3 channels float data to 4 byte uint8 buffer
60
+ * @param float input float data
61
+ * @param rgbe output uint8 buffer
62
+ */
63
+ function float2rgbe(float: Float32Array, rgbe: Uint8Array) {
64
+ const red = float[0];
65
+ const green = float[1];
66
+ const blue = float[2];
67
+ const v = Math.max(red, Math.max(green, blue));
68
+ if (v < 1e-32) {
69
+ rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
70
+ } else {
71
+ const { f, e } = frexp(v);
72
+ const s = (f / v) * 256.0;
73
+ rgbe[0] = red * s;
74
+ rgbe[1] = green * s;
75
+ rgbe[2] = blue * s;
76
+ rgbe[3] = e + 128;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Write float data to RGBE(.hdr) file buffer
82
+ * @param w image width
83
+ * @param h image height
84
+ * @param data float data, RGB 3 channels.
85
+ * @returns file buffer
86
+ */
87
+ function write_hdr(w: number, h: number, data: Float32Array): Uint8Array {
88
+ const s: number[] = [];
89
+ const comp = 3;
90
+ write_hdr_core(s, w, h, comp, data);
91
+ return new Uint8Array(s);
92
+ }
93
+
94
+ function write_hdr_core(
95
+ s: number[],
96
+ x: number,
97
+ y: number,
98
+ comp: number,
99
+ data: Float32Array
100
+ ): void {
101
+ if (y <= 0 || x <= 0 || !data) {
102
+ return;
103
+ }
104
+
105
+ const header =
106
+ '#?RADIANCE\n' +
107
+ 'FORMAT=32-bit_rle_rgbe\n' +
108
+ 'EXPOSURE=1.0\n' +
109
+ '\n' +
110
+ '-Y ' +
111
+ y +
112
+ ' +X ' +
113
+ x +
114
+ '\n';
115
+ header.split('').forEach((c) => {
116
+ const charCode = c.charCodeAt(0);
117
+ s.push(charCode);
118
+ });
119
+
120
+ const scratch = new Uint8Array(x * 4);
121
+ const stbi__flip_vertically_on_write = false;
122
+ for (let i = 0; i < y; i++) {
123
+ write_hdr_scanline(
124
+ s,
125
+ x,
126
+ comp,
127
+ scratch,
128
+ data.subarray(comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i))
129
+ );
130
+ }
131
+ }
132
+
133
+ function write_hdr_scanline(
134
+ s: number[],
135
+ width: number,
136
+ ncomp: number,
137
+ scratch: Uint8Array,
138
+ scanline: Float32Array
139
+ ): void {
140
+ const scanlineheader = new Uint8Array([2, 2, 0, 0]);
141
+ const rgbe = new Uint8Array(4);
142
+ const linear = new Float32Array(3);
143
+ let x;
144
+
145
+ scanlineheader[2] = (width & 0xff00) >> 8;
146
+ scanlineheader[3] = width & 0x00ff;
147
+
148
+ /* skip RLE for images too small or large */
149
+ if (width < 8 || width >= 32768) {
150
+ for (x = 0; x < width; x++) {
151
+ switch (ncomp) {
152
+ case 4: /* fallthrough */
153
+ case 3:
154
+ linear[2] = scanline[x * ncomp + 2];
155
+ linear[1] = scanline[x * ncomp + 1];
156
+ linear[0] = scanline[x * ncomp + 0];
157
+ break;
158
+ default:
159
+ linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
160
+ break;
161
+ }
162
+ float2rgbe(linear, rgbe);
163
+ s.push(rgbe[0], rgbe[1], rgbe[2], rgbe[3]);
164
+ }
165
+ } else {
166
+ let c, r;
167
+ /* encode into scratch buffer */
168
+ for (x = 0; x < width; x++) {
169
+ switch (ncomp) {
170
+ case 4: /* fallthrough */
171
+ case 3:
172
+ linear[2] = scanline[x * ncomp + 2];
173
+ linear[1] = scanline[x * ncomp + 1];
174
+ linear[0] = scanline[x * ncomp + 0];
175
+ break;
176
+ default:
177
+ linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
178
+ break;
179
+ }
180
+ float2rgbe(linear, rgbe);
181
+ scratch[x + width * 0] = rgbe[0];
182
+ scratch[x + width * 1] = rgbe[1];
183
+ scratch[x + width * 2] = rgbe[2];
184
+ scratch[x + width * 3] = rgbe[3];
185
+ }
186
+
187
+ s.push(
188
+ scanlineheader[0],
189
+ scanlineheader[1],
190
+ scanlineheader[2],
191
+ scanlineheader[3]
192
+ );
193
+
194
+ /* RLE each component separately */
195
+ for (c = 0; c < 4; c++) {
196
+ const comp = scratch.subarray(width * c);
197
+
198
+ x = 0;
199
+ while (x < width) {
200
+ // find first run
201
+ r = x;
202
+ while (r + 2 < width) {
203
+ if (comp[r] === comp[r + 1] && comp[r] === comp[r + 2]) break;
204
+ ++r;
205
+ }
206
+ if (r + 2 >= width) r = width;
207
+ // dump up to first run
208
+ while (x < r) {
209
+ let len = r - x;
210
+ if (len > 128) len = 128;
211
+ write_dump_data(s, len, comp.subarray(x));
212
+ x += len;
213
+ }
214
+ // if there's a run, output it
215
+ if (r + 2 < width) {
216
+ // same test as what we break out of in search loop, so only true if we break'd
217
+ // find next byte after run
218
+ while (r < width && comp[r] == comp[x]) ++r;
219
+ // output run up to r
220
+ while (x < r) {
221
+ let len = r - x;
222
+ if (len > 127) len = 127;
223
+ write_run_data(s, len, comp[x]);
224
+ x += len;
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ function write_dump_data(s: number[], length: number, data: Uint8Array): void {
233
+ const lengthbyte = length & 0xff;
234
+ if (!(length <= 128)) throw new Error('length is greater than 128');
235
+ s.push(lengthbyte);
236
+ for (let i = 0; i < length; i++) {
237
+ s.push(data[i]);
238
+ }
239
+ }
240
+
241
+ function write_run_data(s: number[], length: number, databyte: number): void {
242
+ const lengthbyte = (length + 128) & 0xff;
243
+ if (!(length + 128 <= 255)) throw new Error('length is greater than 128');
244
+ s.push(lengthbyte, databyte);
245
+ }
246
+
247
+ function testMagic(uint8: Uint8Array, signature: string): boolean {
248
+ const l = signature.length;
249
+ for (let i = 0; i < l; i++) {
250
+ if (uint8[i] !== signature.charCodeAt(i)) {
251
+ return false;
252
+ }
253
+ }
254
+ return true;
255
+ }
256
+
257
+ function isHdr(uint8: Uint8Array): boolean {
258
+ let r = testMagic(uint8, '#?RADIANCE\n');
259
+ if (!r) {
260
+ r = testMagic(uint8, '#?RGBE\n');
261
+ }
262
+ return r;
263
+ }
264
+
265
+ const MAX_HEADER_LENGTH = 1024 * 10;
266
+ const MAX_DIMENSIONS = 1 << 24;
267
+
268
+ /**
269
+ * Read a RGBE(.hdr) file from buffer
270
+ * @param uint8 RGBE(.hdr) file buffer
271
+ * @returns Failure reason or resolved data.
272
+ */
273
+ function read_hdr(uint8: Uint8Array):
274
+ | string
275
+ | {
276
+ rgbFloat: Float32Array;
277
+ width: number;
278
+ height: number;
279
+ } {
280
+ let header = '';
281
+ let pos = 0;
282
+
283
+ if (!isHdr(uint8)) {
284
+ return 'Corrupt HDR image.';
285
+ }
286
+
287
+ // read header
288
+ while (!header.match(/\n\n[^\n]+\n/g) && pos < MAX_HEADER_LENGTH) {
289
+ header += String.fromCharCode(uint8[pos++]);
290
+ }
291
+
292
+ // check format
293
+ const format = header.match(/FORMAT=(.*)$/m)?.[1];
294
+ if (format !== '32-bit_rle_rgbe') {
295
+ return 'Unsupported HDR format: ' + format;
296
+ }
297
+
298
+ // parse resolution
299
+ const rez: string[] = header.split(/\n/).reverse()[1].split(' ');
300
+ if (rez[0] !== '-Y' || rez[2] !== '+X') {
301
+ return 'Unsupported HDR format';
302
+ }
303
+ const width = Number.parseFloat(rez[3]);
304
+ const height = Number.parseFloat(rez[1]);
305
+ if (width > MAX_DIMENSIONS || height > MAX_DIMENSIONS) {
306
+ return 'Very large image (corrupt?)';
307
+ }
308
+
309
+ let i, j;
310
+ let c1: number = uint8[pos];
311
+ let c2: number = uint8[pos + 1];
312
+ let len: number = uint8[pos + 2];
313
+
314
+ // not run-length encoded, so we have to actually use THIS data as a decoded
315
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
316
+ const notRLE: boolean = c1 !== 2 || c2 !== 2 || !!(len & 0x80); // not run-length encoded
317
+
318
+ const hdrData = new Float32Array(width * height * 3);
319
+ if (width < 8 || width >= 32768 || notRLE) {
320
+ // 32768: 2^15
321
+ // Read flat data
322
+ for (j = 0; j < height; ++j) {
323
+ for (i = 0; i < width; ++i) {
324
+ const rgbe = uint8.subarray(pos, pos + 4);
325
+ pos += 4;
326
+ const start = (j * width + i) * 3;
327
+ rgbe2float(rgbe, hdrData.subarray(start, start + 3));
328
+ }
329
+ }
330
+ } else {
331
+ // Read RLE-encoded data
332
+ let scanline: Uint8Array | undefined;
333
+ let c1: number;
334
+ let c2: number;
335
+ let len: number;
336
+ for (let j = 0; j < height; j++) {
337
+ c1 = uint8[pos++];
338
+ c2 = uint8[pos++];
339
+ len = uint8[pos++];
340
+ if (c1 !== 2 || c2 !== 2 || len & 0x80) {
341
+ return 'Invalid scanline';
342
+ }
343
+
344
+ len = len << 8;
345
+ len |= uint8[pos++];
346
+ if (len !== width) {
347
+ return 'invalid decoded scanline length';
348
+ }
349
+ if (!scanline) {
350
+ scanline = new Uint8Array(width * 4);
351
+ }
352
+
353
+ let count: number;
354
+ let value: number;
355
+ for (let k = 0; k < 4; k++) {
356
+ let nLeft: number;
357
+ i = 0;
358
+ while ((nLeft = width - i) > 0) {
359
+ count = uint8[pos++];
360
+ if (count > 128) {
361
+ // is RUN
362
+ value = uint8[pos++];
363
+ count -= 128;
364
+ if (count > nLeft) {
365
+ return 'bad RLE data in HDR';
366
+ }
367
+ for (let z = 0; z < count; z++) {
368
+ scanline[i++ * 4 + k] = value;
369
+ }
370
+ } else {
371
+ // is DUMP
372
+ if (count > nLeft) {
373
+ return 'bad RLE data in HDR';
374
+ }
375
+ for (let z = 0; z < count; z++) {
376
+ scanline[i++ * 4 + k] = uint8[pos++];
377
+ }
378
+ }
379
+ }
380
+ }
381
+
382
+ for (let i = 0; i < width; i++) {
383
+ rgbe2float(
384
+ scanline.subarray(i * 4),
385
+ hdrData.subarray((j * width + i) * 3)
386
+ );
387
+ }
388
+ }
389
+ }
390
+
391
+ return {
392
+ rgbFloat: hdrData,
393
+ width: width,
394
+ height: height
395
+ };
396
+ }
397
+
398
+ export { read_hdr as readHDR, write_hdr as writeHDR, float2rgbe, rgbe2float };
@@ -0,0 +1,169 @@
1
+ import { FLT_MAX, sanitize } from './sanitize.js';
2
+
3
+ /** HDR transfer functions used by the OIDN input and output processing passes. */
4
+ export type HDRTransfer = 'pu' | 'log';
5
+
6
+ const a = 1.41283765e3;
7
+ const b = 1.64593172;
8
+ const c = 4.31384981e-1;
9
+ const d = -2.94139609e-3;
10
+ const e = 1.92653254e-1;
11
+ const f = 6.26026094e-3;
12
+ const g = 9.98620152e-1;
13
+ const y0 = 1.5794576e-6;
14
+ const y1 = 3.22087631e-2;
15
+ const x0 = 2.23151711e-3;
16
+ const x1 = 3.70974749e-1;
17
+ const yMax = 65504;
18
+ const puXMax = puForward(yMax);
19
+ const puNormScale = 1 / puXMax;
20
+ const puRcpNormScale = puXMax;
21
+ const logXMax = Math.log(yMax + 1);
22
+ const logNormScale = 1 / logXMax;
23
+
24
+ function puForward(y: number) {
25
+ if (y <= y0) return a * y;
26
+ if (y <= y1) return b * Math.pow(y, c) + d;
27
+ return e * Math.log(y + f) + g;
28
+ }
29
+
30
+ function puInverse(x: number) {
31
+ if (x <= x0) return x / a;
32
+ if (x <= x1) return Math.pow((x - d) / b, 1 / c);
33
+ return Math.exp((x - g) / e) - f;
34
+ }
35
+
36
+ function forward(y: number, transfer: HDRTransfer) {
37
+ return transfer === 'log'
38
+ ? Math.log(y + 1) * logNormScale
39
+ : puForward(y) * puNormScale;
40
+ }
41
+
42
+ function inverse(x: number, transfer: HDRTransfer) {
43
+ return transfer === 'log'
44
+ ? Math.exp(x * logXMax) - 1
45
+ : puInverse(x * puRcpNormScale);
46
+ }
47
+
48
+ export function hdrTransferFuncCPU({
49
+ data,
50
+ channels,
51
+ inputScale,
52
+ transfer = 'pu'
53
+ }: {
54
+ data: Float32Array;
55
+ channels: number;
56
+ inputScale: number;
57
+ transfer?: HDRTransfer;
58
+ }) {
59
+ const newData = new Float32Array(data);
60
+ for (let i = 0; i < newData.length; i += channels) {
61
+ for (let channel = 0; channel < 3; channel++) {
62
+ newData[i + channel] = forward(
63
+ sanitize(newData[i + channel] * inputScale, 0, FLT_MAX),
64
+ transfer
65
+ );
66
+ }
67
+ }
68
+ return newData;
69
+ }
70
+
71
+ export function hdrTransferFuncInverseCPU({
72
+ data,
73
+ channels,
74
+ inputScale,
75
+ transfer = 'pu'
76
+ }: {
77
+ data: Float32Array;
78
+ channels: number;
79
+ inputScale: number;
80
+ transfer?: HDRTransfer;
81
+ }) {
82
+ const newData = new Float32Array(data);
83
+ const outputScale = 1 / inputScale;
84
+ for (let i = 0; i < newData.length; i += channels) {
85
+ for (let channel = 0; channel < 3; channel++) {
86
+ // The network output may contain negative values or NaNs.
87
+ newData[i + channel] = inverse(sanitize(newData[i + channel], 0, FLT_MAX), transfer) * outputScale;
88
+ }
89
+ }
90
+ return newData;
91
+ }
92
+
93
+ // TODO put in gpu
94
+ export function avgLogLum({
95
+ data,
96
+ channels
97
+ }: {
98
+ data: Float32Array;
99
+ channels: number;
100
+ }) {
101
+ let totalLuminance = 0;
102
+
103
+ for (let i = 0; i < data.length; i += channels) {
104
+ const r = sanitize(data[i], 0, FLT_MAX);
105
+ const g = sanitize(data[i + 1], 0, FLT_MAX);
106
+ const b = sanitize(data[i + 2], 0, FLT_MAX);
107
+ const lum = 0.212671 * r + 0.71516 * g + 0.072169 * b;
108
+
109
+ // TODO performance
110
+ // https://github.com/RenderKit/oidn/blob/713ec7838ba650f99e0a896549c0dca5eeb3652d/devices/cpu/cpu_autoexposure.cpp#L43
111
+ totalLuminance += Math.log2(lum + 0.0001);
112
+ }
113
+ const numPixels = data.length / channels;
114
+ const averageLuminance = totalLuminance / numPixels;
115
+ const key = 0.18;
116
+
117
+ // https://github.com/RenderKit/oidn/blob/713ec7838ba650f99e0a896549c0dca5eeb3652d/training/color.py#L173
118
+ return key / Math.pow(2, averageLuminance);
119
+ }
120
+
121
+ const transferConstantsWGSL = `
122
+ const a = ${a};
123
+ const b = ${b};
124
+ const c = ${c};
125
+ const d = ${d};
126
+ const e = ${e};
127
+ const f = ${f};
128
+ const g = ${g};
129
+ const y0 =${y0};
130
+ const y1 =${y1};
131
+ const x0 =${x0};
132
+ const x1 =${x1};
133
+
134
+ const normScale = ${puNormScale};
135
+ const rcpNormScale = ${puRcpNormScale};
136
+ `;
137
+
138
+ /** WGSL declarations of `fn HDRForward(y: f32) -> f32` for the input pass. */
139
+ export function hdrForwardWGSL(transfer: HDRTransfer) {
140
+ const forwardCode = transfer === 'log'
141
+ ? `fn HDRForward(y: f32) -> f32 { return log(y + 1.0) * logNormScale; }`
142
+ : `fn HDRForward(y: f32) -> f32 {
143
+ if (y <= y0) { return a * y * normScale; }
144
+ else if (y <= y1) { return (b * pow(y, c) + d) * normScale; }
145
+ else { return (e * log(y + f) + g) * normScale; }
146
+ }`;
147
+ return /* wgsl */ `
148
+ ${transferConstantsWGSL}
149
+ const logXMax = ${logXMax};
150
+ const logNormScale = ${logNormScale};
151
+ ${forwardCode}`;
152
+ }
153
+
154
+ /** WGSL declarations of `fn HDRInverse(x: f32) -> f32` for the output pass. */
155
+ export function hdrInverseWGSL(transfer: HDRTransfer) {
156
+ const inverseCode = transfer === 'log'
157
+ ? `fn HDRInverse(x: f32) -> f32 { return exp(x * logXMax) - 1.0; }`
158
+ : `fn HDRInverse(x: f32) -> f32 {
159
+ let y = x * rcpNormScale;
160
+ if (y <= x0) { return y / a; }
161
+ else if (y <= x1) { return pow((y - d) / b, 1 / c); }
162
+ else { return exp((y - g) / e) - f; }
163
+ }`;
164
+ return /* wgsl */ `
165
+ ${transferConstantsWGSL}
166
+ const logXMax = ${logXMax};
167
+ ${inverseCode}
168
+ `;
169
+ }
package/src/main.ts ADDED
@@ -0,0 +1,126 @@
1
+ import { parseTZA } from './tza';
2
+ import UNet from './UNet';
3
+ import type { UNetEngineSetting, UNetExecutionStats } from './UNet';
4
+ import { initWebGPUBackend } from './backend';
5
+ import type { DynamicTileSetting } from './tileScheduler';
6
+ import type { UNetModelSpec } from './modelSpec';
7
+ import type {
8
+ NativeSubgroupMatrixOptions,
9
+ NativeUNetGemmOptions,
10
+ NativeUNetKernelSetting,
11
+ NativeUNetPrecisionSetting
12
+ } from './nativeUNet';
13
+ import type { HDRTransfer } from './process';
14
+
15
+ export { parseTZA, UNet };
16
+ export { planTileGrid } from './tileScheduler';
17
+ export type {
18
+ DynamicTileOptions,
19
+ DynamicTileSetting,
20
+ PlannedTile,
21
+ TilePlan,
22
+ TileRect
23
+ } from './tileScheduler';
24
+ export {
25
+ detectUNetModelSpec,
26
+ OIDN_UNET_LARGE_SPEC,
27
+ OIDN_UNET_SMALL_SPEC,
28
+ validateUNetModel
29
+ } from './modelSpec';
30
+ export type {
31
+ ModelNodeSpec,
32
+ UNetModelGraph,
33
+ UNetModelSpec,
34
+ ValidatedUNetModel
35
+ } from './modelSpec';
36
+ export { optimizeModelGraph, planModelExecution } from './graphOptimizer';
37
+ export type {
38
+ ExecutableModelNode,
39
+ GraphOptimizationOptions,
40
+ ModelExecutionPlan,
41
+ ModelValueShape,
42
+ OptimizedModelGraph
43
+ } from './graphOptimizer';
44
+ export {
45
+ NativeUNetExecutor,
46
+ resolveNativeUNetPrecision,
47
+ selectSubgroupMatrixConfig
48
+ } from './nativeUNet';
49
+ export type {
50
+ NativeSubgroupMatrixConfig,
51
+ NativeSubgroupMatrixOptions,
52
+ NativeUNetGemmWorkgroup,
53
+ NativeUNetGemmOptions,
54
+ NativeUNetExecutionProfile,
55
+ NativeUNetKernel,
56
+ NativeUNetKernelSetting,
57
+ NativeUNetLayerTiming,
58
+ NativeUNetOptions,
59
+ NativeUNetPrecision,
60
+ NativeUNetPrecisionSetting
61
+ } from './nativeUNet';
62
+
63
+ export interface UNetOptions {
64
+ aux?: boolean;
65
+ hdr?: boolean;
66
+ /** HDR transfer function expected by the trained model. Defaults to PU. */
67
+ hdrTransfer?: HDRTransfer;
68
+ /** Hard upper bound for an output tile edge. Defaults to 512. */
69
+ maxTileSize?: number;
70
+ /** Adaptive GPU-time-based tile sizing. Enabled by default. */
71
+ dynamicTile?: DynamicTileSetting;
72
+ /** `auto` uses native WGSL; `webnn` opts into the WebNN backend. */
73
+ engine?: UNetEngineSetting;
74
+ /** `auto` selects FP16 when shader-f16 was enabled on the GPUDevice. */
75
+ precision?: NativeUNetPrecisionSetting;
76
+ /** `auto` uses implicit GEMM for FP16/FP32 convolutions, except the direct output layer. */
77
+ kernel?: NativeUNetKernelSetting;
78
+ /**
79
+ * Experimental implicit-GEMM tuning for native WGSL execution. These knobs
80
+ * exist for benchmarking and may change or be removed in any release.
81
+ * @experimental
82
+ */
83
+ gemm?: NativeUNetGemmOptions;
84
+ /**
85
+ * Experimental `kernel: 'subgroup-matrix'` tuning. The kernel needs a
86
+ * GPUDevice created with `subgroups` and `chromium-experimental-subgroup-matrix`.
87
+ * @experimental
88
+ */
89
+ subgroupMatrix?: NativeSubgroupMatrixOptions;
90
+ /** Versioned topology descriptor for future/custom OIDN TZA models. */
91
+ modelSpec?: UNetModelSpec;
92
+ }
93
+
94
+ export type { UNetEngineSetting, UNetExecutionStats } from './UNet';
95
+ export type { HDRTransfer } from './process';
96
+ export { WebNNUNetExecutor } from './webnnUNet';
97
+ export type { WebNNRuntimeSupport, WebNNUNetOptions } from './webnnUNet';
98
+ export type {
99
+ OIDNResourceKind,
100
+ OIDNResourceSnapshot,
101
+ OIDNResourceStats
102
+ } from './resourceTracker';
103
+
104
+ export async function initUNetFromBuffer(
105
+ tzaBuffer: ArrayBuffer,
106
+ backendParams?: { device: GPUDevice },
107
+ opts?: UNetOptions
108
+ ) {
109
+ const device = backendParams?.device ?? await initWebGPUBackend();
110
+ const tensors = parseTZA(tzaBuffer);
111
+ const unet = new UNet(tensors, device, opts);
112
+ await unet.prepare();
113
+ return unet;
114
+ }
115
+
116
+ export async function initUNetFromURL(
117
+ modelPath: string,
118
+ backendParams?: { device: GPUDevice },
119
+ opts?: UNetOptions
120
+ ) {
121
+ return fetch(modelPath)
122
+ .then((res) => res.arrayBuffer())
123
+ .then((ab) => {
124
+ return initUNetFromBuffer(ab, backendParams, opts);
125
+ });
126
+ }