@glissade/cli 0.27.0-pre.0 → 0.27.1-pre.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/dist/layerCache.js +119 -0
- package/dist/render.js +10 -2
- package/package.json +10 -10
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { deflateSync, inflateSync } from "node:zlib";
|
|
5
|
+
//#region src/layerCache.ts
|
|
6
|
+
/**
|
|
7
|
+
* §3.5 disk layer-cache (0.27.1): the fs-backed `LayerStore` the CLI injects into
|
|
8
|
+
* the Skia backend. It persists a `cache:true` group's DEVICE-space raster across
|
|
9
|
+
* renders, so an expensive static subtree (a blurred mesh backdrop) rasterizes
|
|
10
|
+
* ONCE and re-blits on later runs — surviving a re-narration that defeats the
|
|
11
|
+
* whole-frame cache (the backdrop's sub-DisplayList is unchanged; only captions /
|
|
12
|
+
* timing move). Sibling of `frameCache.ts`; same on-disk discipline (deflate,
|
|
13
|
+
* atomic write, content-addressed filenames).
|
|
14
|
+
*
|
|
15
|
+
* The compositor supplies the raw key `<sub-DisplayList fnv1a>@<deviceTransformKey>`;
|
|
16
|
+
* this store SALTS it with the toolchain version ⊕ backend caps ⊕ frame size, so a
|
|
17
|
+
* version / capability / resolution change can never serve a stale raster. A
|
|
18
|
+
* restored RGBA composites byte-identically to a fresh raster (the compositor
|
|
19
|
+
* round-trips it through putImageData) — the same contract the frame cache holds.
|
|
20
|
+
*/
|
|
21
|
+
const MAGIC = 1196641329;
|
|
22
|
+
const HEADER_BYTES = 20;
|
|
23
|
+
const BOUNDS_BYTES = 32;
|
|
24
|
+
const FLAG_HAS_BOUNDS = 1;
|
|
25
|
+
const FLAG_UNBOUNDED = 2;
|
|
26
|
+
function encodeEntry(e) {
|
|
27
|
+
const header = Buffer.alloc(HEADER_BYTES);
|
|
28
|
+
header.writeUInt32BE(MAGIC, 0);
|
|
29
|
+
header.writeUInt32BE(1, 4);
|
|
30
|
+
header.writeUInt32BE(e.w, 8);
|
|
31
|
+
header.writeUInt32BE(e.h, 12);
|
|
32
|
+
header.writeUInt32BE((e.bounds ? FLAG_HAS_BOUNDS : 0) | (e.unbounded ? FLAG_UNBOUNDED : 0), 16);
|
|
33
|
+
const parts = [header];
|
|
34
|
+
if (e.bounds) {
|
|
35
|
+
const b = Buffer.alloc(BOUNDS_BYTES);
|
|
36
|
+
b.writeDoubleBE(e.bounds.minX, 0);
|
|
37
|
+
b.writeDoubleBE(e.bounds.minY, 8);
|
|
38
|
+
b.writeDoubleBE(e.bounds.maxX, 16);
|
|
39
|
+
b.writeDoubleBE(e.bounds.maxY, 24);
|
|
40
|
+
parts.push(b);
|
|
41
|
+
}
|
|
42
|
+
parts.push(deflateSync(Buffer.from(e.rgba.buffer, e.rgba.byteOffset, e.rgba.byteLength)));
|
|
43
|
+
return Buffer.concat(parts);
|
|
44
|
+
}
|
|
45
|
+
function decodeEntry(buf) {
|
|
46
|
+
if (buf.length < HEADER_BYTES || buf.readUInt32BE(0) !== MAGIC) throw new Error("corrupt .gsl layer entry (bad magic)");
|
|
47
|
+
const w = buf.readUInt32BE(8);
|
|
48
|
+
const h = buf.readUInt32BE(12);
|
|
49
|
+
const flags = buf.readUInt32BE(16);
|
|
50
|
+
let off = HEADER_BYTES;
|
|
51
|
+
let bounds = null;
|
|
52
|
+
if (flags & FLAG_HAS_BOUNDS) {
|
|
53
|
+
bounds = {
|
|
54
|
+
minX: buf.readDoubleBE(off),
|
|
55
|
+
minY: buf.readDoubleBE(off + 8),
|
|
56
|
+
maxX: buf.readDoubleBE(off + 16),
|
|
57
|
+
maxY: buf.readDoubleBE(off + 24)
|
|
58
|
+
};
|
|
59
|
+
off += BOUNDS_BYTES;
|
|
60
|
+
}
|
|
61
|
+
const raw = inflateSync(buf.subarray(off));
|
|
62
|
+
return {
|
|
63
|
+
rgba: new Uint8ClampedArray(raw.buffer, raw.byteOffset, raw.byteLength),
|
|
64
|
+
w,
|
|
65
|
+
h,
|
|
66
|
+
bounds,
|
|
67
|
+
unbounded: (flags & FLAG_UNBOUNDED) !== 0
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var LayerCache = class {
|
|
71
|
+
dir;
|
|
72
|
+
mode;
|
|
73
|
+
salt;
|
|
74
|
+
stats = {
|
|
75
|
+
hits: 0,
|
|
76
|
+
misses: 0,
|
|
77
|
+
stored: 0
|
|
78
|
+
};
|
|
79
|
+
constructor(opts) {
|
|
80
|
+
this.dir = opts.dir;
|
|
81
|
+
this.mode = opts.mode;
|
|
82
|
+
this.salt = opts.salt;
|
|
83
|
+
if (this.mode !== "off") mkdirSync(this.dir, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
fileFor(key) {
|
|
86
|
+
const h = createHash("sha256").update(this.salt).update("\0").update(key).digest("hex");
|
|
87
|
+
return join(this.dir, `${h}.gsl`);
|
|
88
|
+
}
|
|
89
|
+
get(key) {
|
|
90
|
+
if (this.mode === "off") return void 0;
|
|
91
|
+
const f = this.fileFor(key);
|
|
92
|
+
if (!existsSync(f)) {
|
|
93
|
+
this.stats.misses++;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const e = decodeEntry(readFileSync(f));
|
|
98
|
+
this.stats.hits++;
|
|
99
|
+
return e;
|
|
100
|
+
} catch {
|
|
101
|
+
this.stats.misses++;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
put(key, entry) {
|
|
106
|
+
if (this.mode !== "read-write") return;
|
|
107
|
+
const f = this.fileFor(key);
|
|
108
|
+
if (existsSync(f)) return;
|
|
109
|
+
const tmp = `${f}.tmp${globalThis.process?.pid ?? 0}`;
|
|
110
|
+
writeFileSync(tmp, encodeEntry(entry));
|
|
111
|
+
renameSync(tmp, f);
|
|
112
|
+
this.stats.stored++;
|
|
113
|
+
}
|
|
114
|
+
getStats() {
|
|
115
|
+
return { ...this.stats };
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
//#endregion
|
|
119
|
+
export { LayerCache };
|
package/dist/render.js
CHANGED
|
@@ -420,11 +420,19 @@ async function render(opts) {
|
|
|
420
420
|
mode: opts.cache.mode,
|
|
421
421
|
...opts.cache.maxSize !== void 0 ? { maxSize: opts.cache.maxSize } : {}
|
|
422
422
|
});
|
|
423
|
+
const version = glissadeVersion();
|
|
424
|
+
const caps = capsId(backend.caps);
|
|
423
425
|
keyCtx = {
|
|
424
|
-
version
|
|
425
|
-
capsId:
|
|
426
|
+
version,
|
|
427
|
+
capsId: caps,
|
|
426
428
|
assetsDigest: combineAssetDigests(assetDigests)
|
|
427
429
|
};
|
|
430
|
+
const { LayerCache } = await import("./layerCache.js");
|
|
431
|
+
backend.setLayerStore(new LayerCache({
|
|
432
|
+
dir: join(opts.cache.dir, "layers"),
|
|
433
|
+
mode: opts.cache.mode,
|
|
434
|
+
salt: `${version}|${caps}|${scene.size.w}x${scene.size.h}`
|
|
435
|
+
}));
|
|
428
436
|
}
|
|
429
437
|
let videoOut;
|
|
430
438
|
let remuxDigest;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/cli",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.1-pre.0",
|
|
4
4
|
"description": "glissade CLI: headless rendering via backend-skia (+ FFmpeg mux).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
"@napi-rs/canvas": "^0.1.65",
|
|
24
24
|
"esbuild": "0.28.0",
|
|
25
25
|
"jiti": "^2.4.2",
|
|
26
|
-
"@glissade/backend-skia": "0.27.
|
|
27
|
-
"@glissade/core": "0.27.
|
|
28
|
-
"@glissade/interact": "0.27.
|
|
29
|
-
"@glissade/lottie": "0.27.
|
|
30
|
-
"@glissade/svg": "0.27.
|
|
31
|
-
"@glissade/narrate": "0.27.
|
|
32
|
-
"@glissade/player": "0.27.
|
|
33
|
-
"@glissade/scene": "0.27.
|
|
34
|
-
"@glissade/sfx": "0.27.
|
|
26
|
+
"@glissade/backend-skia": "0.27.1-pre.0",
|
|
27
|
+
"@glissade/core": "0.27.1-pre.0",
|
|
28
|
+
"@glissade/interact": "0.27.1-pre.0",
|
|
29
|
+
"@glissade/lottie": "0.27.1-pre.0",
|
|
30
|
+
"@glissade/svg": "0.27.1-pre.0",
|
|
31
|
+
"@glissade/narrate": "0.27.1-pre.0",
|
|
32
|
+
"@glissade/player": "0.27.1-pre.0",
|
|
33
|
+
"@glissade/scene": "0.27.1-pre.0",
|
|
34
|
+
"@glissade/sfx": "0.27.1-pre.0"
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|