oidn-web 0.2.2 → 0.3.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/src/process.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { buffer, data } from '@tensorflow/tfjs';
1
2
  import { WGPUComputePass } from './WGPUComputePass';
2
3
 
3
4
  const a = 1.41283765e3;
@@ -140,12 +141,53 @@ const rcpNormScale = ${rcpNormScale};
140
141
  `;
141
142
 
142
143
  export class GPUDataProcess {
143
- private _inputPass;
144
+ private _inputPassAux;
145
+ private _inputPassColor;
144
146
  private _outputPass;
145
- constructor(private _device: GPUDevice) {
146
- this._inputPass = new WGPUComputePass('inputPass', this._device, {
147
+ private _copyPass;
148
+
149
+ private _isInputTexture?: boolean;
150
+
151
+ constructor(private _device: GPUDevice, private _isHDR: boolean) {
152
+ const commonUniforms = [
153
+ {
154
+ label: 'inputScale',
155
+ type: 'f32',
156
+ data: new Float32Array([1])
157
+ },
158
+ {
159
+ label: 'inputSize',
160
+ type: 'vec2<f32>',
161
+ data: new Float32Array(2)
162
+ },
163
+ {
164
+ label: 'outputSize',
165
+ type: 'vec2<f32>',
166
+ data: new Float32Array(2)
167
+ },
168
+ {
169
+ label: 'inputOffset',
170
+ type: 'vec2<f32>',
171
+ data: new Float32Array(2)
172
+ }
173
+ ];
174
+ this._inputPassAux = new WGPUComputePass('inputPassAux', this._device, {
147
175
  inputs: ['color', 'albedo', 'normal'],
148
176
  outputs: ['color', 'albedo', 'normal'],
177
+ uniforms: commonUniforms,
178
+ csDefine: '',
179
+ csMain: ``
180
+ });
181
+ this._inputPassColor = new WGPUComputePass('inputPassColor', this._device, {
182
+ inputs: ['color'],
183
+ outputs: ['color'],
184
+ uniforms: commonUniforms,
185
+ csDefine: '',
186
+ csMain: ``
187
+ });
188
+ this._outputPass = new WGPUComputePass('outputPass', this._device, {
189
+ inputs: ['color', 'raw'],
190
+ outputs: ['color'],
149
191
  uniforms: [
150
192
  {
151
193
  label: 'inputScale',
@@ -162,13 +204,65 @@ export class GPUDataProcess {
162
204
  type: 'vec2<f32>',
163
205
  data: new Float32Array(2)
164
206
  },
207
+ {
208
+ label: 'imageSize',
209
+ type: 'vec2<f32>',
210
+ data: new Float32Array(2)
211
+ },
165
212
  {
166
213
  label: 'inputOffset',
167
214
  type: 'vec2<f32>',
168
215
  data: new Float32Array(2)
216
+ },
217
+ {
218
+ label: 'outputOffset',
219
+ type: 'vec2<f32>',
220
+ data: new Float32Array(2)
169
221
  }
170
222
  ],
171
- csDefine: /* wgsl */ `
223
+ csDefine: '',
224
+ csMain: ``
225
+ });
226
+ this._copyPass = new WGPUComputePass('copyPass', this._device, {
227
+ inputs: ['color'],
228
+ outputs: ['color'],
229
+ autoUpdateOutputBuffer: false,
230
+ uniforms: [
231
+ {
232
+ label: 'size',
233
+ type: 'vec2<f32>',
234
+ data: new Float32Array(2)
235
+ }
236
+ ],
237
+ csMain: /*wgsl*/ `
238
+ let outIdx = i32(globalId.x + globalId.y * u32(size.x));
239
+ out_color[outIdx] = textureLoad(in_color, globalId.xy, 0);
240
+ `
241
+ });
242
+ this._inputPassAux.setOutputParams({
243
+ color: { channels: 3 },
244
+ albedo: { channels: 3 },
245
+ normal: { channels: 3 }
246
+ });
247
+ this._inputPassColor.setOutputParams({
248
+ color: { channels: 3 }
249
+ });
250
+ this._outputPass.setOutputParams({
251
+ color: { channels: 4 }
252
+ });
253
+ }
254
+
255
+ private _updatePasses(isInputTexture: boolean, denoiseAlpha = false) {
256
+ if (
257
+ this._isInputTexture != undefined &&
258
+ this._isInputTexture === isInputTexture
259
+ ) {
260
+ return;
261
+ }
262
+
263
+ this._isInputTexture = isInputTexture;
264
+ const isHDR = this._isHDR;
265
+ const commonCSDefine = /* wgsl */ `
172
266
  ${constsCode}
173
267
  fn PUForward(y: f32) -> f32 {
174
268
  if (y <= y0) {
@@ -178,59 +272,49 @@ fn PUForward(y: f32) -> f32 {
178
272
  } else {
179
273
  return e * log(y + f) + g;
180
274
  }
181
- }
182
- `,
183
- csMain: /* wgsl */ `
275
+ }`;
276
+ function readInputCode(inputName: string) {
277
+ return isInputTexture
278
+ ? `textureLoad(in_${inputName}, globalId.xy + vec2u(inputOffset), 0)`
279
+ : `in_${inputName}[inIdx]`;
280
+ }
281
+ const commonCSMain = /* wgsl */ `
184
282
  let x = f32(globalId.x);
185
283
  let y = f32(globalId.y);
186
284
  let inIdx = i32((y + inputOffset.y) * inputSize.x + (x + inputOffset.x));
187
- let col = in_color[inIdx] * inputScale;
188
- let alb = in_albedo[inIdx];
189
- let nor = in_normal[inIdx];
285
+ let col = ${readInputCode('color')};
190
286
 
191
287
  let outIdx = i32(y * outputSize.x + x);
192
- out_color[outIdx] = vec3f(PUForward(col.r), PUForward(col.g), PUForward(col.b)) * normScale;
288
+
289
+ if (${denoiseAlpha}) {
290
+ // Denoise the inversed alpha. Or the anti aliased edge will be too dark after denoised
291
+ out_color[outIdx] = vec3f(1.0 - col.a);
292
+ }
293
+ else if (${isHDR}) {
294
+ out_color[outIdx] = vec3f(PUForward(col.r * inputScale), PUForward(col.g * inputScale), PUForward(col.b * inputScale)) * normScale;
295
+ }
296
+ else {
297
+ out_color[outIdx] = col.rgb;
298
+ }
299
+ `;
300
+ this._inputPassAux.setCSCode({
301
+ csDefine: commonCSDefine,
302
+ csMain: /* wgsl */ `
303
+ ${commonCSMain}
304
+ let alb = ${readInputCode('albedo')};
305
+ let nor = ${readInputCode('normal')};
193
306
  out_normal[outIdx] = nor.rgb;
194
307
  out_albedo[outIdx] = alb.rgb;
195
- `
308
+ `
196
309
  });
197
310
 
198
- this._outputPass = new WGPUComputePass('outputPass', this._device, {
199
- inputs: ['color', 'raw'],
200
- outputs: ['color'],
201
- uniforms: [
202
- {
203
- // TODO inputScale from avg log lum
204
- label: 'inputScale',
205
- type: 'f32',
206
- data: new Float32Array([1])
207
- },
208
- {
209
- label: 'inputSize',
210
- type: 'vec2<f32>',
211
- data: new Float32Array(2)
212
- },
213
- {
214
- label: 'outputSize',
215
- type: 'vec2<f32>',
216
- data: new Float32Array(2)
217
- },
218
- {
219
- label: 'imageSize',
220
- type: 'vec2<f32>',
221
- data: new Float32Array(2)
222
- },
223
- {
224
- label: 'inputOffset',
225
- type: 'vec2<f32>',
226
- data: new Float32Array(2)
227
- },
228
- {
229
- label: 'outputOffset',
230
- type: 'vec2<f32>',
231
- data: new Float32Array(2)
232
- }
233
- ],
311
+ this._inputPassColor.setCSCode({
312
+ csDefine: commonCSDefine,
313
+ csMain: /* wgsl */ `
314
+ ${commonCSMain}
315
+ `
316
+ });
317
+ this._outputPass.setCSCode({
234
318
  csDefine: /* wgsl */ `
235
319
  ${constsCode}
236
320
  fn PUInverse(y: f32) -> f32 {
@@ -251,40 +335,46 @@ if (x >= outputSize.x || y >= outputSize.y) {
251
335
  }
252
336
  let inIdx = i32((y + inputOffset.y) * inputSize.x + x + inputOffset.x);
253
337
  let outIdx = i32((y + outputOffset.y) * imageSize.x + x + outputOffset.x);
254
- let col = in_color[inIdx] * rcpNormScale;
255
- let raw = in_raw[outIdx];
256
- out_color[outIdx] = vec4f(
257
- vec3f(PUInverse(col.r), PUInverse(col.g), PUInverse(col.b)) / inputScale,
258
- // Pick the alpha
259
- raw.a
260
- );
338
+ let col = in_color[inIdx];
339
+ let raw = ${
340
+ isInputTexture
341
+ ? 'textureLoad(in_raw, globalId.xy + vec2u(outputOffset), 0)'
342
+ : 'in_raw[outIdx]'
343
+ };
344
+
345
+ if (${denoiseAlpha}) {
346
+ out_color[outIdx] = vec4f(raw.rgb, 1.0 - col.r);
347
+ }
348
+ else if (${isHDR}) {
349
+ out_color[outIdx] = vec4f(
350
+ vec3f(PUInverse(col.r * rcpNormScale), PUInverse(col.g * rcpNormScale), PUInverse(col.b * rcpNormScale)) / inputScale,
351
+ // Pick the alpha
352
+ raw.a
353
+ );
354
+ }
355
+ else {
356
+ out_color[outIdx] = vec4f(col.rgb, raw.a);
357
+ }
261
358
  `
262
359
  });
263
-
264
- // TODO input scale
265
- this._inputPass.setOutputParams({
266
- color: { channels: 3 },
267
- albedo: { channels: 3 },
268
- normal: { channels: 3 }
269
- });
270
- // TODO input scale
271
- this._outputPass.setOutputParams({
272
- color: { channels: 4 }
273
- });
274
360
  }
275
361
 
276
362
  setImageSize(w: number, h: number) {
277
- this._inputPass.setUniform('inputSize', new Float32Array([w, h]));
363
+ this._inputPassAux.setUniform('inputSize', new Float32Array([w, h]));
364
+ this._inputPassColor.setUniform('inputSize', new Float32Array([w, h]));
278
365
  this._outputPass.setUniform('imageSize', new Float32Array([w, h]));
279
366
  this._outputPass.setSize(w, h);
367
+ this._copyPass.setSize(w, h);
368
+ this._copyPass.setUniform('size', new Float32Array([w, h]));
280
369
  }
281
370
 
282
371
  setInputTile(tile: Tile) {
283
- const inputPass = this._inputPass;
284
372
  const size = new Float32Array([tile.width, tile.height]);
285
- inputPass.setUniform('inputOffset', new Float32Array([tile.x, tile.y]));
286
- inputPass.setUniform('outputSize', size);
287
- inputPass.setSize(size[0], size[1]);
373
+ [this._inputPassAux, this._inputPassColor].forEach((inputPass) => {
374
+ inputPass.setUniform('inputOffset', new Float32Array([tile.x, tile.y]));
375
+ inputPass.setUniform('outputSize', size);
376
+ inputPass.setSize(size[0], size[1]);
377
+ });
288
378
 
289
379
  this._outputPass.setUniform('inputSize', size);
290
380
  }
@@ -304,28 +394,56 @@ out_color[outIdx] = vec4f(
304
394
  }
305
395
 
306
396
  forward(
307
- colorBuffer: GPUBuffer,
397
+ colorBuffer: GPUBuffer | GPUTexture,
308
398
  // TODO optional albedo and normal.
309
- albedoBuffer: GPUBuffer,
310
- normalBuffer: GPUBuffer
399
+ albedoBuffer: GPUBuffer | GPUTexture | undefined,
400
+ normalBuffer: GPUBuffer | GPUTexture | undefined,
401
+
402
+ denoiseAlpha: boolean | undefined
311
403
  ) {
312
- const inputGPUPass = this._inputPass;
404
+ const isInputTexture = colorBuffer instanceof GPUTexture;
405
+ this._updatePasses(isInputTexture, denoiseAlpha);
406
+
407
+ const inputPassAux = this._inputPassAux;
408
+ const inputPassColor = this._inputPassColor;
313
409
  const commandEncoder = this._device.createCommandEncoder();
314
- inputGPUPass.createPass(commandEncoder, {
315
- color: { buffer: colorBuffer, channels: 4 },
316
- albedo: { buffer: albedoBuffer, channels: 4 },
317
- normal: { buffer: normalBuffer, channels: 4 }
318
- });
410
+
411
+ function createInput(bufferOrTex: GPUBuffer | GPUTexture) {
412
+ return bufferOrTex instanceof GPUTexture
413
+ ? {
414
+ texture: bufferOrTex,
415
+ channels: 4
416
+ }
417
+ : {
418
+ buffer: bufferOrTex,
419
+ channels: 4
420
+ };
421
+ }
422
+ if (albedoBuffer && normalBuffer) {
423
+ inputPassAux.createPass(commandEncoder, {
424
+ color: createInput(colorBuffer),
425
+ albedo: createInput(albedoBuffer),
426
+ normal: createInput(normalBuffer)
427
+ });
428
+ } else {
429
+ inputPassColor.createPass(commandEncoder, {
430
+ color: createInput(colorBuffer)
431
+ });
432
+ }
319
433
  this._device.queue.submit([commandEncoder.finish()]);
320
434
 
321
- return {
322
- color: inputGPUPass.getOutputBuffer('color'),
323
- albedo: inputGPUPass.getOutputBuffer('albedo'),
324
- normal: inputGPUPass.getOutputBuffer('normal')
325
- };
435
+ return albedoBuffer && normalBuffer
436
+ ? {
437
+ color: inputPassAux.getOutput('color'),
438
+ albedo: inputPassAux.getOutput('albedo'),
439
+ normal: inputPassAux.getOutput('normal')
440
+ }
441
+ : {
442
+ color: inputPassColor.getOutput('color')
443
+ };
326
444
  }
327
445
 
328
- inverse(buffer: GPUBuffer, raw: GPUBuffer) {
446
+ inverse(buffer: GPUBuffer, raw: GPUBuffer | GPUTexture) {
329
447
  const device = this._device;
330
448
 
331
449
  const commandEncoder = device.createCommandEncoder();
@@ -333,30 +451,44 @@ out_color[outIdx] = vec4f(
333
451
 
334
452
  outputGPUPass.createPass(commandEncoder, {
335
453
  color: { buffer: buffer, channels: 4 },
336
- raw: { buffer: raw, channels: 4 }
454
+ raw:
455
+ raw instanceof GPUBuffer
456
+ ? { buffer: raw, channels: 4 }
457
+ : { texture: raw, channels: 4 }
337
458
  });
338
459
  this._device.queue.submit([commandEncoder.finish()]);
339
460
 
340
- return outputGPUPass.getOutputBuffer('color');
461
+ return outputGPUPass.getOutput('color');
341
462
  }
342
463
 
343
- copyInputDataToOutput(inputColorBuffer: GPUBuffer) {
464
+ copyInputDataToOutput(inputColorBuffer: GPUBuffer | GPUTexture) {
344
465
  const encoder = this._device.createCommandEncoder();
345
466
  const outputGPUPass = this._outputPass;
346
- const colorBuffer = outputGPUPass.getOutputBuffer('color');
347
- encoder.copyBufferToBuffer(
348
- inputColorBuffer,
349
- 0,
350
- colorBuffer,
351
- 0,
352
- colorBuffer.size
353
- );
467
+ const colorBuffer = outputGPUPass.getOutput('color');
468
+ const copyPass = this._copyPass;
469
+
470
+ if (inputColorBuffer instanceof GPUTexture) {
471
+ copyPass.setOutputBuffers({
472
+ color: colorBuffer
473
+ });
474
+ copyPass.createPass(encoder, {
475
+ color: { texture: inputColorBuffer, channels: 4 }
476
+ });
477
+ } else {
478
+ encoder.copyBufferToBuffer(
479
+ inputColorBuffer,
480
+ 0,
481
+ colorBuffer,
482
+ 0,
483
+ colorBuffer.size
484
+ );
485
+ }
354
486
 
355
487
  this._device.queue.submit([encoder.finish()]);
356
488
  }
357
489
 
358
490
  dispose() {
359
491
  this._outputPass.dispose();
360
- this._inputPass.dispose();
492
+ this._inputPassAux.dispose();
361
493
  }
362
494
  }