oidn-web 0.3.4 → 0.4.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 +49 -0
- package/README.md +140 -4
- package/benchmarks/compare.mjs +651 -0
- package/benchmarks/leak.mjs +255 -0
- package/benchmarks/results/before-spatial.json +391 -0
- package/benchmarks/results/before-spatial.md +47 -0
- package/benchmarks/results/int8-scan.json +2007 -0
- package/benchmarks/results/int8-scan.md +160 -0
- package/benchmarks/results/int8-w8a8-scan.json +2007 -0
- package/benchmarks/results/int8-w8a8-scan.md +160 -0
- package/benchmarks/results/int8-weight-channel.json +1413 -0
- package/benchmarks/results/int8-weight-channel.md +118 -0
- package/benchmarks/results/int8-weight-only.json +1437 -0
- package/benchmarks/results/int8-weight-only.md +118 -0
- package/benchmarks/results/kernel-webnn-final.json +1115 -0
- package/benchmarks/results/kernel-webnn-final.md +104 -0
- package/benchmarks/results/latest-optimized.json +375 -0
- package/benchmarks/results/latest-optimized.md +47 -0
- package/benchmarks/results/latest.json +391 -0
- package/benchmarks/results/latest.md +47 -0
- package/benchmarks/results/profile-baseline.json +331 -0
- package/benchmarks/results/profile-baseline.md +12 -0
- package/benchmarks/results/profile-conv2x.json +331 -0
- package/benchmarks/results/profile-conv2x.md +12 -0
- package/benchmarks/results/profile-fast-init.json +385 -0
- package/benchmarks/results/profile-fast-init.md +47 -0
- package/benchmarks/results/profile-fp16-fma.json +369 -0
- package/benchmarks/results/profile-fp16-fma.md +47 -0
- package/benchmarks/results/profile-fp16-tiled-decoder.json +369 -0
- package/benchmarks/results/profile-fp16-tiled-decoder.md +47 -0
- package/benchmarks/results/profile-fp16-tiled-encoder.json +369 -0
- package/benchmarks/results/profile-fp16-tiled-encoder.md +47 -0
- package/benchmarks/results/profile-fp16-unfused-pool.json +385 -0
- package/benchmarks/results/profile-fp16-unfused-pool.md +47 -0
- package/benchmarks/results/profile-input-major.json +347 -0
- package/benchmarks/results/profile-input-major.md +12 -0
- package/benchmarks/results/profile-k16.json +347 -0
- package/benchmarks/results/profile-k16.md +12 -0
- package/benchmarks/results/profile-k4.json +347 -0
- package/benchmarks/results/profile-k4.md +12 -0
- package/benchmarks/results/profile-pool-reuse.json +331 -0
- package/benchmarks/results/profile-pool-reuse.md +12 -0
- package/benchmarks/results/profile-precompiled.json +385 -0
- package/benchmarks/results/profile-precompiled.md +47 -0
- package/benchmarks/results/profile-static-channels.json +385 -0
- package/benchmarks/results/profile-static-channels.md +47 -0
- package/benchmarks/results/profile-static-io.json +385 -0
- package/benchmarks/results/profile-static-io.md +47 -0
- package/benchmarks/results/profile-tiled-conv.json +331 -0
- package/benchmarks/results/profile-tiled-conv.md +12 -0
- package/benchmarks/results/profile-tiled-decoder.json +347 -0
- package/benchmarks/results/profile-tiled-decoder.md +12 -0
- package/benchmarks/results/profile-tiled-matmul.json +331 -0
- package/benchmarks/results/profile-tiled-matmul.md +12 -0
- package/benchmarks/results/profile-unfused-decoder.json +379 -0
- package/benchmarks/results/profile-unfused-decoder.md +12 -0
- package/benchmarks/results/profile-unfused-pool.json +347 -0
- package/benchmarks/results/profile-unfused-pool.md +12 -0
- package/benchmarks/results/spatial-auto.json +575 -0
- package/benchmarks/results/spatial-auto.md +61 -0
- package/benchmarks/results/subgroup-smoke.json +1094 -0
- package/benchmarks/results/subgroup-smoke.md +104 -0
- package/benchmarks/results/webnn-smoke.json +739 -0
- package/benchmarks/results/webnn-smoke.md +76 -0
- package/dist/oidn.js +4189 -22603
- package/dist/oidn.umd.cjs +784 -5807
- package/lib/UNet.d.ts +66 -15
- package/lib/UNet.js +162 -257
- package/lib/UNet.js.map +1 -1
- package/lib/WGPUComputePass.d.ts +1 -1
- package/lib/WGPUComputePass.js +6 -4
- package/lib/WGPUComputePass.js.map +1 -1
- package/lib/backend.d.ts +8 -4
- package/lib/backend.js +36 -44
- package/lib/backend.js.map +1 -1
- package/lib/graphOptimizer.d.ts +54 -0
- package/lib/graphOptimizer.js +216 -0
- package/lib/graphOptimizer.js.map +1 -0
- package/lib/main.d.ts +33 -10
- package/lib/main.js +5 -0
- package/lib/main.js.map +1 -1
- package/lib/modelSpec.d.ts +80 -0
- package/lib/modelSpec.js +270 -0
- package/lib/modelSpec.js.map +1 -0
- package/lib/nativeUNet.d.ts +67 -0
- package/lib/nativeUNet.js +1735 -0
- package/lib/nativeUNet.js.map +1 -0
- package/lib/process.js +38 -35
- package/lib/process.js.map +1 -1
- package/lib/resourceTracker.d.ts +26 -0
- package/lib/resourceTracker.js +65 -0
- package/lib/resourceTracker.js.map +1 -0
- package/lib/tileScheduler.d.ts +33 -0
- package/lib/tileScheduler.js +86 -0
- package/lib/tileScheduler.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 +9 -5
- package/scripts/inspect-model.mjs +64 -0
- package/src/UNet.ts +236 -339
- package/src/WGPUComputePass.ts +6 -4
- package/src/backend.ts +42 -55
- package/src/graphOptimizer.ts +301 -0
- package/src/main.ts +71 -11
- package/src/modelSpec.ts +414 -0
- package/src/nativeUNet.ts +2256 -0
- package/src/process.ts +38 -36
- package/src/resourceTracker.ts +94 -0
- package/src/tileScheduler.ts +138 -0
- package/src/webnnUNet.ts +812 -0
- package/tests/modelSpec.test.mjs +128 -0
- package/tests/resourceLifecycle.test.mjs +383 -0
- package/tests/tileScheduler.test.mjs +90 -0
- package/lib/helper.d.ts +0 -4
- package/lib/helper.js +0 -33
- package/lib/helper.js.map +0 -1
- package/lib/kernels.d.ts +0 -1
- package/lib/kernels.js +0 -26
- package/lib/kernels.js.map +0 -1
- package/src/helper.ts +0 -43
- package/src/kernels.ts +0 -31
package/src/process.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { buffer, data } from '@tensorflow/tfjs';
|
|
2
1
|
import { WGPUComputePass } from './WGPUComputePass';
|
|
3
2
|
|
|
4
3
|
const a = 1.41283765e3;
|
|
@@ -157,18 +156,18 @@ export class GPUDataProcess {
|
|
|
157
156
|
},
|
|
158
157
|
{
|
|
159
158
|
label: 'inputSize',
|
|
160
|
-
type: '
|
|
161
|
-
data: new
|
|
159
|
+
type: 'vec2i',
|
|
160
|
+
data: new Int32Array(2)
|
|
162
161
|
},
|
|
163
162
|
{
|
|
164
163
|
label: 'outputSize',
|
|
165
|
-
type: '
|
|
166
|
-
data: new
|
|
164
|
+
type: 'vec2i',
|
|
165
|
+
data: new Int32Array(2)
|
|
167
166
|
},
|
|
168
167
|
{
|
|
169
168
|
label: 'inputOffset',
|
|
170
|
-
type: '
|
|
171
|
-
data: new
|
|
169
|
+
type: 'vec2i',
|
|
170
|
+
data: new Int32Array(2)
|
|
172
171
|
}
|
|
173
172
|
];
|
|
174
173
|
this._inputPassAux = new WGPUComputePass('inputPassAux', this._device, {
|
|
@@ -196,28 +195,28 @@ export class GPUDataProcess {
|
|
|
196
195
|
},
|
|
197
196
|
{
|
|
198
197
|
label: 'inputSize',
|
|
199
|
-
type: '
|
|
200
|
-
data: new
|
|
198
|
+
type: 'vec2i',
|
|
199
|
+
data: new Int32Array(2)
|
|
201
200
|
},
|
|
202
201
|
{
|
|
203
202
|
label: 'outputSize',
|
|
204
|
-
type: '
|
|
205
|
-
data: new
|
|
203
|
+
type: 'vec2i',
|
|
204
|
+
data: new Int32Array(2)
|
|
206
205
|
},
|
|
207
206
|
{
|
|
208
207
|
label: 'imageSize',
|
|
209
|
-
type: '
|
|
210
|
-
data: new
|
|
208
|
+
type: 'vec2i',
|
|
209
|
+
data: new Int32Array(2)
|
|
211
210
|
},
|
|
212
211
|
{
|
|
213
212
|
label: 'inputOffset',
|
|
214
|
-
type: '
|
|
215
|
-
data: new
|
|
213
|
+
type: 'vec2i',
|
|
214
|
+
data: new Int32Array(2)
|
|
216
215
|
},
|
|
217
216
|
{
|
|
218
217
|
label: 'outputOffset',
|
|
219
|
-
type: '
|
|
220
|
-
data: new
|
|
218
|
+
type: 'vec2i',
|
|
219
|
+
data: new Int32Array(2)
|
|
221
220
|
}
|
|
222
221
|
],
|
|
223
222
|
csDefine: '',
|
|
@@ -230,8 +229,8 @@ export class GPUDataProcess {
|
|
|
230
229
|
uniforms: [
|
|
231
230
|
{
|
|
232
231
|
label: 'size',
|
|
233
|
-
type: '
|
|
234
|
-
data: new
|
|
232
|
+
type: 'vec2i',
|
|
233
|
+
data: new Int32Array(2)
|
|
235
234
|
}
|
|
236
235
|
],
|
|
237
236
|
csMain: /*wgsl*/ `
|
|
@@ -279,12 +278,12 @@ fn PUForward(y: f32) -> f32 {
|
|
|
279
278
|
: `in_${inputName}[inIdx]`;
|
|
280
279
|
}
|
|
281
280
|
const commonCSMain = /* wgsl */ `
|
|
282
|
-
let x =
|
|
283
|
-
let y =
|
|
284
|
-
let inIdx =
|
|
281
|
+
let x = i32(globalId.x);
|
|
282
|
+
let y = i32(globalId.y);
|
|
283
|
+
let inIdx = (y + inputOffset.y) * inputSize.x + (x + inputOffset.x);
|
|
285
284
|
let col = ${readInputCode('color')};
|
|
286
285
|
|
|
287
|
-
let outIdx =
|
|
286
|
+
let outIdx = y * outputSize.x + x;
|
|
288
287
|
|
|
289
288
|
if (${denoiseAlpha}) {
|
|
290
289
|
// Denoise the inversed alpha. Or the anti aliased edge will be too dark after denoised
|
|
@@ -328,13 +327,13 @@ fn PUInverse(y: f32) -> f32 {
|
|
|
328
327
|
}
|
|
329
328
|
`,
|
|
330
329
|
csMain: /* wgsl */ `
|
|
331
|
-
let x =
|
|
332
|
-
let y =
|
|
330
|
+
let x = i32(globalId.x);
|
|
331
|
+
let y = i32(globalId.y);
|
|
333
332
|
if (x >= outputSize.x || y >= outputSize.y) {
|
|
334
333
|
return;
|
|
335
334
|
}
|
|
336
|
-
let inIdx =
|
|
337
|
-
let outIdx =
|
|
335
|
+
let inIdx = (y + inputOffset.y) * inputSize.x + x + inputOffset.x;
|
|
336
|
+
let outIdx = (y + outputOffset.y) * imageSize.x + x + outputOffset.x;
|
|
338
337
|
let col = in_color[inIdx];
|
|
339
338
|
let raw = ${
|
|
340
339
|
isInputTexture
|
|
@@ -360,18 +359,18 @@ else {
|
|
|
360
359
|
}
|
|
361
360
|
|
|
362
361
|
setImageSize(w: number, h: number) {
|
|
363
|
-
this._inputPassAux.setUniform('inputSize', new
|
|
364
|
-
this._inputPassColor.setUniform('inputSize', new
|
|
365
|
-
this._outputPass.setUniform('imageSize', new
|
|
362
|
+
this._inputPassAux.setUniform('inputSize', new Int32Array([w, h]));
|
|
363
|
+
this._inputPassColor.setUniform('inputSize', new Int32Array([w, h]));
|
|
364
|
+
this._outputPass.setUniform('imageSize', new Int32Array([w, h]));
|
|
366
365
|
this._outputPass.setSize(w, h);
|
|
367
366
|
this._copyPass.setSize(w, h);
|
|
368
|
-
this._copyPass.setUniform('size', new
|
|
367
|
+
this._copyPass.setUniform('size', new Int32Array([w, h]));
|
|
369
368
|
}
|
|
370
369
|
|
|
371
370
|
setInputTile(tile: Tile) {
|
|
372
|
-
const size = new
|
|
371
|
+
const size = new Int32Array([tile.width, tile.height]);
|
|
373
372
|
[this._inputPassAux, this._inputPassColor].forEach((inputPass) => {
|
|
374
|
-
inputPass.setUniform('inputOffset', new
|
|
373
|
+
inputPass.setUniform('inputOffset', new Int32Array([tile.x, tile.y]));
|
|
375
374
|
inputPass.setUniform('outputSize', size);
|
|
376
375
|
inputPass.setSize(size[0], size[1]);
|
|
377
376
|
});
|
|
@@ -381,14 +380,14 @@ else {
|
|
|
381
380
|
|
|
382
381
|
setOutputTile(dstTile: Tile, srcTile: Tile) {
|
|
383
382
|
const outputPass = this._outputPass;
|
|
384
|
-
const size = new
|
|
383
|
+
const size = new Int32Array([dstTile.width, dstTile.height]);
|
|
385
384
|
const dx = dstTile.x - srcTile.x;
|
|
386
385
|
const dy = dstTile.y - srcTile.y;
|
|
387
386
|
outputPass.setUniform('outputSize', size);
|
|
388
|
-
outputPass.setUniform('inputOffset', new
|
|
387
|
+
outputPass.setUniform('inputOffset', new Int32Array([dx, dy]));
|
|
389
388
|
outputPass.setUniform(
|
|
390
389
|
'outputOffset',
|
|
391
|
-
new
|
|
390
|
+
new Int32Array([dstTile.x, dstTile.y])
|
|
392
391
|
);
|
|
393
392
|
outputPass.setExecuteSize(size[0], size[1]);
|
|
394
393
|
}
|
|
@@ -490,5 +489,8 @@ else {
|
|
|
490
489
|
dispose() {
|
|
491
490
|
this._outputPass.dispose();
|
|
492
491
|
this._inputPassAux.dispose();
|
|
492
|
+
this._inputPassColor.dispose();
|
|
493
|
+
// copyPass borrows outputPass's target and must not destroy it again.
|
|
494
|
+
this._copyPass.dispose(false);
|
|
493
495
|
}
|
|
494
496
|
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export interface DynamicTileOptions {
|
|
2
|
+
/** Smallest output tile edge. Defaults to 256. */
|
|
3
|
+
minTileSize?: number;
|
|
4
|
+
/** Initial output tile edge. Defaults to 384. */
|
|
5
|
+
initialTileSize?: number;
|
|
6
|
+
/** Desired upper bound for one tile's GPU latency. Defaults to 16 ms. */
|
|
7
|
+
targetTileTimeMs?: number;
|
|
8
|
+
/** Amount added or removed after each completed execution. Defaults to 128. */
|
|
9
|
+
adjustmentStep?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type DynamicTileSetting = boolean | DynamicTileOptions;
|
|
13
|
+
|
|
14
|
+
const defaultMinTileSize = 256;
|
|
15
|
+
const defaultInitialTileSize = 384;
|
|
16
|
+
const defaultTargetTileTimeMs = 16;
|
|
17
|
+
const defaultAdjustmentStep = 128;
|
|
18
|
+
export const OIDN_TILE_ALIGNMENT = 16;
|
|
19
|
+
|
|
20
|
+
function alignUp(value: number, alignment: number) {
|
|
21
|
+
return Math.ceil(value / alignment) * alignment;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function alignDown(value: number, alignment: number) {
|
|
25
|
+
return Math.floor(value / alignment) * alignment;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function clamp(value: number, min: number, max: number) {
|
|
29
|
+
return Math.min(Math.max(value, min), max);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function median(values: number[]) {
|
|
33
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
34
|
+
const middle = Math.floor(sorted.length / 2);
|
|
35
|
+
return sorted.length % 2
|
|
36
|
+
? sorted[middle]
|
|
37
|
+
: (sorted[middle - 1] + sorted[middle]) / 2;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Fits one output dimension without exceeding the configured tile cap. */
|
|
41
|
+
export function fitTileDimension(imageSize: number, maxTileSize: number) {
|
|
42
|
+
return imageSize <= maxTileSize
|
|
43
|
+
? Math.min(alignUp(imageSize, OIDN_TILE_ALIGNMENT), maxTileSize)
|
|
44
|
+
: maxTileSize;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Chooses an output tile size from completed GPU timings. A size change is
|
|
49
|
+
* applied to the next execution so one in-flight tiled pass keeps a stable
|
|
50
|
+
* overlap, allocation plan, and pipeline set.
|
|
51
|
+
*/
|
|
52
|
+
export class DynamicTileController {
|
|
53
|
+
readonly enabled: boolean;
|
|
54
|
+
readonly maxTileSize: number;
|
|
55
|
+
readonly minTileSize: number;
|
|
56
|
+
readonly targetTileTimeMs: number;
|
|
57
|
+
|
|
58
|
+
private _tileSize: number;
|
|
59
|
+
private _adjustmentStep: number;
|
|
60
|
+
|
|
61
|
+
constructor(maxTileSize: number, setting: DynamicTileSetting = true) {
|
|
62
|
+
const options = typeof setting === 'object' ? setting : {};
|
|
63
|
+
this.enabled = setting !== false;
|
|
64
|
+
this.maxTileSize = Math.max(
|
|
65
|
+
OIDN_TILE_ALIGNMENT,
|
|
66
|
+
alignDown(maxTileSize, OIDN_TILE_ALIGNMENT)
|
|
67
|
+
);
|
|
68
|
+
this.minTileSize = clamp(
|
|
69
|
+
alignUp(options.minTileSize ?? defaultMinTileSize, OIDN_TILE_ALIGNMENT),
|
|
70
|
+
OIDN_TILE_ALIGNMENT,
|
|
71
|
+
this.maxTileSize
|
|
72
|
+
);
|
|
73
|
+
this.targetTileTimeMs = Math.max(
|
|
74
|
+
1,
|
|
75
|
+
options.targetTileTimeMs ?? defaultTargetTileTimeMs
|
|
76
|
+
);
|
|
77
|
+
this._adjustmentStep = Math.max(
|
|
78
|
+
OIDN_TILE_ALIGNMENT,
|
|
79
|
+
alignUp(
|
|
80
|
+
options.adjustmentStep ?? defaultAdjustmentStep,
|
|
81
|
+
OIDN_TILE_ALIGNMENT
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
this._tileSize = this.enabled
|
|
85
|
+
? clamp(
|
|
86
|
+
alignUp(
|
|
87
|
+
options.initialTileSize ?? defaultInitialTileSize,
|
|
88
|
+
OIDN_TILE_ALIGNMENT
|
|
89
|
+
),
|
|
90
|
+
this.minTileSize,
|
|
91
|
+
this.maxTileSize
|
|
92
|
+
)
|
|
93
|
+
: this.maxTileSize;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
get tileSize() {
|
|
97
|
+
return this._tileSize;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Returns true when the next execution should use a different tile size. */
|
|
101
|
+
observe(tileTimesMs: number[]) {
|
|
102
|
+
if (!this.enabled || tileTimesMs.length === 0) return false;
|
|
103
|
+
|
|
104
|
+
const validTimes = tileTimesMs.filter(
|
|
105
|
+
(duration) => Number.isFinite(duration) && duration >= 0
|
|
106
|
+
);
|
|
107
|
+
if (validTimes.length === 0) return false;
|
|
108
|
+
|
|
109
|
+
const observedTime = median(validTimes);
|
|
110
|
+
let nextTileSize = this._tileSize;
|
|
111
|
+
if (observedTime > this.targetTileTimeMs * 1.25) {
|
|
112
|
+
nextTileSize -= this._adjustmentStep;
|
|
113
|
+
} else if (observedTime < this.targetTileTimeMs * 0.65) {
|
|
114
|
+
nextTileSize += this._adjustmentStep;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
nextTileSize = clamp(
|
|
118
|
+
alignUp(nextTileSize, OIDN_TILE_ALIGNMENT),
|
|
119
|
+
this.minTileSize,
|
|
120
|
+
this.maxTileSize
|
|
121
|
+
);
|
|
122
|
+
if (nextTileSize === this._tileSize) return false;
|
|
123
|
+
this._tileSize = nextTileSize;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Waits for all work submitted before this call and tolerates device loss. */
|
|
129
|
+
export async function waitForSubmittedGPUWork(
|
|
130
|
+
queue: Pick<GPUQueue, 'onSubmittedWorkDone'>
|
|
131
|
+
) {
|
|
132
|
+
try {
|
|
133
|
+
await queue.onSubmittedWorkDone();
|
|
134
|
+
} catch {
|
|
135
|
+
// tileExecute has no error callback. Preserve its completion/cancellation
|
|
136
|
+
// behavior and let the owning GPUDevice report device loss separately.
|
|
137
|
+
}
|
|
138
|
+
}
|