@framecast/rt 1.0.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/LICENSE +18 -0
- package/README.md +78 -0
- package/index.d.ts +57 -0
- package/package.json +43 -0
- package/rt.js +1254 -0
- package/sr.js +205 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@framecast/rt - the Framecast WebGPU inference runtime (rt.js, sr.js)
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2026 MONZik
|
|
4
|
+
|
|
5
|
+
This library is free software: you can redistribute it and/or modify it
|
|
6
|
+
under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
option) any later version.
|
|
9
|
+
|
|
10
|
+
This library is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
for more details: https://www.gnu.org/licenses/lgpl-3.0.html
|
|
14
|
+
|
|
15
|
+
Note: this LGPL grant covers the runtime library as distributed in this
|
|
16
|
+
package. The Framecast repository as a whole (extension, tools, training
|
|
17
|
+
code) remains AGPL-3.0; the model weights are licensed separately (see
|
|
18
|
+
WEIGHTS_LICENSE.md in the repository).
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @framecast/rt
|
|
2
|
+
|
|
3
|
+
Real-time neural frame interpolation on **raw WebGPU** - the runtime behind
|
|
4
|
+
the [Framecast](https://github.com/MONZikWasTaken/Framecast) extension,
|
|
5
|
+
packaged as a library. Hand-written WGSL compute kernels, no ML framework,
|
|
6
|
+
~3 ms per generated frame at 720p on a mid-range GPU (RTX 4060 Ti).
|
|
7
|
+
|
|
8
|
+
Requires a browser with WebGPU and `shader-f16` (Chrome 121+; Apple Silicon
|
|
9
|
+
works).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npm i @framecast/rt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Weights are **not** bundled - fetch them once from the Framecast release
|
|
18
|
+
(2.9 MB) and cache as you like:
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
const BASE = 'https://github.com/MONZikWasTaken/Framecast/releases/download/v1.0.0';
|
|
22
|
+
const [bin, manifest] = await Promise.all([
|
|
23
|
+
fetch(`${BASE}/rt_v7s.bin`).then(r => r.arrayBuffer()),
|
|
24
|
+
fetch(`${BASE}/rt_v7s.json`).then(r => r.json()),
|
|
25
|
+
]);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Interpolate between two frames
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
import { createRT } from '@framecast/rt';
|
|
32
|
+
|
|
33
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
34
|
+
const device = await adapter.requestDevice({
|
|
35
|
+
requiredFeatures: adapter.features.has('shader-f16') ? ['shader-f16'] : [],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// dimensions must be divisible by 16
|
|
39
|
+
const rt = await createRT(device, {
|
|
40
|
+
w: 1280, h: 720,
|
|
41
|
+
weightsBin: bin, weightsManifest: manifest,
|
|
42
|
+
textureInput: true, textureOutput: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// frameA/frameB are GPUTextures (rgba8unorm, TEXTURE_BINDING);
|
|
46
|
+
// out is rgba8unorm with STORAGE_BINDING
|
|
47
|
+
rt.prepPair(frameA, frameB); // t-free trunk, once per pair
|
|
48
|
+
rt.runT(0.5, out); // one mid; call again with any t in (0,1)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`prepPair` + `runT` is the real-time path: the trunk runs once per frame
|
|
52
|
+
pair, each additional mid costs only the small t-conditioned head - that is
|
|
53
|
+
what makes 4x-6x factors affordable.
|
|
54
|
+
|
|
55
|
+
For one-off use (benchmarks, offline tools) there is also a buffer-mode API:
|
|
56
|
+
`rt.run(rgbaA, rgbaB, t)` takes and returns `Uint8Array` RGBA pixels.
|
|
57
|
+
|
|
58
|
+
## Squeeze the last 20%
|
|
59
|
+
|
|
60
|
+
Kernel shapes are GPU-specific. Run the autotuner once per machine and pass
|
|
61
|
+
the result in:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import { createRT, tuneConvRB } from '@framecast/rt';
|
|
65
|
+
|
|
66
|
+
const tune = await tuneConvRB(device, { ci: 192, co: 192, w16: 80, h16: 45 });
|
|
67
|
+
localStorage.setItem('fcTune', JSON.stringify(tune));
|
|
68
|
+
// ...next session:
|
|
69
|
+
const rt = await createRT(device, { ...opts, convTune: JSON.parse(localStorage.getItem('fcTune')) });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
**LGPL-3.0-or-later.** Embed it in anything, including commercial products;
|
|
75
|
+
modifications to the library itself must be published. The model weights are
|
|
76
|
+
licensed separately (non-commercial - see
|
|
77
|
+
[WEIGHTS_LICENSE](https://github.com/MONZikWasTaken/Framecast/blob/main/WEIGHTS_LICENSE.md));
|
|
78
|
+
for commercial weight licensing, get in touch.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Kernel autotune result for the register-blocked conv (per GPU + shape). */
|
|
2
|
+
export interface ConvTune {
|
|
3
|
+
coc: number;
|
|
4
|
+
slab: number;
|
|
5
|
+
sg?: boolean;
|
|
6
|
+
ms?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface CreateRTOptions {
|
|
10
|
+
/** Output width in pixels. Must be divisible by 16. */
|
|
11
|
+
w: number;
|
|
12
|
+
/** Output height in pixels. Must be divisible by 16. */
|
|
13
|
+
h: number;
|
|
14
|
+
/** Raw weights blob (the released .bin file). */
|
|
15
|
+
weightsBin: ArrayBuffer;
|
|
16
|
+
/** Weights manifest (the released .json file, parsed). */
|
|
17
|
+
weightsManifest: Record<string, { offset: number; shape: number[] }>;
|
|
18
|
+
/** Kernel tune from tuneConvRB; omit for safe defaults. */
|
|
19
|
+
convTune?: ConvTune | null;
|
|
20
|
+
/** Inputs are GPUTextures instead of RGBA8 CPU buffers. */
|
|
21
|
+
textureInput?: boolean;
|
|
22
|
+
/** Mids are written into GPUTextures instead of read back to CPU. */
|
|
23
|
+
textureOutput?: boolean;
|
|
24
|
+
/** Bake the static-region guard into the flow kernel. */
|
|
25
|
+
staticGuard?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RT {
|
|
29
|
+
/** Buffer mode only: interpolate one mid between two RGBA8 frames. */
|
|
30
|
+
run(rgbaA: Uint8Array, rgbaB: Uint8Array, t?: number): Promise<Uint8Array>;
|
|
31
|
+
/**
|
|
32
|
+
* Batched mids for every t. Buffer mode returns RGBA8 arrays; texture mode
|
|
33
|
+
* writes into outTexs and resolves null (nothing crosses the bus).
|
|
34
|
+
*/
|
|
35
|
+
runMulti(
|
|
36
|
+
a: Uint8Array | GPUTexture,
|
|
37
|
+
b: Uint8Array | GPUTexture,
|
|
38
|
+
ts: number[],
|
|
39
|
+
outTexs?: GPUTexture[],
|
|
40
|
+
): Promise<Uint8Array[] | null>;
|
|
41
|
+
/** Texture mode: run the t-free trunk once for a frame pair. */
|
|
42
|
+
prepPair(a: GPUTexture, b: GPUTexture): void;
|
|
43
|
+
/** Texture mode: one mid at timestep t into outTex (call prepPair first). */
|
|
44
|
+
runT(t: number, outTex: GPUTexture): void;
|
|
45
|
+
/** Per-pass GPU timings (requires timestamp-query). */
|
|
46
|
+
profile(rgbaA: Uint8Array, rgbaB: Uint8Array): Promise<string>;
|
|
47
|
+
readonly w: number;
|
|
48
|
+
readonly h: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createRT(device: GPUDevice, opts: CreateRTOptions): Promise<RT>;
|
|
52
|
+
|
|
53
|
+
/** Bench conv kernel variants on the real shape; persist the result per GPU. */
|
|
54
|
+
export function tuneConvRB(
|
|
55
|
+
device: GPUDevice,
|
|
56
|
+
shape: { ci: number; co: number; w16: number; h16: number },
|
|
57
|
+
): Promise<ConvTune>;
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@framecast/rt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Real-time neural frame interpolation on raw WebGPU: hand-written WGSL kernels, 2.9 MB model, ~3 ms per generated frame on a mid-range GPU. The runtime behind the Framecast extension.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./rt.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./rt.js",
|
|
9
|
+
"./sr": "./sr.js"
|
|
10
|
+
},
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"rt.js",
|
|
14
|
+
"sr.js",
|
|
15
|
+
"index.d.ts",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prepack": "node prepack.mjs"
|
|
21
|
+
},
|
|
22
|
+
"license": "LGPL-3.0-or-later",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/MONZikWasTaken/Framecast.git",
|
|
26
|
+
"directory": "packages/rt"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/MONZikWasTaken/Framecast#readme",
|
|
29
|
+
"bugs": "https://github.com/MONZikWasTaken/Framecast/issues",
|
|
30
|
+
"keywords": [
|
|
31
|
+
"webgpu",
|
|
32
|
+
"wgsl",
|
|
33
|
+
"frame-interpolation",
|
|
34
|
+
"video",
|
|
35
|
+
"rife",
|
|
36
|
+
"machine-learning",
|
|
37
|
+
"real-time"
|
|
38
|
+
],
|
|
39
|
+
"author": "MONZik",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
}
|
|
43
|
+
}
|