oidn-web 0.2.2 → 0.3.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/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 = false;
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,62 @@ 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 (this._isInputTexture === isInputTexture) {
257
+ return;
258
+ }
259
+
260
+ this._isInputTexture = isInputTexture;
261
+ const isHDR = this._isHDR;
262
+ const commonCSDefine = /* wgsl */ `
172
263
  ${constsCode}
173
264
  fn PUForward(y: f32) -> f32 {
174
265
  if (y <= y0) {
@@ -178,59 +269,49 @@ fn PUForward(y: f32) -> f32 {
178
269
  } else {
179
270
  return e * log(y + f) + g;
180
271
  }
181
- }
182
- `,
183
- csMain: /* wgsl */ `
272
+ }`;
273
+ function readInputCode(inputName: string) {
274
+ return isInputTexture
275
+ ? `textureLoad(in_${inputName}, globalId.xy + vec2u(inputOffset), 0)`
276
+ : `in_${inputName}[inIdx]'`;
277
+ }
278
+ const commonCSMain = /* wgsl */ `
184
279
  let x = f32(globalId.x);
185
280
  let y = f32(globalId.y);
186
281
  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];
282
+ let col = ${readInputCode('color')};
190
283
 
191
284
  let outIdx = i32(y * outputSize.x + x);
192
- out_color[outIdx] = vec3f(PUForward(col.r), PUForward(col.g), PUForward(col.b)) * normScale;
285
+
286
+ if (${denoiseAlpha}) {
287
+ // Denoise the inversed alpha. Or the anti aliased edge will be too dark after denoised
288
+ out_color[outIdx] = vec3f(1.0 - col.a);
289
+ }
290
+ else if (${isHDR}) {
291
+ out_color[outIdx] = vec3f(PUForward(col.r * inputScale), PUForward(col.g * inputScale), PUForward(col.b * inputScale)) * normScale;
292
+ }
293
+ else {
294
+ out_color[outIdx] = col.rgb;
295
+ }
296
+ `;
297
+ this._inputPassAux.setCSCode({
298
+ csDefine: commonCSDefine,
299
+ csMain: /* wgsl */ `
300
+ ${commonCSMain}
301
+ let alb = ${readInputCode('albedo')};
302
+ let nor = ${readInputCode('normal')};
193
303
  out_normal[outIdx] = nor.rgb;
194
304
  out_albedo[outIdx] = alb.rgb;
195
- `
305
+ `
196
306
  });
197
307
 
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
- ],
308
+ this._inputPassColor.setCSCode({
309
+ csDefine: commonCSDefine,
310
+ csMain: /* wgsl */ `
311
+ ${commonCSMain}
312
+ `
313
+ });
314
+ this._outputPass.setCSCode({
234
315
  csDefine: /* wgsl */ `
235
316
  ${constsCode}
236
317
  fn PUInverse(y: f32) -> f32 {
@@ -251,40 +332,46 @@ if (x >= outputSize.x || y >= outputSize.y) {
251
332
  }
252
333
  let inIdx = i32((y + inputOffset.y) * inputSize.x + x + inputOffset.x);
253
334
  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
- );
335
+ let col = in_color[inIdx];
336
+ let raw = ${
337
+ isInputTexture
338
+ ? 'textureLoad(in_raw, globalId.xy + vec2u(outputOffset), 0)'
339
+ : 'in_raw[outIdx]'
340
+ };
341
+
342
+ if (${denoiseAlpha}) {
343
+ out_color[outIdx] = vec4f(raw.rgb, 1.0 - col.r);
344
+ }
345
+ else if (${isHDR}) {
346
+ out_color[outIdx] = vec4f(
347
+ vec3f(PUInverse(col.r * rcpNormScale), PUInverse(col.g * rcpNormScale), PUInverse(col.b * rcpNormScale)) / inputScale,
348
+ // Pick the alpha
349
+ raw.a
350
+ );
351
+ }
352
+ else {
353
+ out_color[outIdx] = vec4f(col.rgb, raw.a);
354
+ }
261
355
  `
262
356
  });
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
357
  }
275
358
 
276
359
  setImageSize(w: number, h: number) {
277
- this._inputPass.setUniform('inputSize', new Float32Array([w, h]));
360
+ this._inputPassAux.setUniform('inputSize', new Float32Array([w, h]));
361
+ this._inputPassColor.setUniform('inputSize', new Float32Array([w, h]));
278
362
  this._outputPass.setUniform('imageSize', new Float32Array([w, h]));
279
363
  this._outputPass.setSize(w, h);
364
+ this._copyPass.setSize(w, h);
365
+ this._copyPass.setUniform('size', new Float32Array([w, h]));
280
366
  }
281
367
 
282
368
  setInputTile(tile: Tile) {
283
- const inputPass = this._inputPass;
284
369
  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]);
370
+ [this._inputPassAux, this._inputPassColor].forEach((inputPass) => {
371
+ inputPass.setUniform('inputOffset', new Float32Array([tile.x, tile.y]));
372
+ inputPass.setUniform('outputSize', size);
373
+ inputPass.setSize(size[0], size[1]);
374
+ });
288
375
 
289
376
  this._outputPass.setUniform('inputSize', size);
290
377
  }
@@ -304,28 +391,56 @@ out_color[outIdx] = vec4f(
304
391
  }
305
392
 
306
393
  forward(
307
- colorBuffer: GPUBuffer,
394
+ colorBuffer: GPUBuffer | GPUTexture,
308
395
  // TODO optional albedo and normal.
309
- albedoBuffer: GPUBuffer,
310
- normalBuffer: GPUBuffer
396
+ albedoBuffer: GPUBuffer | GPUTexture | undefined,
397
+ normalBuffer: GPUBuffer | GPUTexture | undefined,
398
+
399
+ denoiseAlpha: boolean | undefined
311
400
  ) {
312
- const inputGPUPass = this._inputPass;
401
+ const isInputTexture = colorBuffer instanceof GPUTexture;
402
+ this._updatePasses(isInputTexture, denoiseAlpha);
403
+
404
+ const inputPassAux = this._inputPassAux;
405
+ const inputPassColor = this._inputPassColor;
313
406
  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
- });
407
+
408
+ function createInput(bufferOrTex: GPUBuffer | GPUTexture) {
409
+ return bufferOrTex instanceof GPUTexture
410
+ ? {
411
+ texture: bufferOrTex,
412
+ channels: 4
413
+ }
414
+ : {
415
+ buffer: bufferOrTex,
416
+ channels: 4
417
+ };
418
+ }
419
+ if (albedoBuffer && normalBuffer) {
420
+ inputPassAux.createPass(commandEncoder, {
421
+ color: createInput(colorBuffer),
422
+ albedo: createInput(albedoBuffer),
423
+ normal: createInput(normalBuffer)
424
+ });
425
+ } else {
426
+ inputPassColor.createPass(commandEncoder, {
427
+ color: createInput(colorBuffer)
428
+ });
429
+ }
319
430
  this._device.queue.submit([commandEncoder.finish()]);
320
431
 
321
- return {
322
- color: inputGPUPass.getOutputBuffer('color'),
323
- albedo: inputGPUPass.getOutputBuffer('albedo'),
324
- normal: inputGPUPass.getOutputBuffer('normal')
325
- };
432
+ return albedoBuffer && normalBuffer
433
+ ? {
434
+ color: inputPassAux.getOutput('color'),
435
+ albedo: inputPassAux.getOutput('albedo'),
436
+ normal: inputPassAux.getOutput('normal')
437
+ }
438
+ : {
439
+ color: inputPassColor.getOutput('color')
440
+ };
326
441
  }
327
442
 
328
- inverse(buffer: GPUBuffer, raw: GPUBuffer) {
443
+ inverse(buffer: GPUBuffer, raw: GPUBuffer | GPUTexture) {
329
444
  const device = this._device;
330
445
 
331
446
  const commandEncoder = device.createCommandEncoder();
@@ -333,30 +448,44 @@ out_color[outIdx] = vec4f(
333
448
 
334
449
  outputGPUPass.createPass(commandEncoder, {
335
450
  color: { buffer: buffer, channels: 4 },
336
- raw: { buffer: raw, channels: 4 }
451
+ raw:
452
+ raw instanceof GPUBuffer
453
+ ? { buffer: raw, channels: 4 }
454
+ : { texture: raw, channels: 4 }
337
455
  });
338
456
  this._device.queue.submit([commandEncoder.finish()]);
339
457
 
340
- return outputGPUPass.getOutputBuffer('color');
458
+ return outputGPUPass.getOutput('color');
341
459
  }
342
460
 
343
- copyInputDataToOutput(inputColorBuffer: GPUBuffer) {
461
+ copyInputDataToOutput(inputColorBuffer: GPUBuffer | GPUTexture) {
344
462
  const encoder = this._device.createCommandEncoder();
345
463
  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
- );
464
+ const colorBuffer = outputGPUPass.getOutput('color');
465
+ const copyPass = this._copyPass;
466
+
467
+ if (inputColorBuffer instanceof GPUTexture) {
468
+ copyPass.setOutputBuffers({
469
+ color: colorBuffer
470
+ });
471
+ copyPass.createPass(encoder, {
472
+ color: { texture: inputColorBuffer, channels: 4 }
473
+ });
474
+ } else {
475
+ encoder.copyBufferToBuffer(
476
+ inputColorBuffer,
477
+ 0,
478
+ colorBuffer,
479
+ 0,
480
+ colorBuffer.size
481
+ );
482
+ }
354
483
 
355
484
  this._device.queue.submit([encoder.finish()]);
356
485
  }
357
486
 
358
487
  dispose() {
359
488
  this._outputPass.dispose();
360
- this._inputPass.dispose();
489
+ this._inputPassAux.dispose();
361
490
  }
362
491
  }