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.
- package/CHANGELOG.md +182 -0
- package/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/oidn.js +6140 -0
- package/dist/oidn.umd.cjs +1513 -0
- package/lib/UNet.d.ts +161 -0
- package/lib/UNet.js +536 -0
- package/lib/UNet.js.map +1 -0
- package/lib/WGPUComputePass.d.ts +62 -0
- package/lib/WGPUComputePass.js +278 -0
- package/lib/WGPUComputePass.js.map +1 -0
- package/lib/WGPUFullQuadPass.d.ts +50 -0
- package/lib/WGPUFullQuadPass.js +261 -0
- package/lib/WGPUFullQuadPass.js.map +1 -0
- package/lib/backend.d.ts +1 -0
- package/lib/backend.js +39 -0
- package/lib/backend.js.map +1 -0
- package/lib/finalRgbShader.d.ts +13 -0
- package/lib/finalRgbShader.js +160 -0
- package/lib/finalRgbShader.js.map +1 -0
- package/lib/gpuTypes.d.ts +158 -0
- package/lib/gpuTypes.js +27 -0
- package/lib/gpuTypes.js.map +1 -0
- package/lib/graphOptimizer.d.ts +54 -0
- package/lib/graphOptimizer.js +216 -0
- package/lib/graphOptimizer.js.map +1 -0
- package/lib/hdr.d.ts +31 -0
- package/lib/hdr.js +340 -0
- package/lib/hdr.js.map +1 -0
- package/lib/hdrTransfer.d.ts +22 -0
- package/lib/hdrTransfer.js +128 -0
- package/lib/hdrTransfer.js.map +1 -0
- package/lib/main.d.ts +57 -0
- package/lib/main.js +24 -0
- package/lib/main.js.map +1 -0
- package/lib/modelSpec.d.ts +80 -0
- package/lib/modelSpec.js +270 -0
- package/lib/modelSpec.js.map +1 -0
- package/lib/native/denoise.d.ts +25 -0
- package/lib/native/denoise.js +223 -0
- package/lib/native/denoise.js.map +1 -0
- package/lib/native/program.d.ts +53 -0
- package/lib/native/program.js +310 -0
- package/lib/native/program.js.map +1 -0
- package/lib/native/recordingDevice.d.ts +125 -0
- package/lib/native/recordingDevice.js +247 -0
- package/lib/native/recordingDevice.js.map +1 -0
- package/lib/nativeUNet.d.ts +25 -0
- package/lib/nativeUNet.js +184 -0
- package/lib/nativeUNet.js.map +1 -0
- package/lib/nativeUNetCore.d.ts +202 -0
- package/lib/nativeUNetCore.js +2945 -0
- package/lib/nativeUNetCore.js.map +1 -0
- package/lib/process.d.ts +37 -0
- package/lib/process.js +328 -0
- package/lib/process.js.map +1 -0
- package/lib/resourceTracker.d.ts +26 -0
- package/lib/resourceTracker.js +65 -0
- package/lib/resourceTracker.js.map +1 -0
- package/lib/sanitize.d.ts +15 -0
- package/lib/sanitize.js +28 -0
- package/lib/sanitize.js.map +1 -0
- package/lib/tileScheduler.d.ts +61 -0
- package/lib/tileScheduler.js +199 -0
- package/lib/tileScheduler.js.map +1 -0
- package/lib/tza.d.ts +15 -0
- package/lib/tza.js +114 -0
- package/lib/tza.js.map +1 -0
- package/lib/webnnUNet.d.ts +52 -0
- package/lib/webnnUNet.js +535 -0
- package/lib/webnnUNet.js.map +1 -0
- package/package.json +43 -4
- package/src/UNet.ts +861 -0
- package/src/WGPUComputePass.ts +382 -0
- package/src/WGPUFullQuadPass.ts +348 -0
- package/src/backend.ts +42 -0
- package/src/finalRgbShader.ts +186 -0
- package/src/gpuTypes.ts +179 -0
- package/src/graphOptimizer.ts +301 -0
- package/src/hdr.ts +398 -0
- package/src/hdrTransfer.ts +169 -0
- package/src/main.ts +126 -0
- package/src/modelSpec.ts +414 -0
- package/src/native/core-profile.json +40 -0
- package/src/native/denoise.ts +268 -0
- package/src/native/program.ts +326 -0
- package/src/native/recordingDevice.ts +341 -0
- package/src/nativeUNet.ts +269 -0
- package/src/nativeUNetCore.ts +3735 -0
- package/src/process.ts +368 -0
- package/src/resourceTracker.ts +94 -0
- package/src/sanitize.ts +29 -0
- package/src/tileScheduler.ts +330 -0
- package/src/tza.ts +136 -0
- package/src/webnnUNet.ts +812 -0
package/src/process.ts
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { WGPUComputePass } from './WGPUComputePass';
|
|
2
|
+
import {
|
|
3
|
+
hdrForwardWGSL,
|
|
4
|
+
hdrInverseWGSL,
|
|
5
|
+
hdrTransferFuncCPU,
|
|
6
|
+
hdrTransferFuncInverseCPU,
|
|
7
|
+
type HDRTransfer
|
|
8
|
+
} from './hdrTransfer';
|
|
9
|
+
import { sanitizeWGSL } from './sanitize';
|
|
10
|
+
export { avgLogLum, hdrTransferFuncCPU, hdrTransferFuncInverseCPU } from './hdrTransfer';
|
|
11
|
+
export type { HDRTransfer } from './hdrTransfer';
|
|
12
|
+
|
|
13
|
+
export class Tile {
|
|
14
|
+
constructor(
|
|
15
|
+
public x: number,
|
|
16
|
+
public y: number,
|
|
17
|
+
public width: number,
|
|
18
|
+
public height: number
|
|
19
|
+
) {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class GPUDataProcess {
|
|
23
|
+
private _inputPassAux;
|
|
24
|
+
private _inputPassColor;
|
|
25
|
+
private _outputPass;
|
|
26
|
+
private _copyPass;
|
|
27
|
+
|
|
28
|
+
private _isInputTexture?: boolean;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private _device: GPUDevice,
|
|
32
|
+
private _isHDR: boolean,
|
|
33
|
+
private _hdrTransfer: HDRTransfer = 'pu'
|
|
34
|
+
) {
|
|
35
|
+
const commonUniforms = [
|
|
36
|
+
{
|
|
37
|
+
label: 'inputScale',
|
|
38
|
+
type: 'f32',
|
|
39
|
+
data: new Float32Array([1])
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label: 'inputSize',
|
|
43
|
+
type: 'vec2i',
|
|
44
|
+
data: new Int32Array(2)
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'outputSize',
|
|
48
|
+
type: 'vec2i',
|
|
49
|
+
data: new Int32Array(2)
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'inputOffset',
|
|
53
|
+
type: 'vec2i',
|
|
54
|
+
data: new Int32Array(2)
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
this._inputPassAux = new WGPUComputePass('inputPassAux', this._device, {
|
|
58
|
+
inputs: ['color', 'albedo', 'normal'],
|
|
59
|
+
outputs: ['color', 'albedo', 'normal'],
|
|
60
|
+
uniforms: commonUniforms,
|
|
61
|
+
csDefine: '',
|
|
62
|
+
csMain: ``
|
|
63
|
+
});
|
|
64
|
+
this._inputPassColor = new WGPUComputePass('inputPassColor', this._device, {
|
|
65
|
+
inputs: ['color'],
|
|
66
|
+
outputs: ['color'],
|
|
67
|
+
uniforms: commonUniforms,
|
|
68
|
+
csDefine: '',
|
|
69
|
+
csMain: ``
|
|
70
|
+
});
|
|
71
|
+
this._outputPass = new WGPUComputePass('outputPass', this._device, {
|
|
72
|
+
inputs: ['color', 'raw'],
|
|
73
|
+
outputs: ['color'],
|
|
74
|
+
uniforms: [
|
|
75
|
+
{
|
|
76
|
+
label: 'inputScale',
|
|
77
|
+
type: 'f32',
|
|
78
|
+
data: new Float32Array([1])
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
label: 'inputSize',
|
|
82
|
+
type: 'vec2i',
|
|
83
|
+
data: new Int32Array(2)
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: 'outputSize',
|
|
87
|
+
type: 'vec2i',
|
|
88
|
+
data: new Int32Array(2)
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: 'imageSize',
|
|
92
|
+
type: 'vec2i',
|
|
93
|
+
data: new Int32Array(2)
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
label: 'inputOffset',
|
|
97
|
+
type: 'vec2i',
|
|
98
|
+
data: new Int32Array(2)
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
label: 'outputOffset',
|
|
102
|
+
type: 'vec2i',
|
|
103
|
+
data: new Int32Array(2)
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
csDefine: '',
|
|
107
|
+
csMain: ``
|
|
108
|
+
});
|
|
109
|
+
this._copyPass = new WGPUComputePass('copyPass', this._device, {
|
|
110
|
+
inputs: ['color'],
|
|
111
|
+
outputs: ['color'],
|
|
112
|
+
autoUpdateOutputBuffer: false,
|
|
113
|
+
uniforms: [
|
|
114
|
+
{
|
|
115
|
+
label: 'size',
|
|
116
|
+
type: 'vec2i',
|
|
117
|
+
data: new Int32Array(2)
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
csMain: /*wgsl*/ `
|
|
121
|
+
let outIdx = i32(globalId.x + globalId.y * u32(size.x));
|
|
122
|
+
out_color[outIdx] = textureLoad(in_color, globalId.xy, 0);
|
|
123
|
+
`
|
|
124
|
+
});
|
|
125
|
+
this._inputPassAux.setOutputParams({
|
|
126
|
+
color: { channels: 3 },
|
|
127
|
+
albedo: { channels: 3 },
|
|
128
|
+
normal: { channels: 3 }
|
|
129
|
+
});
|
|
130
|
+
this._inputPassColor.setOutputParams({
|
|
131
|
+
color: { channels: 3 }
|
|
132
|
+
});
|
|
133
|
+
this._outputPass.setOutputParams({
|
|
134
|
+
color: { channels: 4 }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private _updatePasses(isInputTexture: boolean, denoiseAlpha = false) {
|
|
139
|
+
if (
|
|
140
|
+
this._isInputTexture != undefined &&
|
|
141
|
+
this._isInputTexture === isInputTexture
|
|
142
|
+
) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this._isInputTexture = isInputTexture;
|
|
147
|
+
const isHDR = this._isHDR;
|
|
148
|
+
const commonCSDefine = hdrForwardWGSL(this._hdrTransfer) + sanitizeWGSL;
|
|
149
|
+
function readInputCode(inputName: string) {
|
|
150
|
+
return isInputTexture
|
|
151
|
+
? `textureLoad(in_${inputName}, vec2u(inputPosition), 0)`
|
|
152
|
+
: `in_${inputName}[inIdx]`;
|
|
153
|
+
}
|
|
154
|
+
const commonCSMain = /* wgsl */ `
|
|
155
|
+
let x = i32(globalId.x);
|
|
156
|
+
let y = i32(globalId.y);
|
|
157
|
+
let inputPosition = clamp(
|
|
158
|
+
vec2i(x, y) + inputOffset,
|
|
159
|
+
vec2i(0),
|
|
160
|
+
inputSize - vec2i(1)
|
|
161
|
+
);
|
|
162
|
+
let inIdx = inputPosition.y * inputSize.x + inputPosition.x;
|
|
163
|
+
let col = ${readInputCode('color')};
|
|
164
|
+
|
|
165
|
+
let outIdx = y * outputSize.x + x;
|
|
166
|
+
|
|
167
|
+
if (${denoiseAlpha}) {
|
|
168
|
+
// Denoise the inversed alpha. Or the anti aliased edge will be too dark after denoised
|
|
169
|
+
out_color[outIdx] = sanitize(vec3f(1.0 - col.a), 0.0, 1.0);
|
|
170
|
+
}
|
|
171
|
+
else if (${isHDR}) {
|
|
172
|
+
let scaled = sanitize(col.rgb * inputScale, 0.0, FLT_MAX);
|
|
173
|
+
out_color[outIdx] = vec3f(HDRForward(scaled.r), HDRForward(scaled.g), HDRForward(scaled.b));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
out_color[outIdx] = sanitize(col.rgb, 0.0, 1.0);
|
|
177
|
+
}
|
|
178
|
+
`;
|
|
179
|
+
this._inputPassAux.setCSCode({
|
|
180
|
+
csDefine: commonCSDefine,
|
|
181
|
+
csMain: /* wgsl */ `
|
|
182
|
+
${commonCSMain}
|
|
183
|
+
let alb = ${readInputCode('albedo')};
|
|
184
|
+
let nor = ${readInputCode('normal')};
|
|
185
|
+
// Normals arrive [0, 1]-encoded, so OIDN's [-1, 1] clamp becomes [0, 1].
|
|
186
|
+
out_normal[outIdx] = sanitize(nor.rgb, 0.0, 1.0);
|
|
187
|
+
out_albedo[outIdx] = sanitize(alb.rgb, 0.0, 1.0);
|
|
188
|
+
`
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
this._inputPassColor.setCSCode({
|
|
192
|
+
csDefine: commonCSDefine,
|
|
193
|
+
csMain: /* wgsl */ `
|
|
194
|
+
${commonCSMain}
|
|
195
|
+
`
|
|
196
|
+
});
|
|
197
|
+
this._outputPass.setCSCode({
|
|
198
|
+
csDefine: hdrInverseWGSL(this._hdrTransfer) + sanitizeWGSL,
|
|
199
|
+
csMain: /* wgsl */ `
|
|
200
|
+
let x = i32(globalId.x);
|
|
201
|
+
let y = i32(globalId.y);
|
|
202
|
+
if (x >= outputSize.x || y >= outputSize.y) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
let inIdx = (y + inputOffset.y) * inputSize.x + x + inputOffset.x;
|
|
206
|
+
let outIdx = (y + outputOffset.y) * imageSize.x + x + outputOffset.x;
|
|
207
|
+
let col = in_color[inIdx];
|
|
208
|
+
let raw = ${
|
|
209
|
+
isInputTexture
|
|
210
|
+
? 'textureLoad(in_raw, globalId.xy + vec2u(outputOffset), 0)'
|
|
211
|
+
: 'in_raw[outIdx]'
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// The network output may contain negative values or NaNs.
|
|
215
|
+
let denoised = sanitize(col.rgb, 0.0, ${isHDR && !denoiseAlpha ? 'FLT_MAX' : '1.0'});
|
|
216
|
+
if (${denoiseAlpha}) {
|
|
217
|
+
out_color[outIdx] = vec4f(raw.rgb, 1.0 - denoised.r);
|
|
218
|
+
}
|
|
219
|
+
else if (${isHDR}) {
|
|
220
|
+
out_color[outIdx] = vec4f(
|
|
221
|
+
vec3f(HDRInverse(denoised.r), HDRInverse(denoised.g), HDRInverse(denoised.b)) / inputScale,
|
|
222
|
+
// Pick the alpha
|
|
223
|
+
raw.a
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
out_color[outIdx] = vec4f(denoised, raw.a);
|
|
228
|
+
}
|
|
229
|
+
`
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
setImageSize(w: number, h: number) {
|
|
234
|
+
this._inputPassAux.setUniform('inputSize', new Int32Array([w, h]));
|
|
235
|
+
this._inputPassColor.setUniform('inputSize', new Int32Array([w, h]));
|
|
236
|
+
this._outputPass.setUniform('imageSize', new Int32Array([w, h]));
|
|
237
|
+
this._outputPass.setSize(w, h);
|
|
238
|
+
this._copyPass.setSize(w, h);
|
|
239
|
+
this._copyPass.setUniform('size', new Int32Array([w, h]));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
setInputTile(tile: Tile) {
|
|
243
|
+
const size = new Int32Array([tile.width, tile.height]);
|
|
244
|
+
[this._inputPassAux, this._inputPassColor].forEach((inputPass) => {
|
|
245
|
+
inputPass.setUniform('inputOffset', new Int32Array([tile.x, tile.y]));
|
|
246
|
+
inputPass.setUniform('outputSize', size);
|
|
247
|
+
inputPass.setSize(size[0], size[1]);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
this._outputPass.setUniform('inputSize', size);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
setOutputTile(dstTile: Tile, srcTile: Tile) {
|
|
254
|
+
const outputPass = this._outputPass;
|
|
255
|
+
const size = new Int32Array([dstTile.width, dstTile.height]);
|
|
256
|
+
const dx = dstTile.x - srcTile.x;
|
|
257
|
+
const dy = dstTile.y - srcTile.y;
|
|
258
|
+
outputPass.setUniform('outputSize', size);
|
|
259
|
+
outputPass.setUniform('inputOffset', new Int32Array([dx, dy]));
|
|
260
|
+
outputPass.setUniform(
|
|
261
|
+
'outputOffset',
|
|
262
|
+
new Int32Array([dstTile.x, dstTile.y])
|
|
263
|
+
);
|
|
264
|
+
outputPass.setExecuteSize(size[0], size[1]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
forward(
|
|
268
|
+
colorBuffer: GPUBuffer | GPUTexture,
|
|
269
|
+
// TODO optional albedo and normal.
|
|
270
|
+
albedoBuffer: GPUBuffer | GPUTexture | undefined,
|
|
271
|
+
normalBuffer: GPUBuffer | GPUTexture | undefined,
|
|
272
|
+
|
|
273
|
+
denoiseAlpha: boolean | undefined
|
|
274
|
+
) {
|
|
275
|
+
const isInputTexture = colorBuffer instanceof GPUTexture;
|
|
276
|
+
this._updatePasses(isInputTexture, denoiseAlpha);
|
|
277
|
+
|
|
278
|
+
const inputPassAux = this._inputPassAux;
|
|
279
|
+
const inputPassColor = this._inputPassColor;
|
|
280
|
+
const commandEncoder = this._device.createCommandEncoder();
|
|
281
|
+
|
|
282
|
+
function createInput(bufferOrTex: GPUBuffer | GPUTexture) {
|
|
283
|
+
return bufferOrTex instanceof GPUTexture
|
|
284
|
+
? {
|
|
285
|
+
texture: bufferOrTex,
|
|
286
|
+
channels: 4
|
|
287
|
+
}
|
|
288
|
+
: {
|
|
289
|
+
buffer: bufferOrTex,
|
|
290
|
+
channels: 4
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (albedoBuffer && normalBuffer) {
|
|
294
|
+
inputPassAux.createPass(commandEncoder, {
|
|
295
|
+
color: createInput(colorBuffer),
|
|
296
|
+
albedo: createInput(albedoBuffer),
|
|
297
|
+
normal: createInput(normalBuffer)
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
inputPassColor.createPass(commandEncoder, {
|
|
301
|
+
color: createInput(colorBuffer)
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
this._device.queue.submit([commandEncoder.finish()]);
|
|
305
|
+
|
|
306
|
+
return albedoBuffer && normalBuffer
|
|
307
|
+
? {
|
|
308
|
+
color: inputPassAux.getOutput('color'),
|
|
309
|
+
albedo: inputPassAux.getOutput('albedo'),
|
|
310
|
+
normal: inputPassAux.getOutput('normal')
|
|
311
|
+
}
|
|
312
|
+
: {
|
|
313
|
+
color: inputPassColor.getOutput('color')
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
inverse(buffer: GPUBuffer, raw: GPUBuffer | GPUTexture) {
|
|
318
|
+
const device = this._device;
|
|
319
|
+
|
|
320
|
+
const commandEncoder = device.createCommandEncoder();
|
|
321
|
+
const outputGPUPass = this._outputPass;
|
|
322
|
+
|
|
323
|
+
outputGPUPass.createPass(commandEncoder, {
|
|
324
|
+
color: { buffer: buffer, channels: 4 },
|
|
325
|
+
raw:
|
|
326
|
+
raw instanceof GPUBuffer
|
|
327
|
+
? { buffer: raw, channels: 4 }
|
|
328
|
+
: { texture: raw, channels: 4 }
|
|
329
|
+
});
|
|
330
|
+
this._device.queue.submit([commandEncoder.finish()]);
|
|
331
|
+
|
|
332
|
+
return outputGPUPass.getOutput('color');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
copyInputDataToOutput(inputColorBuffer: GPUBuffer | GPUTexture) {
|
|
336
|
+
const encoder = this._device.createCommandEncoder();
|
|
337
|
+
const outputGPUPass = this._outputPass;
|
|
338
|
+
const colorBuffer = outputGPUPass.getOutput('color');
|
|
339
|
+
const copyPass = this._copyPass;
|
|
340
|
+
|
|
341
|
+
if (inputColorBuffer instanceof GPUTexture) {
|
|
342
|
+
copyPass.setOutputBuffers({
|
|
343
|
+
color: colorBuffer
|
|
344
|
+
});
|
|
345
|
+
copyPass.createPass(encoder, {
|
|
346
|
+
color: { texture: inputColorBuffer, channels: 4 }
|
|
347
|
+
});
|
|
348
|
+
} else {
|
|
349
|
+
encoder.copyBufferToBuffer(
|
|
350
|
+
inputColorBuffer,
|
|
351
|
+
0,
|
|
352
|
+
colorBuffer,
|
|
353
|
+
0,
|
|
354
|
+
colorBuffer.size
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
this._device.queue.submit([encoder.finish()]);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
dispose() {
|
|
362
|
+
this._outputPass.dispose();
|
|
363
|
+
this._inputPassAux.dispose();
|
|
364
|
+
this._inputPassColor.dispose();
|
|
365
|
+
// copyPass borrows outputPass's target and must not destroy it again.
|
|
366
|
+
this._copyPass.dispose(false);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export type OIDNResourceKind =
|
|
2
|
+
| 'gpu-buffer'
|
|
3
|
+
| 'gpu-query-set'
|
|
4
|
+
| 'ml-context'
|
|
5
|
+
| 'ml-graph'
|
|
6
|
+
| 'ml-tensor';
|
|
7
|
+
|
|
8
|
+
export interface OIDNResourceStats {
|
|
9
|
+
created: number;
|
|
10
|
+
destroyed: number;
|
|
11
|
+
live: number;
|
|
12
|
+
peakLive: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface OIDNResourceSnapshot {
|
|
16
|
+
live: number;
|
|
17
|
+
created: number;
|
|
18
|
+
destroyed: number;
|
|
19
|
+
peakLive: number;
|
|
20
|
+
pending: number;
|
|
21
|
+
byKind: Partial<Record<OIDNResourceKind, OIDNResourceStats>>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface MutableResourceStats extends OIDNResourceStats {
|
|
25
|
+
resources: Set<object>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Counts explicitly owned browser resources. Keeping the ownership registry in
|
|
30
|
+
* production makes cache eviction and failure cleanup testable without relying
|
|
31
|
+
* on JS heap measurements, which do not include WebGPU allocations.
|
|
32
|
+
*/
|
|
33
|
+
export class OIDNResourceTracker {
|
|
34
|
+
private _stats = new Map<OIDNResourceKind, MutableResourceStats>();
|
|
35
|
+
|
|
36
|
+
track<T extends object>(kind: OIDNResourceKind, resource: T): T {
|
|
37
|
+
let stats = this._stats.get(kind);
|
|
38
|
+
if (!stats) {
|
|
39
|
+
stats = {
|
|
40
|
+
created: 0,
|
|
41
|
+
destroyed: 0,
|
|
42
|
+
live: 0,
|
|
43
|
+
peakLive: 0,
|
|
44
|
+
resources: new Set()
|
|
45
|
+
};
|
|
46
|
+
this._stats.set(kind, stats);
|
|
47
|
+
}
|
|
48
|
+
if (stats.resources.has(resource)) return resource;
|
|
49
|
+
stats.resources.add(resource);
|
|
50
|
+
stats.created++;
|
|
51
|
+
stats.live++;
|
|
52
|
+
stats.peakLive = Math.max(stats.peakLive, stats.live);
|
|
53
|
+
return resource;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
release(
|
|
57
|
+
kind: OIDNResourceKind,
|
|
58
|
+
resource: object | undefined,
|
|
59
|
+
destroy: () => void
|
|
60
|
+
) {
|
|
61
|
+
if (!resource) return false;
|
|
62
|
+
const stats = this._stats.get(kind);
|
|
63
|
+
if (!stats?.resources.delete(resource)) return false;
|
|
64
|
+
try {
|
|
65
|
+
destroy();
|
|
66
|
+
} finally {
|
|
67
|
+
stats.destroyed++;
|
|
68
|
+
stats.live--;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
snapshot(pending = 0): OIDNResourceSnapshot {
|
|
74
|
+
let live = 0;
|
|
75
|
+
let created = 0;
|
|
76
|
+
let destroyed = 0;
|
|
77
|
+
let peakLive = 0;
|
|
78
|
+
const byKind: OIDNResourceSnapshot['byKind'] = {};
|
|
79
|
+
for (const [kind, stats] of this._stats) {
|
|
80
|
+
const entry = {
|
|
81
|
+
created: stats.created,
|
|
82
|
+
destroyed: stats.destroyed,
|
|
83
|
+
live: stats.live,
|
|
84
|
+
peakLive: stats.peakLive
|
|
85
|
+
};
|
|
86
|
+
byKind[kind] = entry;
|
|
87
|
+
live += entry.live;
|
|
88
|
+
created += entry.created;
|
|
89
|
+
destroyed += entry.destroyed;
|
|
90
|
+
peakLive += entry.peakLive;
|
|
91
|
+
}
|
|
92
|
+
return { live, created, destroyed, peakLive, pending, byKind };
|
|
93
|
+
}
|
|
94
|
+
}
|
package/src/sanitize.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OIDN's input and output sanitizing: NaN becomes 0, then values are clamped
|
|
3
|
+
* to their valid range. HDR values are bounded by FLT_MAX, so +inf becomes
|
|
4
|
+
* FLT_MAX. Used before the network, by auto-exposure, and on the network
|
|
5
|
+
* output before the inverse transfer.
|
|
6
|
+
*/
|
|
7
|
+
export const FLT_MAX = 3.4028234663852886e38;
|
|
8
|
+
|
|
9
|
+
export function sanitize(value: number, lo: number, hi: number) {
|
|
10
|
+
return Number.isNaN(value) ? 0 : Math.min(Math.max(value, lo), hi);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* WGSL declarations of `FLT_MAX` and `fn sanitize(v: vec3f, lo: f32, hi: f32)`.
|
|
15
|
+
* WGSL compilers may assume floats are finite, so NaN and infinity are
|
|
16
|
+
* detected from the bits rather than with comparisons. FLT_MAX is spelled out
|
|
17
|
+
* so the native core does not depend on scriptc's number formatting.
|
|
18
|
+
*/
|
|
19
|
+
export const sanitizeWGSL = /* wgsl */ `
|
|
20
|
+
const FLT_MAX = 3.4028234663852886e38;
|
|
21
|
+
fn sanitize(v: vec3f, lo: f32, hi: f32) -> vec3f {
|
|
22
|
+
let bits = bitcast<vec3u>(v);
|
|
23
|
+
let magnitude = bits & vec3u(0x7fffffffu);
|
|
24
|
+
let negative = (bits & vec3u(0x80000000u)) != vec3u(0u);
|
|
25
|
+
let infinite = select(vec3f(hi), vec3f(lo), negative);
|
|
26
|
+
let clamped = select(clamp(v, vec3f(lo), vec3f(hi)), infinite, magnitude == vec3u(0x7f800000u));
|
|
27
|
+
return select(clamped, vec3f(0.0), magnitude > vec3u(0x7f800000u));
|
|
28
|
+
}
|
|
29
|
+
`;
|