@myzonerocks/gosslens 0.12.0 → 0.13.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/README.md +52 -2
- package/dist/src/index.d.ts +301 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1079 -13
- package/dist/src/index.js.map +1 -1
- package/dist/src/screen-capture.d.ts +16 -0
- package/dist/src/screen-capture.d.ts.map +1 -0
- package/dist/src/screen-capture.js +75 -0
- package/dist/src/screen-capture.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +1273 -11
- package/src/screen-capture.ts +99 -0
package/src/index.ts
CHANGED
|
@@ -7,6 +7,159 @@
|
|
|
7
7
|
// rotation) straight to the engine.
|
|
8
8
|
|
|
9
9
|
export const GOSS_OK = 0;
|
|
10
|
+
/// The session's scope does not carry the verb an op needs. A host can grant this
|
|
11
|
+
/// one; GOSS_UNSUPPORTED it cannot.
|
|
12
|
+
export const GOSS_OUT_OF_SCOPE = 9;
|
|
13
|
+
|
|
14
|
+
/// What a scope may carry. Reading is covered by the snapshot sections; these are
|
|
15
|
+
/// the things that change something or reach a resource.
|
|
16
|
+
export const enum GossVerb {
|
|
17
|
+
Annotate = 0,
|
|
18
|
+
Egress = 1,
|
|
19
|
+
Record = 2,
|
|
20
|
+
Remember = 3,
|
|
21
|
+
SearchMemory = 4,
|
|
22
|
+
OpenClip = 5,
|
|
23
|
+
CaptureScreen = 6,
|
|
24
|
+
SubmitFrame = 7,
|
|
25
|
+
SubmitWorld = 8,
|
|
26
|
+
SubmitAudio = 9,
|
|
27
|
+
AudioOut = 10,
|
|
28
|
+
SealMemory = 11,
|
|
29
|
+
ActivateLens = 12,
|
|
30
|
+
LoadModel = 13,
|
|
31
|
+
EnableTracking = 14,
|
|
32
|
+
Retouch = 15,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// The mask for a set of verbs, which is what setScope takes.
|
|
36
|
+
export function gossVerbMask(verbs: GossVerb[]): number {
|
|
37
|
+
return verbs.reduce((mask, verb) => mask | (1 << verb), 0);
|
|
38
|
+
}
|
|
39
|
+
export const GOSS_AGAIN = 7;
|
|
40
|
+
|
|
41
|
+
/// What this build's media backend declares it encodes.
|
|
42
|
+
export interface GossMediaCapabilities {
|
|
43
|
+
videoCodecs: number;
|
|
44
|
+
audioCodecs: number;
|
|
45
|
+
containers: number;
|
|
46
|
+
maxWidth: number;
|
|
47
|
+
maxHeight: number;
|
|
48
|
+
maxBitDepth: number;
|
|
49
|
+
hdr: boolean;
|
|
50
|
+
zeroCopy: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// What the engine will let this process capture. Scale is the field to carry
|
|
54
|
+
/// through: a point sent back without it lands at half its place on a retina
|
|
55
|
+
/// display.
|
|
56
|
+
export interface GossScreenSurface {
|
|
57
|
+
id: number;
|
|
58
|
+
kind: number;
|
|
59
|
+
title: string;
|
|
60
|
+
logicalWidth: number;
|
|
61
|
+
logicalHeight: number;
|
|
62
|
+
originX: number;
|
|
63
|
+
originY: number;
|
|
64
|
+
scale: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// What the frame says at one place in it. The quadrilateral is in normalized
|
|
68
|
+
/// frame space and in reading order, so a coordinate sent back maps to a pixel.
|
|
69
|
+
export interface GossReading {
|
|
70
|
+
text: string;
|
|
71
|
+
quad: number[];
|
|
72
|
+
confidence: number;
|
|
73
|
+
origin: number;
|
|
74
|
+
script: number;
|
|
75
|
+
direction: number;
|
|
76
|
+
trackId: number;
|
|
77
|
+
line: number;
|
|
78
|
+
paragraph: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// One thing an agent draws back into the frame.
|
|
82
|
+
export interface GossAnnotation {
|
|
83
|
+
id: number;
|
|
84
|
+
kind: number;
|
|
85
|
+
space?: number;
|
|
86
|
+
rect?: [number, number, number, number];
|
|
87
|
+
trackId?: number;
|
|
88
|
+
colour?: [number, number, number, number];
|
|
89
|
+
z?: number;
|
|
90
|
+
opacity?: number;
|
|
91
|
+
lifetimeKind?: number;
|
|
92
|
+
lifetimeValue?: number;
|
|
93
|
+
onLost?: number;
|
|
94
|
+
value?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// What the brain sees and what it costs.
|
|
98
|
+
export interface GossEgressConfig {
|
|
99
|
+
targetLongEdge?: number;
|
|
100
|
+
format?: number;
|
|
101
|
+
quality?: number;
|
|
102
|
+
maxFps?: number;
|
|
103
|
+
maxBytesPerSecond?: number;
|
|
104
|
+
source?: number;
|
|
105
|
+
trigger?: number;
|
|
106
|
+
changeThreshold?: number;
|
|
107
|
+
keyframeIntervalUs?: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Why a frame was or was not sent, so a gateway can explain itself.
|
|
111
|
+
export interface GossEgressDecision {
|
|
112
|
+
send: boolean;
|
|
113
|
+
reason: number;
|
|
114
|
+
changeScore: number;
|
|
115
|
+
sinceLastUs: number;
|
|
116
|
+
sentTotal: number;
|
|
117
|
+
heldTotal: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// One thing that happened. What a and b mean is per kind.
|
|
121
|
+
export interface GossEvent {
|
|
122
|
+
kind: number;
|
|
123
|
+
sequence: number;
|
|
124
|
+
timestampUs: number;
|
|
125
|
+
a: number;
|
|
126
|
+
b: number;
|
|
127
|
+
value: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// The event kinds, mirroring goss_event_kind.
|
|
131
|
+
export const enum GossEventKind {
|
|
132
|
+
FaceAppeared = 1,
|
|
133
|
+
FaceLost = 2,
|
|
134
|
+
FaceCountChanged = 3,
|
|
135
|
+
HandAppeared = 4,
|
|
136
|
+
HandLost = 5,
|
|
137
|
+
GestureRecognised = 6,
|
|
138
|
+
BodyAppeared = 7,
|
|
139
|
+
BodyLost = 8,
|
|
140
|
+
ActionRecognised = 9,
|
|
141
|
+
TrackingStateChanged = 10,
|
|
142
|
+
AudioBeat = 22,
|
|
143
|
+
LensActivated = 25,
|
|
144
|
+
LensNodeDegraded = 26,
|
|
145
|
+
LensNodeFailed = 27,
|
|
146
|
+
DegradeLevelChanged = 30,
|
|
147
|
+
PoolExhausted = 31,
|
|
148
|
+
RecordingStarted = 32,
|
|
149
|
+
RecordingPaused = 33,
|
|
150
|
+
RecordingResumed = 34,
|
|
151
|
+
RecordingStopped = 35,
|
|
152
|
+
Interruption = 36,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// What interrupted a recording, as the page saw it.
|
|
156
|
+
export const enum GossInterruption {
|
|
157
|
+
Pause = 0,
|
|
158
|
+
CameraLost = 1,
|
|
159
|
+
AudioRoute = 2,
|
|
160
|
+
Backgrounded = 3,
|
|
161
|
+
Thermal = 4,
|
|
162
|
+
}
|
|
10
163
|
|
|
11
164
|
export const enum GossDegradeLevel {
|
|
12
165
|
Full = 0,
|
|
@@ -405,6 +558,24 @@ export class Gosslens {
|
|
|
405
558
|
return Number(this.mod.ccall("goss_capabilities", "number", [], []));
|
|
406
559
|
}
|
|
407
560
|
|
|
561
|
+
/// The rails a lens declares that this build lacks, as GOSS_CAP_ bits; zero means
|
|
562
|
+
/// every one it asked for is here. A catalogue filters on this rather than
|
|
563
|
+
/// activating a lens to find out.
|
|
564
|
+
lensCapabilitiesMissing(manifestJson: string): number {
|
|
565
|
+
const bytes = new TextEncoder().encode(manifestJson);
|
|
566
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]) as number;
|
|
567
|
+
const out = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
568
|
+
try {
|
|
569
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
570
|
+
const status = this.mod.ccall("goss_lens_capabilities_missing", "number", ["number", "number", "number"], [ptr, bytes.length, out]);
|
|
571
|
+
if (status !== GOSS_OK) return 0;
|
|
572
|
+
return this.mod.HEAPU32[out >> 2];
|
|
573
|
+
} finally {
|
|
574
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [out, 8]);
|
|
575
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
408
579
|
/// Loads gosslens_web.js and checks its ABI major version. A
|
|
409
580
|
/// dynamic import, not static: bun's bundler would otherwise inline
|
|
410
581
|
/// this file, breaking Emscripten's own import.meta.url-relative
|
|
@@ -412,8 +583,12 @@ export class Gosslens {
|
|
|
412
583
|
static async load(canvas: HTMLCanvasElement, wasmJsUrl: string | URL): Promise<Gosslens> {
|
|
413
584
|
const imported = (await import(/* @vite-ignore */ String(wasmJsUrl))) as { default: EngineModuleFactory };
|
|
414
585
|
const mod = await imported.default({ canvas });
|
|
586
|
+
// The engine decides, rather than this file comparing against a literal major
|
|
587
|
+
// that would go stale the day the major moves.
|
|
415
588
|
const version = mod.ccall("goss_abi_version", "number", [], []) >>> 0;
|
|
416
|
-
if (
|
|
589
|
+
if (mod.ccall("goss_abi_check", "number", ["number"], [GOSS_ABI_MAJOR << 16]) !== GOSS_OK) {
|
|
590
|
+
throw new Error(`gosslens abi major mismatch: ${version >> 16}`);
|
|
591
|
+
}
|
|
417
592
|
return new Gosslens(mod, version);
|
|
418
593
|
}
|
|
419
594
|
|
|
@@ -1092,6 +1267,137 @@ export class GossEngine {
|
|
|
1092
1267
|
return sum;
|
|
1093
1268
|
}
|
|
1094
1269
|
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
/// What the engine is doing now, as against what it was asked for. Every
|
|
1273
|
+
/// field is measured. The u64 fields are read as their low word, which holds
|
|
1274
|
+
/// the whole count at any rate this engine reaches.
|
|
1275
|
+
/// What this build's media backend declares it encodes, as bit sets over the
|
|
1276
|
+
/// codec and container enums, so a page asks rather than assuming.
|
|
1277
|
+
mediaCapabilities(): GossMediaCapabilities | null {
|
|
1278
|
+
const bytes = 8 * 4;
|
|
1279
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
1280
|
+
try {
|
|
1281
|
+
if (this.mod.ccall("goss_engine_media_capabilities", "number", ["number", "number"], [this.handle, ptr]) !== GOSS_OK) return null;
|
|
1282
|
+
const w = (offset: number) => this.mod.HEAPU32[(ptr + offset) >> 2];
|
|
1283
|
+
return {
|
|
1284
|
+
videoCodecs: w(0),
|
|
1285
|
+
audioCodecs: w(4),
|
|
1286
|
+
containers: w(8),
|
|
1287
|
+
maxWidth: w(12),
|
|
1288
|
+
maxHeight: w(16),
|
|
1289
|
+
maxBitDepth: w(20),
|
|
1290
|
+
hdr: w(24) !== 0,
|
|
1291
|
+
zeroCopy: w(28) !== 0,
|
|
1292
|
+
};
|
|
1293
|
+
} finally {
|
|
1294
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/// Everything capturable. Empty where permission has not been granted, so
|
|
1299
|
+
/// prompt rather than treating it as an error.
|
|
1300
|
+
screens(): GossScreenSurface[] {
|
|
1301
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1302
|
+
const entryPtr = this.mod.ccall("goss_alloc", "number", ["number"], [40]) as number;
|
|
1303
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1304
|
+
const out: GossScreenSurface[] = [];
|
|
1305
|
+
try {
|
|
1306
|
+
if (this.mod.ccall("goss_engine_screen_count", "number", ["number", "number"], [this.handle, countPtr]) !== GOSS_OK) return out;
|
|
1307
|
+
const count = this.mod.HEAPU32[countPtr >> 2];
|
|
1308
|
+
for (let i = 0; i < count; i += 1) {
|
|
1309
|
+
if (this.mod.ccall("goss_engine_screen_at", "number", ["number", "number", "number"], [this.handle, i, entryPtr]) !== GOSS_OK) continue;
|
|
1310
|
+
const f = (off: number) => this.mod.HEAPF32[(entryPtr + off) >> 2];
|
|
1311
|
+
const u = (off: number) => this.mod.HEAPU32[(entryPtr + off) >> 2];
|
|
1312
|
+
this.mod.ccall("goss_engine_screen_title", "number", ["number", "number", "number", "number", "number"], [this.handle, i, 0, 0, lenPtr]);
|
|
1313
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
1314
|
+
let title = "";
|
|
1315
|
+
if (needed > 0) {
|
|
1316
|
+
const titlePtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
1317
|
+
try {
|
|
1318
|
+
if (this.mod.ccall("goss_engine_screen_title", "number", ["number", "number", "number", "number", "number"], [this.handle, i, titlePtr, needed, lenPtr]) === GOSS_OK) {
|
|
1319
|
+
title = new TextDecoder().decode(this.mod.HEAPU8.subarray(titlePtr, titlePtr + needed));
|
|
1320
|
+
}
|
|
1321
|
+
} finally {
|
|
1322
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [titlePtr, needed]);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
out.push({ id: u(0), kind: u(8), title, logicalWidth: f(12), logicalHeight: f(16), originX: f(20), originY: f(24), scale: f(28) });
|
|
1326
|
+
}
|
|
1327
|
+
return out;
|
|
1328
|
+
} finally {
|
|
1329
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
1330
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [entryPtr, 40]);
|
|
1331
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/// The operators a model needs that this build does not implement. An empty
|
|
1336
|
+
/// list means the model runs; anything in it names exactly what is missing.
|
|
1337
|
+
mlOpSupport(model: Uint8Array): string[] {
|
|
1338
|
+
const modelPtr = this.mod.ccall("goss_alloc", "number", ["number"], [model.length]) as number;
|
|
1339
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1340
|
+
this.mod.HEAPU8.set(model, modelPtr);
|
|
1341
|
+
let capacity = 256;
|
|
1342
|
+
try {
|
|
1343
|
+
while (capacity <= 1 << 16) {
|
|
1344
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [capacity]) as number;
|
|
1345
|
+
try {
|
|
1346
|
+
const status = this.mod.ccall(
|
|
1347
|
+
"goss_ml_op_support",
|
|
1348
|
+
"number",
|
|
1349
|
+
["number", "number", "number", "number", "number"],
|
|
1350
|
+
[modelPtr, model.length, outPtr, capacity, lenPtr],
|
|
1351
|
+
) as number;
|
|
1352
|
+
if (status !== GOSS_OK && status !== GOSS_AGAIN) return [];
|
|
1353
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
1354
|
+
if (needed <= capacity) {
|
|
1355
|
+
const text = new TextDecoder().decode(this.mod.HEAPU8.subarray(outPtr, outPtr + needed));
|
|
1356
|
+
return text.split("\n").filter((line) => line.length !== 0);
|
|
1357
|
+
}
|
|
1358
|
+
capacity = needed;
|
|
1359
|
+
} finally {
|
|
1360
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, capacity]);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return [];
|
|
1364
|
+
} finally {
|
|
1365
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [modelPtr, model.length]);
|
|
1366
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
engineReport(): GossEngineReport | null {
|
|
1371
|
+
const bytes = ENGINE_REPORT_BYTES;
|
|
1372
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
1373
|
+
try {
|
|
1374
|
+
if (this.mod.ccall("goss_engine_read_report", "number", ["number", "number"], [this.handle, ptr]) !== 0) return null;
|
|
1375
|
+
// The low word of each u64 is the whole count at any size this engine
|
|
1376
|
+
// reaches, and reading it avoids a BigInt in the hot read.
|
|
1377
|
+
const w = (offset: number) => this.mod.HEAPU32[(ptr + offset) >> 2];
|
|
1378
|
+
return {
|
|
1379
|
+
rendererBackend: w(0),
|
|
1380
|
+
zeroCopyImport: w(4) !== 0,
|
|
1381
|
+
texturePoolCapacity: w(8),
|
|
1382
|
+
texturePoolLive: w(12),
|
|
1383
|
+
texturePoolPeak: w(16),
|
|
1384
|
+
texturePoolExhausted: w(20),
|
|
1385
|
+
stagingPoolCapacity: w(24),
|
|
1386
|
+
stagingPoolLive: w(28),
|
|
1387
|
+
stagingPoolPeak: w(32),
|
|
1388
|
+
stagingPoolExhausted: w(36),
|
|
1389
|
+
texturePoolBins: w(40),
|
|
1390
|
+
texturePoolBinsRefused: w(44),
|
|
1391
|
+
stagingPoolBins: w(48),
|
|
1392
|
+
bgfxLiveBytes: w(56),
|
|
1393
|
+
bgfxAllocCallsLastFrame: w(64),
|
|
1394
|
+
bgfxBytesLastFrame: w(72),
|
|
1395
|
+
};
|
|
1396
|
+
} finally {
|
|
1397
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1095
1401
|
destroy(): void {
|
|
1096
1402
|
this.mod.ccall("goss_engine_destroy", null, ["number"], [this.handle]);
|
|
1097
1403
|
// The renderer is gone now, so bgfx no longer references the selector.
|
|
@@ -1147,6 +1453,84 @@ export interface GossCaptureUi {
|
|
|
1147
1453
|
/// its own scratch allocations (frame descriptor, pixel buffer,
|
|
1148
1454
|
/// landmarks) rather than one shared per-engine pool - matches every
|
|
1149
1455
|
/// other SDK's own per-session confinement.
|
|
1456
|
+
/// What the engine is doing now, as against what it was asked for.
|
|
1457
|
+
export interface GossEngineReport {
|
|
1458
|
+
rendererBackend: number;
|
|
1459
|
+
zeroCopyImport: boolean;
|
|
1460
|
+
texturePoolCapacity: number;
|
|
1461
|
+
texturePoolLive: number;
|
|
1462
|
+
texturePoolPeak: number;
|
|
1463
|
+
texturePoolExhausted: number;
|
|
1464
|
+
stagingPoolCapacity: number;
|
|
1465
|
+
stagingPoolLive: number;
|
|
1466
|
+
stagingPoolPeak: number;
|
|
1467
|
+
stagingPoolExhausted: number;
|
|
1468
|
+
texturePoolBins: number;
|
|
1469
|
+
texturePoolBinsRefused: number;
|
|
1470
|
+
stagingPoolBins: number;
|
|
1471
|
+
bgfxLiveBytes: number;
|
|
1472
|
+
bgfxAllocCallsLastFrame: number;
|
|
1473
|
+
bgfxBytesLastFrame: number;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
/// The ABI major these bindings were generated against: handing the engine its own
|
|
1477
|
+
/// version back would always match and test nothing. The major, not the whole version,
|
|
1478
|
+
/// because only the major decides compatibility. Held to the header by the abi gate.
|
|
1479
|
+
const GOSS_ABI_MAJOR = 0;
|
|
1480
|
+
|
|
1481
|
+
/// What a report struct measures, checked against the engine's own layout.
|
|
1482
|
+
const ENGINE_REPORT_BYTES = 80;
|
|
1483
|
+
const SESSION_REPORT_BYTES = 88;
|
|
1484
|
+
|
|
1485
|
+
/// This session's counters: work asked for against work done.
|
|
1486
|
+
export interface GossSessionReport {
|
|
1487
|
+
framesSubmitted: number;
|
|
1488
|
+
framesRendered: number;
|
|
1489
|
+
degradeLevel: GossDegradeLevel;
|
|
1490
|
+
degradeTransitions: number;
|
|
1491
|
+
faceAnalysis: number;
|
|
1492
|
+
handAnalysis: number;
|
|
1493
|
+
poseAnalysis: number;
|
|
1494
|
+
segmentationAnalysis: number;
|
|
1495
|
+
mlAnalysis: number;
|
|
1496
|
+
nodesDegraded: number;
|
|
1497
|
+
nodeReportsLost: number;
|
|
1498
|
+
scriptFaults: number;
|
|
1499
|
+
/// A model rail's frame buffer growing mid-run; steady state is zero.
|
|
1500
|
+
mlPlanGrowths: number;
|
|
1501
|
+
mlPlanBytes: number;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/// What a lens node is doing, as against what its manifest asked for.
|
|
1505
|
+
export enum GossNodeState {
|
|
1506
|
+
Ready = 0,
|
|
1507
|
+
Degraded = 1,
|
|
1508
|
+
Failed = 2,
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/// Why a node is not ready.
|
|
1512
|
+
export enum GossNodeReason {
|
|
1513
|
+
None = 0,
|
|
1514
|
+
OutOfMemory = 1,
|
|
1515
|
+
AssetMissing = 2,
|
|
1516
|
+
AssetMalformed = 3,
|
|
1517
|
+
AssetTooLarge = 4,
|
|
1518
|
+
ShaderMissing = 5,
|
|
1519
|
+
ShaderLinkFailed = 6,
|
|
1520
|
+
ModelRejected = 7,
|
|
1521
|
+
ModelUnsupported = 8,
|
|
1522
|
+
CapabilityUnavailable = 9,
|
|
1523
|
+
ConstraintFailed = 10,
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
/// One lens node that is not doing what its manifest asked.
|
|
1527
|
+
export interface GossNodeReport {
|
|
1528
|
+
id: string;
|
|
1529
|
+
nodeIndex: number;
|
|
1530
|
+
state: GossNodeState;
|
|
1531
|
+
reason: GossNodeReason;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1150
1534
|
export class GossSession {
|
|
1151
1535
|
private worldScratchPtr = 0;
|
|
1152
1536
|
private worldScratchLen = 0;
|
|
@@ -1178,6 +1562,7 @@ export class GossSession {
|
|
|
1178
1562
|
/// freed in destroy() - no per-call wasm heap churn.
|
|
1179
1563
|
private scratchPtr = 0;
|
|
1180
1564
|
private scratchCapacity = 0;
|
|
1565
|
+
private lensChangeInFlight = false;
|
|
1181
1566
|
|
|
1182
1567
|
private constructor(
|
|
1183
1568
|
private readonly mod: EngineModule,
|
|
@@ -1257,16 +1642,46 @@ export class GossSession {
|
|
|
1257
1642
|
/// A lens built entirely from beauty.* nodes (beauty-baseline, say)
|
|
1258
1643
|
/// activates and runs for real regardless, since those go through
|
|
1259
1644
|
/// applyWebBeautyChain's own embedded shaders, not a per-lens one.
|
|
1260
|
-
|
|
1645
|
+
/// Activates a lens, resolving false when it is live but a node the manifest did
|
|
1646
|
+
/// not mark optional could not do what it asked; nodeReports says which and why.
|
|
1647
|
+
/// On the WebGPU build this suspends and creates work that reaches Dawn, so not
|
|
1648
|
+
/// awaiting left the module suspended and the next render reentered it.
|
|
1649
|
+
async activateLens(manifestJson: string): Promise<boolean> {
|
|
1261
1650
|
const bytes = new TextEncoder().encode(manifestJson);
|
|
1262
1651
|
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
1263
1652
|
this.mod.HEAPU8.set(bytes, ptr);
|
|
1264
|
-
this.
|
|
1265
|
-
|
|
1653
|
+
this.lensChangeInFlight = true;
|
|
1654
|
+
try {
|
|
1655
|
+
const status = (await this.mod.ccall(
|
|
1656
|
+
"goss_session_activate_lens",
|
|
1657
|
+
"number",
|
|
1658
|
+
["number", "number", "number"],
|
|
1659
|
+
[this.handle, ptr, bytes.length],
|
|
1660
|
+
{ async: true },
|
|
1661
|
+
)) as number;
|
|
1662
|
+
return status === GOSS_OK;
|
|
1663
|
+
} finally {
|
|
1664
|
+
this.lensChangeInFlight = false;
|
|
1665
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/// Deactivation destroys the same resources, so it suspends for the same
|
|
1670
|
+
/// reason and is awaited for the same reason.
|
|
1671
|
+
async deactivateLens(): Promise<void> {
|
|
1672
|
+
this.lensChangeInFlight = true;
|
|
1673
|
+
try {
|
|
1674
|
+
await this.mod.ccall("goss_session_deactivate_lens", null, ["number"], [this.handle], { async: true });
|
|
1675
|
+
} finally {
|
|
1676
|
+
this.lensChangeInFlight = false;
|
|
1677
|
+
}
|
|
1266
1678
|
}
|
|
1267
1679
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1680
|
+
/// True while an activation or deactivation is unwound. A render during that
|
|
1681
|
+
/// window reenters a suspended module, which Asyncify cannot do, so a caller
|
|
1682
|
+
/// driving its own frame loop skips the frame rather than calling in.
|
|
1683
|
+
get isLensChangeInFlight(): boolean {
|
|
1684
|
+
return this.lensChangeInFlight;
|
|
1270
1685
|
}
|
|
1271
1686
|
|
|
1272
1687
|
/// Advances the active lens's triggers/param ramps by dtUs, evaluating
|
|
@@ -1332,6 +1747,231 @@ export class GossSession {
|
|
|
1332
1747
|
/// Casts a world-space ray against the submitted world mesh, returning the
|
|
1333
1748
|
/// nearest surface hit `{ point, distance }`, or null when no mesh is
|
|
1334
1749
|
/// submitted or the ray misses. A tap-to-place lens anchors content there.
|
|
1750
|
+
/// Decodes a PNG to packed RGBA8 through the decoder the engine already carries,
|
|
1751
|
+
/// for a caller holding an encoded image and none of its own.
|
|
1752
|
+
decodePng(bytes: Uint8Array): { rgba: Uint8Array; width: number; height: number } | null {
|
|
1753
|
+
const inPtr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]) as number;
|
|
1754
|
+
const wPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1755
|
+
const hPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1756
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1757
|
+
let outPtr = 0;
|
|
1758
|
+
let needed = 0;
|
|
1759
|
+
try {
|
|
1760
|
+
this.mod.HEAPU8.set(bytes, inPtr);
|
|
1761
|
+
const args = ["number", "number", "number", "number", "number", "number", "number"];
|
|
1762
|
+
this.mod.ccall("goss_engine_decode_png", "number", args, [inPtr, bytes.length, 0, 0, wPtr, hPtr, lenPtr]);
|
|
1763
|
+
needed = this.mod.HEAPU32[lenPtr >> 2]!;
|
|
1764
|
+
if (needed === 0) return null;
|
|
1765
|
+
outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
1766
|
+
if (this.mod.ccall("goss_engine_decode_png", "number", args, [inPtr, bytes.length, outPtr, needed, wPtr, hPtr, lenPtr]) !== GOSS_OK) return null;
|
|
1767
|
+
return {
|
|
1768
|
+
rgba: this.mod.HEAPU8.slice(outPtr, outPtr + needed),
|
|
1769
|
+
width: this.mod.HEAPU32[wPtr >> 2]!,
|
|
1770
|
+
height: this.mod.HEAPU32[hPtr >> 2]!,
|
|
1771
|
+
};
|
|
1772
|
+
} finally {
|
|
1773
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [inPtr, bytes.length]);
|
|
1774
|
+
if (outPtr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, needed]);
|
|
1775
|
+
for (const [ptr, size] of [[wPtr, 4], [hPtr, 4], [lenPtr, 8]] as const) {
|
|
1776
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, size]);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/// A walkable route over the submitted world mesh, so an agent walks content
|
|
1782
|
+
/// across real scanned ground. Null when no mesh is submitted or no route exists.
|
|
1783
|
+
pathAcrossWorld(start: [number, number, number], goal: [number, number, number]): [number, number, number][] | null {
|
|
1784
|
+
const capacity = 256;
|
|
1785
|
+
const aPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
1786
|
+
const bPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
1787
|
+
const outBytes = capacity * 12;
|
|
1788
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [outBytes]) as number;
|
|
1789
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1790
|
+
try {
|
|
1791
|
+
this.mod.HEAPF32.set(start, aPtr / 4);
|
|
1792
|
+
this.mod.HEAPF32.set(goal, bPtr / 4);
|
|
1793
|
+
const args = ["number", "number", "number", "number", "number", "number"];
|
|
1794
|
+
if (this.mod.ccall("goss_session_path_across_world", "number", args, [this.handle, aPtr, bPtr, outPtr, capacity, countPtr]) !== GOSS_OK) return null;
|
|
1795
|
+
const found = Math.min(this.mod.HEAPU32[countPtr >> 2]!, capacity);
|
|
1796
|
+
const out: [number, number, number][] = [];
|
|
1797
|
+
for (let i = 0; i < found; i += 1) {
|
|
1798
|
+
const w = (outPtr >> 2) + i * 3;
|
|
1799
|
+
out.push([this.mod.HEAPF32[w]!, this.mod.HEAPF32[w + 1]!, this.mod.HEAPF32[w + 2]!]);
|
|
1800
|
+
}
|
|
1801
|
+
return out;
|
|
1802
|
+
} finally {
|
|
1803
|
+
for (const [ptr, bytes] of [[aPtr, 12], [bPtr, 12], [outPtr, outBytes], [countPtr, 8]] as const) {
|
|
1804
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
/// What a submitted plane is, as a named kind rather than the platform's own
|
|
1810
|
+
/// number, and whether a thing can rest on it.
|
|
1811
|
+
planeKind(planeId: number): { kind: number; bearing: boolean } | null {
|
|
1812
|
+
const kindPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1813
|
+
const bearingPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1814
|
+
try {
|
|
1815
|
+
const args = ["number", "number", "number", "number"];
|
|
1816
|
+
if (this.mod.ccall("goss_session_plane_kind", "number", args, [this.handle, planeId, kindPtr, bearingPtr]) !== GOSS_OK) return null;
|
|
1817
|
+
return { kind: this.mod.HEAPU32[kindPtr >> 2]!, bearing: this.mod.HEAPU32[bearingPtr >> 2] === 1 };
|
|
1818
|
+
} finally {
|
|
1819
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [kindPtr, 4]);
|
|
1820
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [bearingPtr, 4]);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/// The plane this session would call the floor: the lowest bearing surface it
|
|
1825
|
+
/// has been shown, or null when it has been shown none.
|
|
1826
|
+
floorPlaneId(): number | null {
|
|
1827
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1828
|
+
try {
|
|
1829
|
+
if (this.mod.ccall("goss_session_floor_plane", "number", ["number", "number"], [this.handle, ptr]) !== GOSS_OK) return null;
|
|
1830
|
+
return this.mod.HEAPU32[ptr >> 2]!;
|
|
1831
|
+
} finally {
|
|
1832
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, 8]);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/// Where a footprint fits, best surface first: the bearing plane with the most
|
|
1837
|
+
/// room left afterwards. An empty array is an answer.
|
|
1838
|
+
placeOn(
|
|
1839
|
+
item: { width: number; depth: number; height?: number },
|
|
1840
|
+
occupants: { planeId: number; x: number; z: number; width: number; depth: number }[] = [],
|
|
1841
|
+
): { planeId: number; position: [number, number, number]; freeFraction: number }[] {
|
|
1842
|
+
const itemPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
1843
|
+
const occupantBytes = occupants.length * 24;
|
|
1844
|
+
const occupantPtr = occupants.length === 0 ? 0 : (this.mod.ccall("goss_alloc", "number", ["number"], [occupantBytes]) as number);
|
|
1845
|
+
const capacity = 32;
|
|
1846
|
+
const outBytes = capacity * 24;
|
|
1847
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [outBytes]) as number;
|
|
1848
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1849
|
+
try {
|
|
1850
|
+
this.mod.HEAPF32.set([item.width, item.depth, item.height ?? 0], itemPtr / 4);
|
|
1851
|
+
// Each occupant is a u64 id then four floats: the id is written as two words
|
|
1852
|
+
// because the heap view is 32-bit and an id past 2^32 is not a plane id any
|
|
1853
|
+
// platform hands out.
|
|
1854
|
+
occupants.forEach((o, i) => {
|
|
1855
|
+
const base = occupantPtr + i * 24;
|
|
1856
|
+
this.mod.HEAPU32[base >> 2] = o.planeId;
|
|
1857
|
+
this.mod.HEAPU32[(base >> 2) + 1] = 0;
|
|
1858
|
+
this.mod.HEAPF32.set([o.x, o.z, o.width, o.depth], (base + 8) / 4);
|
|
1859
|
+
});
|
|
1860
|
+
const args = ["number", "number", "number", "number", "number", "number", "number"];
|
|
1861
|
+
const status = this.mod.ccall("goss_session_place_on", "number", args, [this.handle, itemPtr, occupantPtr, occupants.length, outPtr, capacity, countPtr]) as number;
|
|
1862
|
+
if (status !== GOSS_OK && status !== GOSS_AGAIN) return [];
|
|
1863
|
+
const found = Math.min(this.mod.HEAPU32[countPtr >> 2]!, capacity);
|
|
1864
|
+
const out: { planeId: number; position: [number, number, number]; freeFraction: number }[] = [];
|
|
1865
|
+
for (let i = 0; i < found; i += 1) {
|
|
1866
|
+
const base = outPtr + i * 24;
|
|
1867
|
+
const floats = (base + 8) / 4;
|
|
1868
|
+
out.push({
|
|
1869
|
+
planeId: this.mod.HEAPU32[base >> 2]!,
|
|
1870
|
+
position: [this.mod.HEAPF32[floats]!, this.mod.HEAPF32[floats + 1]!, this.mod.HEAPF32[floats + 2]!],
|
|
1871
|
+
freeFraction: this.mod.HEAPF32[floats + 3]!,
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
return out;
|
|
1875
|
+
} finally {
|
|
1876
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [itemPtr, 12]);
|
|
1877
|
+
if (occupantPtr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [occupantPtr, occupantBytes]);
|
|
1878
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, outBytes]);
|
|
1879
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 8]);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
/// Point to point in metres with its uncertainty. `known` is false when either
|
|
1884
|
+
/// end vouched for no accuracy, so a sigma of zero is never read as certainty.
|
|
1885
|
+
measureBetween(
|
|
1886
|
+
from: [number, number, number],
|
|
1887
|
+
to: [number, number, number],
|
|
1888
|
+
fromAccuracyM = 0,
|
|
1889
|
+
toAccuracyM = 0,
|
|
1890
|
+
): { metres: number; sigma: number; known: boolean } | null {
|
|
1891
|
+
const aPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
1892
|
+
const bPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
1893
|
+
const valuePtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1894
|
+
const sigmaPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1895
|
+
const knownPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1896
|
+
try {
|
|
1897
|
+
this.mod.HEAPF32.set(from, aPtr / 4);
|
|
1898
|
+
this.mod.HEAPF32.set(to, bPtr / 4);
|
|
1899
|
+
const args = ["number", "number", "number", "number", "number", "number", "number", "number"];
|
|
1900
|
+
const status = this.mod.ccall("goss_session_measure_between", "number", args, [this.handle, aPtr, fromAccuracyM, bPtr, toAccuracyM, valuePtr, sigmaPtr, knownPtr]) as number;
|
|
1901
|
+
if (status !== GOSS_OK) return null;
|
|
1902
|
+
return {
|
|
1903
|
+
metres: this.mod.HEAPF32[valuePtr >> 2]!,
|
|
1904
|
+
sigma: this.mod.HEAPF32[sigmaPtr >> 2]!,
|
|
1905
|
+
known: this.mod.HEAPU32[knownPtr >> 2] === 1,
|
|
1906
|
+
};
|
|
1907
|
+
} finally {
|
|
1908
|
+
for (const [ptr, bytes] of [[aPtr, 12], [bPtr, 12], [valuePtr, 4], [sigmaPtr, 4], [knownPtr, 4]] as const) {
|
|
1909
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
/// What this device can offer another: one landmark per world anchor it holds,
|
|
1915
|
+
/// in its own frame. A pose never crosses.
|
|
1916
|
+
sharedLandmarks(): { id: number; position: [number, number, number]; confidence: number }[] {
|
|
1917
|
+
const capacity = 32;
|
|
1918
|
+
const bytes = capacity * 24;
|
|
1919
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
1920
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
1921
|
+
try {
|
|
1922
|
+
const args = ["number", "number", "number", "number"];
|
|
1923
|
+
const status = this.mod.ccall("goss_session_shared_landmarks", "number", args, [this.handle, outPtr, capacity, countPtr]) as number;
|
|
1924
|
+
if (status !== GOSS_OK && status !== GOSS_AGAIN) return [];
|
|
1925
|
+
const found = Math.min(this.mod.HEAPU32[countPtr >> 2]!, capacity);
|
|
1926
|
+
const out: { id: number; position: [number, number, number]; confidence: number }[] = [];
|
|
1927
|
+
for (let i = 0; i < found; i += 1) {
|
|
1928
|
+
const base = outPtr + i * 24;
|
|
1929
|
+
const floats = (base + 8) / 4;
|
|
1930
|
+
out.push({
|
|
1931
|
+
id: this.mod.HEAPU32[base >> 2]!,
|
|
1932
|
+
position: [this.mod.HEAPF32[floats]!, this.mod.HEAPF32[floats + 1]!, this.mod.HEAPF32[floats + 2]!],
|
|
1933
|
+
confidence: this.mod.HEAPF32[floats + 3]!,
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
return out;
|
|
1937
|
+
} finally {
|
|
1938
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, bytes]);
|
|
1939
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 8]);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
/// The transform from the sender's origin into this one, column-major, with the
|
|
1944
|
+
/// fit it achieved. Null when fewer than three landmarks matched, which cannot
|
|
1945
|
+
/// fix a rigid transform.
|
|
1946
|
+
alignShared(theirs: { id: number; position: [number, number, number]; confidence?: number }[]): { transform: Float32Array; rmsError: number; matched: number } | null {
|
|
1947
|
+
const bytes = Math.max(theirs.length, 1) * 24;
|
|
1948
|
+
const listPtr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
1949
|
+
const transformPtr = this.mod.ccall("goss_alloc", "number", ["number"], [64]) as number;
|
|
1950
|
+
const rmsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1951
|
+
const matchedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
1952
|
+
try {
|
|
1953
|
+
theirs.forEach((l, i) => {
|
|
1954
|
+
const base = listPtr + i * 24;
|
|
1955
|
+
this.mod.HEAPU32[base >> 2] = l.id;
|
|
1956
|
+
this.mod.HEAPU32[(base >> 2) + 1] = 0;
|
|
1957
|
+
this.mod.HEAPF32.set([l.position[0], l.position[1], l.position[2], l.confidence ?? 1], (base + 8) / 4);
|
|
1958
|
+
});
|
|
1959
|
+
const args = ["number", "number", "number", "number", "number", "number"];
|
|
1960
|
+
const status = this.mod.ccall("goss_session_align_shared", "number", args, [this.handle, listPtr, theirs.length, transformPtr, rmsPtr, matchedPtr]) as number;
|
|
1961
|
+
if (status !== GOSS_OK) return null;
|
|
1962
|
+
return {
|
|
1963
|
+
transform: this.mod.HEAPF32.slice(transformPtr >> 2, (transformPtr >> 2) + 16),
|
|
1964
|
+
rmsError: this.mod.HEAPF32[rmsPtr >> 2]!,
|
|
1965
|
+
matched: this.mod.HEAPU32[matchedPtr >> 2]!,
|
|
1966
|
+
};
|
|
1967
|
+
} finally {
|
|
1968
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [listPtr, bytes]);
|
|
1969
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [transformPtr, 64]);
|
|
1970
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [rmsPtr, 4]);
|
|
1971
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [matchedPtr, 4]);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1335
1975
|
raycastWorldMesh(origin: [number, number, number], direction: [number, number, number]): { point: [number, number, number]; distance: number } | null {
|
|
1336
1976
|
const oPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
1337
1977
|
this.mod.HEAPF32.set(origin, oPtr / 4);
|
|
@@ -2878,6 +3518,615 @@ export class GossSession {
|
|
|
2878
3518
|
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
2879
3519
|
}
|
|
2880
3520
|
|
|
3521
|
+
/// Every section this build writes, asked of the engine rather than assumed: a
|
|
3522
|
+
/// hand-written mask excluded the embedding section the day it was added.
|
|
3523
|
+
selectAll(): number {
|
|
3524
|
+
return this.mod.ccall("goss_perception_select_all", "number", [], []) as number;
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
/// How many verbs this engine build knows, asked of the engine rather than taken
|
|
3528
|
+
/// from the enum, so a newer engine behind this wrapper is not misread.
|
|
3529
|
+
verbCount(): number {
|
|
3530
|
+
return this.mod.ccall("goss_scope_verb_count", "number", [], []) as number;
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
/// The engine's own name for a verb, so a refusal reads as a sentence and a
|
|
3534
|
+
/// permission prompt reads as words rather than a bitmask.
|
|
3535
|
+
verbName(verb: GossVerb | number): string | null {
|
|
3536
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3537
|
+
let outPtr = 0;
|
|
3538
|
+
let needed = 0;
|
|
3539
|
+
try {
|
|
3540
|
+
const args = ["number", "number", "number", "number"];
|
|
3541
|
+
this.mod.ccall("goss_scope_verb_name", "number", args, [verb, 0, 0, lenPtr]);
|
|
3542
|
+
needed = this.mod.HEAPU32[lenPtr >> 2]!;
|
|
3543
|
+
if (needed === 0) return null;
|
|
3544
|
+
outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
3545
|
+
if (this.mod.ccall("goss_scope_verb_name", "number", args, [verb, outPtr, needed, lenPtr]) !== GOSS_OK) return null;
|
|
3546
|
+
return new TextDecoder().decode(this.mod.HEAPU8.subarray(outPtr, outPtr + needed));
|
|
3547
|
+
} finally {
|
|
3548
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3549
|
+
if (outPtr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, needed]);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
/// One versioned record of what the engine currently sees. Every section
|
|
3554
|
+
/// carries its own tag, version and byte length, so a consumer built against an
|
|
3555
|
+
/// older schema steps over what it does not know. Sized in one retry.
|
|
3556
|
+
perceptionSnapshot(select = this.selectAll()): Uint8Array | null {
|
|
3557
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3558
|
+
try {
|
|
3559
|
+
this.mod.ccall("goss_session_perception_snapshot", "number", ["number", "number", "number", "number", "number"], [this.handle, select, 0, 0, lenPtr]);
|
|
3560
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3561
|
+
if (needed === 0) return null;
|
|
3562
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
3563
|
+
try {
|
|
3564
|
+
const status = this.mod.ccall("goss_session_perception_snapshot", "number", ["number", "number", "number", "number", "number"], [this.handle, select, ptr, needed, lenPtr]);
|
|
3565
|
+
if (status !== GOSS_OK) return null;
|
|
3566
|
+
const written = this.mod.HEAPU32[lenPtr >> 2];
|
|
3567
|
+
return this.mod.HEAPU8.slice(ptr, ptr + written);
|
|
3568
|
+
} finally {
|
|
3569
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
3570
|
+
}
|
|
3571
|
+
} finally {
|
|
3572
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 4]);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
/// The same record as compact JSON, for a gateway that speaks it.
|
|
3577
|
+
perceptionJson(select = this.selectAll()): string | null {
|
|
3578
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3579
|
+
try {
|
|
3580
|
+
this.mod.ccall("goss_session_perception_json", "number", ["number", "number", "number", "number", "number"], [this.handle, select, 0, 0, lenPtr]);
|
|
3581
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3582
|
+
if (needed === 0) return null;
|
|
3583
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
3584
|
+
try {
|
|
3585
|
+
const status = this.mod.ccall("goss_session_perception_json", "number", ["number", "number", "number", "number", "number"], [this.handle, select, ptr, needed, lenPtr]);
|
|
3586
|
+
if (status !== GOSS_OK) return null;
|
|
3587
|
+
const written = this.mod.HEAPU32[lenPtr >> 2];
|
|
3588
|
+
return new TextDecoder().decode(this.mod.HEAPU8.slice(ptr, ptr + written));
|
|
3589
|
+
} finally {
|
|
3590
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
3591
|
+
}
|
|
3592
|
+
} finally {
|
|
3593
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 4]);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
/// Opens a screen as a source. A scale of zero takes the surface's own.
|
|
3598
|
+
openScreen(surfaceId: number, scale = 0): number | null {
|
|
3599
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3600
|
+
try {
|
|
3601
|
+
if (this.mod.ccall("goss_session_open_screen", "number", ["number", "number", "number", "number"], [this.handle, surfaceId, scale, ptr]) !== GOSS_OK) return null;
|
|
3602
|
+
return this.mod.HEAPU32[ptr >> 2];
|
|
3603
|
+
} finally {
|
|
3604
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, 4]);
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
closeScreen(screen: number): boolean {
|
|
3609
|
+
return this.mod.ccall("goss_session_close_screen", "number", ["number", "number"], [this.handle, screen]) === GOSS_OK;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
/// Submits the newest frame. False when the screen has not changed, so a still
|
|
3613
|
+
/// desktop costs nothing.
|
|
3614
|
+
stepScreen(screen: number, source = ""): boolean {
|
|
3615
|
+
const bytes = new TextEncoder().encode(source);
|
|
3616
|
+
const ptr = bytes.length === 0 ? 0 : (this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]) as number);
|
|
3617
|
+
try {
|
|
3618
|
+
if (ptr !== 0) this.mod.HEAPU8.set(bytes, ptr);
|
|
3619
|
+
return this.mod.ccall("goss_session_step_screen", "number", ["number", "number", "number", "number"], [this.handle, screen, ptr, bytes.length]) === GOSS_OK;
|
|
3620
|
+
} finally {
|
|
3621
|
+
if (ptr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
/// Where a normalized point lands: logical points, backing pixels, desktop.
|
|
3626
|
+
screenPoint(screen: number, x: number, y: number): { logical: [number, number]; pixel: [number, number]; desktop: [number, number] } | null {
|
|
3627
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [24]) as number;
|
|
3628
|
+
try {
|
|
3629
|
+
if (this.mod.ccall("goss_session_screen_point", "number", ["number", "number", "number", "number", "number", "number", "number"], [this.handle, screen, x, y, ptr, ptr + 8, ptr + 16]) !== GOSS_OK) return null;
|
|
3630
|
+
const f = (off: number) => this.mod.HEAPF32[(ptr + off) >> 2];
|
|
3631
|
+
return { logical: [f(0), f(4)], pixel: [f(8), f(12)], desktop: [f(16), f(20)] };
|
|
3632
|
+
} finally {
|
|
3633
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, 24]);
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
|
|
3637
|
+
/// Narrows what this session answers. A session opens fully permissive; a read
|
|
3638
|
+
/// out of scope is dropped from the record rather than failing, and a verb out
|
|
3639
|
+
/// of scope is refused.
|
|
3640
|
+
setScope(sections: number, verbs: number): boolean {
|
|
3641
|
+
return this.mod.ccall("goss_session_set_scope", "number", ["number", "number", "number"], [this.handle, sections, verbs]) === GOSS_OK;
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
scope(): { sections: number; verbs: number } {
|
|
3645
|
+
const sectionsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3646
|
+
const verbsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3647
|
+
try {
|
|
3648
|
+
if (this.mod.ccall("goss_session_scope", "number", ["number", "number", "number"], [this.handle, sectionsPtr, verbsPtr]) !== GOSS_OK) return { sections: 0, verbs: 0 };
|
|
3649
|
+
return { sections: this.mod.HEAPU32[sectionsPtr >> 2], verbs: this.mod.HEAPU32[verbsPtr >> 2] };
|
|
3650
|
+
} finally {
|
|
3651
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [sectionsPtr, 4]);
|
|
3652
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [verbsPtr, 4]);
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
/// Opens the memory plane. Nothing is remembered until this is called, and
|
|
3657
|
+
/// the bound is yours, so the memory you were promised is the memory you get.
|
|
3658
|
+
memoryOpen(dim: number, maxEntries = 4096): boolean {
|
|
3659
|
+
return this.mod.ccall("goss_session_memory_open", "number", ["number", "number", "number"], [this.handle, dim, maxEntries]) === GOSS_OK;
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3662
|
+
memoryClose(): boolean {
|
|
3663
|
+
return this.mod.ccall("goss_session_memory_close", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3666
|
+
/// Remembers one embedding; the same id replaces rather than duplicating.
|
|
3667
|
+
remember(id: number, embedding: Float32Array): boolean {
|
|
3668
|
+
const bytes = embedding.length * 4;
|
|
3669
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
3670
|
+
try {
|
|
3671
|
+
this.mod.HEAPF32.set(embedding, ptr >> 2);
|
|
3672
|
+
return this.mod.ccall("goss_session_memory_remember", "number", ["number", "number", "number", "number"], [this.handle, id, ptr, embedding.length]) === GOSS_OK;
|
|
3673
|
+
} finally {
|
|
3674
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
forget(id: number): boolean {
|
|
3679
|
+
return this.mod.ccall("goss_session_memory_forget", "number", ["number", "number"], [this.handle, id]) === GOSS_OK;
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
/// The nearest remembered embeddings, fewer than k on a smaller memory rather
|
|
3683
|
+
/// than padded with nothing.
|
|
3684
|
+
memorySearch(query: Float32Array, k = 8): { id: number; score: number }[] {
|
|
3685
|
+
const queryBytes = query.length * 4;
|
|
3686
|
+
const queryPtr = this.mod.ccall("goss_alloc", "number", ["number"], [queryBytes]) as number;
|
|
3687
|
+
const idsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [k * 8]) as number;
|
|
3688
|
+
const scoresPtr = this.mod.ccall("goss_alloc", "number", ["number"], [k * 4]) as number;
|
|
3689
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3690
|
+
try {
|
|
3691
|
+
this.mod.HEAPF32.set(query, queryPtr >> 2);
|
|
3692
|
+
if (this.mod.ccall("goss_session_memory_search", "number", ["number", "number", "number", "number", "number", "number", "number"], [this.handle, queryPtr, query.length, k, idsPtr, scoresPtr, countPtr]) !== GOSS_OK) return [];
|
|
3693
|
+
const count = this.mod.HEAPU32[countPtr >> 2];
|
|
3694
|
+
const out: { id: number; score: number }[] = [];
|
|
3695
|
+
for (let i = 0; i < count; i += 1) {
|
|
3696
|
+
// The low word of the id is the whole value at any count this engine
|
|
3697
|
+
// reaches, and reading it avoids a BigInt in the hot read.
|
|
3698
|
+
out.push({ id: this.mod.HEAPU32[(idsPtr + i * 8) >> 2], score: this.mod.HEAPF32[(scoresPtr + i * 4) >> 2] });
|
|
3699
|
+
}
|
|
3700
|
+
return out;
|
|
3701
|
+
} finally {
|
|
3702
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [queryPtr, queryBytes]);
|
|
3703
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [idsPtr, k * 8]);
|
|
3704
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [scoresPtr, k * 4]);
|
|
3705
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
memoryStats(): { count: number; bytes: number } {
|
|
3710
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3711
|
+
const bytesPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3712
|
+
try {
|
|
3713
|
+
if (this.mod.ccall("goss_session_memory_stats", "number", ["number", "number", "number"], [this.handle, countPtr, bytesPtr]) !== GOSS_OK) return { count: 0, bytes: 0 };
|
|
3714
|
+
return { count: this.mod.HEAPU32[countPtr >> 2], bytes: this.mod.HEAPU32[bytesPtr >> 2] };
|
|
3715
|
+
} finally {
|
|
3716
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3717
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [bytesPtr, 8]);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
/// The whole memory as bytes, so a cold start is instant.
|
|
3722
|
+
memorySave(): Uint8Array {
|
|
3723
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3724
|
+
try {
|
|
3725
|
+
this.mod.ccall("goss_session_memory_save", "number", ["number", "number", "number", "number"], [this.handle, 0, 0, lenPtr]);
|
|
3726
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3727
|
+
if (needed === 0) return new Uint8Array(0);
|
|
3728
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
3729
|
+
try {
|
|
3730
|
+
if (this.mod.ccall("goss_session_memory_save", "number", ["number", "number", "number", "number"], [this.handle, ptr, needed, lenPtr]) !== GOSS_OK) return new Uint8Array(0);
|
|
3731
|
+
return new Uint8Array(this.mod.HEAPU8.subarray(ptr, ptr + needed));
|
|
3732
|
+
} finally {
|
|
3733
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
3734
|
+
}
|
|
3735
|
+
} finally {
|
|
3736
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
/// The memory sealed under a host key. The nonce is yours: reusing one under
|
|
3741
|
+
/// the same key breaks the cipher.
|
|
3742
|
+
memorySaveSealed(key: Uint8Array, nonce: Uint8Array): Uint8Array {
|
|
3743
|
+
const keyPtr = this.mod.ccall("goss_alloc", "number", ["number"], [key.length]) as number;
|
|
3744
|
+
const noncePtr = this.mod.ccall("goss_alloc", "number", ["number"], [nonce.length]) as number;
|
|
3745
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3746
|
+
try {
|
|
3747
|
+
this.mod.HEAPU8.set(key, keyPtr);
|
|
3748
|
+
this.mod.HEAPU8.set(nonce, noncePtr);
|
|
3749
|
+
this.mod.ccall("goss_session_memory_save_sealed", "number", ["number", "number", "number", "number", "number", "number"], [this.handle, keyPtr, noncePtr, 0, 0, lenPtr]);
|
|
3750
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3751
|
+
if (needed === 0) return new Uint8Array(0);
|
|
3752
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
3753
|
+
try {
|
|
3754
|
+
if (this.mod.ccall("goss_session_memory_save_sealed", "number", ["number", "number", "number", "number", "number", "number"], [this.handle, keyPtr, noncePtr, ptr, needed, lenPtr]) !== GOSS_OK) return new Uint8Array(0);
|
|
3755
|
+
return new Uint8Array(this.mod.HEAPU8.subarray(ptr, ptr + needed));
|
|
3756
|
+
} finally {
|
|
3757
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
3758
|
+
}
|
|
3759
|
+
} finally {
|
|
3760
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [keyPtr, key.length]);
|
|
3761
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [noncePtr, nonce.length]);
|
|
3762
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
|
|
3766
|
+
memoryLoadSealed(key: Uint8Array, bytes: Uint8Array): boolean {
|
|
3767
|
+
const keyPtr = this.mod.ccall("goss_alloc", "number", ["number"], [key.length]) as number;
|
|
3768
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]) as number;
|
|
3769
|
+
try {
|
|
3770
|
+
this.mod.HEAPU8.set(key, keyPtr);
|
|
3771
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
3772
|
+
return this.mod.ccall("goss_session_memory_load_sealed", "number", ["number", "number", "number", "number"], [this.handle, keyPtr, ptr, bytes.length]) === GOSS_OK;
|
|
3773
|
+
} finally {
|
|
3774
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [keyPtr, key.length]);
|
|
3775
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
memoryLoad(bytes: Uint8Array): boolean {
|
|
3780
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]) as number;
|
|
3781
|
+
try {
|
|
3782
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
3783
|
+
return this.mod.ccall("goss_session_memory_load", "number", ["number", "number", "number"], [this.handle, ptr, bytes.length]) === GOSS_OK;
|
|
3784
|
+
} finally {
|
|
3785
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
/// Turns on the text rail. A detector alone finds where the text is, which
|
|
3790
|
+
/// is what a redaction or a rectified crop needs; pass a recogniser and its
|
|
3791
|
+
/// dictionary to get strings back.
|
|
3792
|
+
enableText(detector: Uint8Array, recognizer?: Uint8Array, dictionary?: Uint8Array, detectSide = 320): boolean {
|
|
3793
|
+
const rec = recognizer ?? new Uint8Array(0);
|
|
3794
|
+
const dict = dictionary ?? new Uint8Array(0);
|
|
3795
|
+
const detPtr = this.mod.ccall("goss_alloc", "number", ["number"], [detector.length]) as number;
|
|
3796
|
+
const recPtr = rec.length === 0 ? 0 : (this.mod.ccall("goss_alloc", "number", ["number"], [rec.length]) as number);
|
|
3797
|
+
const dictPtr = dict.length === 0 ? 0 : (this.mod.ccall("goss_alloc", "number", ["number"], [dict.length]) as number);
|
|
3798
|
+
try {
|
|
3799
|
+
this.mod.HEAPU8.set(detector, detPtr);
|
|
3800
|
+
if (recPtr !== 0) this.mod.HEAPU8.set(rec, recPtr);
|
|
3801
|
+
if (dictPtr !== 0) this.mod.HEAPU8.set(dict, dictPtr);
|
|
3802
|
+
return this.mod.ccall(
|
|
3803
|
+
"goss_session_enable_text",
|
|
3804
|
+
"number",
|
|
3805
|
+
["number", "number", "number", "number", "number", "number", "number", "number"],
|
|
3806
|
+
[this.handle, detPtr, detector.length, recPtr, rec.length, dictPtr, dict.length, detectSide],
|
|
3807
|
+
) === GOSS_OK;
|
|
3808
|
+
} finally {
|
|
3809
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [detPtr, detector.length]);
|
|
3810
|
+
if (recPtr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [recPtr, rec.length]);
|
|
3811
|
+
if (dictPtr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [dictPtr, dict.length]);
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
disableText(): boolean {
|
|
3816
|
+
return this.mod.ccall("goss_session_disable_text", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
/// How many readings the frame holds and how many the bound turned away.
|
|
3820
|
+
textCount(): { live: number; refused: number } {
|
|
3821
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3822
|
+
const refusedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3823
|
+
try {
|
|
3824
|
+
if (this.mod.ccall("goss_session_text_count", "number", ["number", "number", "number"], [this.handle, countPtr, refusedPtr]) !== GOSS_OK) {
|
|
3825
|
+
return { live: 0, refused: 0 };
|
|
3826
|
+
}
|
|
3827
|
+
return { live: this.mod.HEAPU32[countPtr >> 2], refused: this.mod.HEAPU32[refusedPtr >> 2] };
|
|
3828
|
+
} finally {
|
|
3829
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3830
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [refusedPtr, 8]);
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
/// Everything the frame says, each reading with its quadrilateral and string.
|
|
3835
|
+
readings(): GossReading[] {
|
|
3836
|
+
const { live } = this.textCount();
|
|
3837
|
+
const entryBytes = 64;
|
|
3838
|
+
const entryPtr = this.mod.ccall("goss_alloc", "number", ["number"], [entryBytes]) as number;
|
|
3839
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3840
|
+
const out: GossReading[] = [];
|
|
3841
|
+
try {
|
|
3842
|
+
for (let i = 0; i < live; i += 1) {
|
|
3843
|
+
if (this.mod.ccall("goss_session_text_at", "number", ["number", "number", "number"], [this.handle, i, entryPtr]) !== GOSS_OK) continue;
|
|
3844
|
+
const f32 = (off: number) => this.mod.HEAPF32[(entryPtr + off) >> 2];
|
|
3845
|
+
const u32 = (off: number) => this.mod.HEAPU32[(entryPtr + off) >> 2];
|
|
3846
|
+
this.mod.ccall("goss_session_text_string", "number", ["number", "number", "number", "number", "number"], [this.handle, i, 0, 0, lenPtr]);
|
|
3847
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3848
|
+
let text = "";
|
|
3849
|
+
if (needed > 0) {
|
|
3850
|
+
const textPtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]) as number;
|
|
3851
|
+
try {
|
|
3852
|
+
if (this.mod.ccall("goss_session_text_string", "number", ["number", "number", "number", "number", "number"], [this.handle, i, textPtr, needed, lenPtr]) === GOSS_OK) {
|
|
3853
|
+
text = new TextDecoder().decode(this.mod.HEAPU8.subarray(textPtr, textPtr + needed));
|
|
3854
|
+
}
|
|
3855
|
+
} finally {
|
|
3856
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [textPtr, needed]);
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
out.push({
|
|
3860
|
+
text,
|
|
3861
|
+
quad: [f32(0), f32(4), f32(8), f32(12), f32(16), f32(20), f32(24), f32(28)],
|
|
3862
|
+
confidence: f32(32),
|
|
3863
|
+
origin: u32(36),
|
|
3864
|
+
script: u32(40),
|
|
3865
|
+
direction: u32(44),
|
|
3866
|
+
trackId: u32(48),
|
|
3867
|
+
line: u32(52),
|
|
3868
|
+
paragraph: u32(56),
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
return out;
|
|
3872
|
+
} finally {
|
|
3873
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [entryPtr, entryBytes]);
|
|
3874
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
/// Adds or updates one annotation. The same id replaces rather than
|
|
3879
|
+
/// duplicating, so moving one box every frame leaks no entry per frame.
|
|
3880
|
+
annotate(a: GossAnnotation, text = ""): boolean {
|
|
3881
|
+
const bytes = 64;
|
|
3882
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
3883
|
+
const encoded = new TextEncoder().encode(text);
|
|
3884
|
+
const textPtr = encoded.length === 0 ? 0 : (this.mod.ccall("goss_alloc", "number", ["number"], [encoded.length]) as number);
|
|
3885
|
+
try {
|
|
3886
|
+
const u32 = (off: number, v: number) => { this.mod.HEAPU32[(ptr + off) >> 2] = v; };
|
|
3887
|
+
const f32 = (off: number, v: number) => { this.mod.HEAPF32[(ptr + off) >> 2] = v; };
|
|
3888
|
+
u32(0, a.id);
|
|
3889
|
+
u32(4, a.kind);
|
|
3890
|
+
u32(8, a.space ?? 0);
|
|
3891
|
+
for (let i = 0; i < 4; i += 1) f32(12 + i * 4, a.rect?.[i] ?? 0);
|
|
3892
|
+
u32(28, a.trackId ?? 0);
|
|
3893
|
+
this.mod.HEAPU8[ptr + 32] = a.colour?.[0] ?? 255;
|
|
3894
|
+
this.mod.HEAPU8[ptr + 33] = a.colour?.[1] ?? 255;
|
|
3895
|
+
this.mod.HEAPU8[ptr + 34] = a.colour?.[2] ?? 255;
|
|
3896
|
+
this.mod.HEAPU8[ptr + 35] = a.colour?.[3] ?? 255;
|
|
3897
|
+
this.mod.HEAP32[(ptr + 36) >> 2] = a.z ?? 0;
|
|
3898
|
+
f32(40, a.opacity ?? 1);
|
|
3899
|
+
u32(44, a.lifetimeKind ?? 0);
|
|
3900
|
+
u32(48, a.lifetimeValue ?? 0);
|
|
3901
|
+
u32(52, 0);
|
|
3902
|
+
u32(56, a.onLost ?? 0);
|
|
3903
|
+
f32(60, a.value ?? 0);
|
|
3904
|
+
if (textPtr !== 0) this.mod.HEAPU8.set(encoded, textPtr);
|
|
3905
|
+
return this.mod.ccall("goss_session_annotate", "number", ["number", "number", "number", "number"], [this.handle, ptr, textPtr, encoded.length]) === GOSS_OK;
|
|
3906
|
+
} finally {
|
|
3907
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3908
|
+
if (textPtr !== 0) this.mod.ccall("goss_free", null, ["number", "number"], [textPtr, encoded.length]);
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3912
|
+
annotationRemove(id: number): boolean {
|
|
3913
|
+
return this.mod.ccall("goss_session_annotation_remove", "number", ["number", "number"], [this.handle, id]) === GOSS_OK;
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
annotationClear(): boolean {
|
|
3917
|
+
return this.mod.ccall("goss_session_annotation_clear", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
/// Live count and how many adds the bound turned away.
|
|
3921
|
+
annotationCount(): { live: number; refused: number } {
|
|
3922
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
3923
|
+
const refusedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
3924
|
+
try {
|
|
3925
|
+
if (this.mod.ccall("goss_session_annotation_count", "number", ["number", "number", "number"], [this.handle, countPtr, refusedPtr]) !== GOSS_OK) {
|
|
3926
|
+
return { live: 0, refused: 0 };
|
|
3927
|
+
}
|
|
3928
|
+
return { live: this.mod.HEAPU32[countPtr >> 2], refused: this.mod.HEAPU32[refusedPtr >> 2] };
|
|
3929
|
+
} finally {
|
|
3930
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3931
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [refusedPtr, 8]);
|
|
3932
|
+
}
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
/// Installs the egress policy: what the brain sees and what it costs.
|
|
3936
|
+
egressConfigure(config: GossEgressConfig): boolean {
|
|
3937
|
+
const bytes = 48;
|
|
3938
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
3939
|
+
try {
|
|
3940
|
+
const u32 = (off: number, v: number) => { this.mod.HEAPU32[(ptr + off) >> 2] = v; };
|
|
3941
|
+
u32(0, config.targetLongEdge ?? 0);
|
|
3942
|
+
u32(4, config.format ?? 0);
|
|
3943
|
+
u32(8, config.quality ?? 80);
|
|
3944
|
+
u32(12, config.maxFps ?? 0);
|
|
3945
|
+
u32(16, config.maxBytesPerSecond ?? 0);
|
|
3946
|
+
u32(20, 0);
|
|
3947
|
+
u32(24, config.source ?? 0);
|
|
3948
|
+
u32(28, config.trigger ?? 0b1010);
|
|
3949
|
+
this.mod.HEAPF32[(ptr + 32) >> 2] = config.changeThreshold ?? 0.02;
|
|
3950
|
+
u32(40, config.keyframeIntervalUs ?? 5_000_000);
|
|
3951
|
+
u32(44, 0);
|
|
3952
|
+
return this.mod.ccall("goss_session_egress_configure", "number", ["number", "number"], [this.handle, ptr]) === GOSS_OK;
|
|
3953
|
+
} finally {
|
|
3954
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
/// One frame, whatever the change score says.
|
|
3959
|
+
egressRequest(): boolean {
|
|
3960
|
+
return this.mod.ccall("goss_session_egress_request", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
/// Whether this frame is worth sending and why. Null when there are no pixels
|
|
3964
|
+
/// to score, which is not a failure.
|
|
3965
|
+
egressDecide(): GossEgressDecision | null {
|
|
3966
|
+
const bytes = 40;
|
|
3967
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
3968
|
+
try {
|
|
3969
|
+
if (this.mod.ccall("goss_session_egress_decide", "number", ["number", "number"], [this.handle, ptr]) !== GOSS_OK) return null;
|
|
3970
|
+
const w = (off: number) => this.mod.HEAPU32[(ptr + off) >> 2];
|
|
3971
|
+
return {
|
|
3972
|
+
send: w(0) !== 0,
|
|
3973
|
+
reason: w(4),
|
|
3974
|
+
changeScore: this.mod.HEAPF32[(ptr + 8) >> 2],
|
|
3975
|
+
sinceLastUs: w(16),
|
|
3976
|
+
sentTotal: w(24),
|
|
3977
|
+
heldTotal: w(32),
|
|
3978
|
+
};
|
|
3979
|
+
} finally {
|
|
3980
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
|
|
3984
|
+
/// The session's events as an async iterable, which is how a page wants them:
|
|
3985
|
+
/// `for await (const e of session.eventStream())`. Polls on an interval because
|
|
3986
|
+
/// the ring is drained by the caller and there is nothing to await on the
|
|
3987
|
+
/// engine side. A drop is yielded as an event of its own rather than swallowed.
|
|
3988
|
+
async *eventStream(pollMs = 16, batch = 64): AsyncGenerator<GossEvent> {
|
|
3989
|
+
for (;;) {
|
|
3990
|
+
const drained = this.pollEvents(batch);
|
|
3991
|
+
for (const event of drained.events) yield event;
|
|
3992
|
+
if (drained.dropped > 0) {
|
|
3993
|
+
yield { kind: 0, sequence: 0, timestampUs: 0, a: drained.dropped, b: 0, value: 0 };
|
|
3994
|
+
}
|
|
3995
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
/// Drains the session's event ring in order. `dropped` says whether anything
|
|
4000
|
+
/// was missed since the last drain, and is cleared by the read.
|
|
4001
|
+
pollEvents(capacity = 64): { events: GossEvent[]; dropped: number } {
|
|
4002
|
+
// kind u32 at 0, sequence u64 at 8, timestamp i64 at 16, a u32 at 24,
|
|
4003
|
+
// b u32 at 28, value f32 at 32; forty bytes with the tail padding.
|
|
4004
|
+
const stride = 40;
|
|
4005
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [capacity * stride]) as number;
|
|
4006
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]) as number;
|
|
4007
|
+
const droppedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
4008
|
+
try {
|
|
4009
|
+
if (this.mod.ccall("goss_session_poll_events", "number", ["number", "number", "number", "number", "number"], [this.handle, ptr, capacity, countPtr, droppedPtr]) !== GOSS_OK) {
|
|
4010
|
+
return { events: [], dropped: 0 };
|
|
4011
|
+
}
|
|
4012
|
+
const n = this.mod.HEAPU32[countPtr >> 2];
|
|
4013
|
+
const events: GossEvent[] = [];
|
|
4014
|
+
for (let i = 0; i < n; i += 1) {
|
|
4015
|
+
const base = ptr + i * stride;
|
|
4016
|
+
events.push({
|
|
4017
|
+
kind: this.mod.HEAPU32[base >> 2],
|
|
4018
|
+
// The low word of the sequence is the whole count at any rate a session
|
|
4019
|
+
// reaches, and reading it avoids a BigInt in the hot drain.
|
|
4020
|
+
sequence: this.mod.HEAPU32[(base + 8) >> 2],
|
|
4021
|
+
timestampUs: this.mod.HEAPU32[(base + 16) >> 2],
|
|
4022
|
+
a: this.mod.HEAPU32[(base + 24) >> 2],
|
|
4023
|
+
b: this.mod.HEAPU32[(base + 28) >> 2],
|
|
4024
|
+
value: this.mod.HEAPF32[(base + 32) >> 2],
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4027
|
+
return { events, dropped: this.mod.HEAPU32[droppedPtr >> 2] };
|
|
4028
|
+
} finally {
|
|
4029
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, capacity * stride]);
|
|
4030
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
4031
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [droppedPtr, 8]);
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
/// A break the page saw and the engine cannot: the camera track ended, the
|
|
4036
|
+
/// tab was hidden. Declared so the gap it leaves is the break rather than
|
|
4037
|
+
/// drift counted against the engine.
|
|
4038
|
+
reportInterruption(kind: GossInterruption): boolean {
|
|
4039
|
+
return this.mod.ccall("goss_session_report_interruption", "number", ["number", "number"], [this.handle, kind]) === GOSS_OK;
|
|
4040
|
+
}
|
|
4041
|
+
|
|
4042
|
+
/// This session's counters: frames in and out, the rung and how often it
|
|
4043
|
+
/// moved, the analysis each modality ran, the nodes that are not ready.
|
|
4044
|
+
sessionReport(): GossSessionReport | null {
|
|
4045
|
+
const bytes = SESSION_REPORT_BYTES;
|
|
4046
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]) as number;
|
|
4047
|
+
try {
|
|
4048
|
+
if (this.mod.ccall("goss_session_read_report", "number", ["number", "number"], [this.handle, ptr]) !== 0) return null;
|
|
4049
|
+
// The low word of each u64 is the whole count at any rate this engine
|
|
4050
|
+
// reaches, and reading it avoids a BigInt in the hot read.
|
|
4051
|
+
const low = (offset: number) => this.mod.HEAPU32[(ptr + offset) >> 2];
|
|
4052
|
+
return {
|
|
4053
|
+
framesSubmitted: low(0),
|
|
4054
|
+
framesRendered: low(8),
|
|
4055
|
+
degradeLevel: low(16),
|
|
4056
|
+
degradeTransitions: low(20),
|
|
4057
|
+
faceAnalysis: low(24),
|
|
4058
|
+
handAnalysis: low(32),
|
|
4059
|
+
poseAnalysis: low(40),
|
|
4060
|
+
segmentationAnalysis: low(48),
|
|
4061
|
+
mlAnalysis: low(56),
|
|
4062
|
+
nodesDegraded: low(64),
|
|
4063
|
+
nodeReportsLost: low(68),
|
|
4064
|
+
scriptFaults: low(72),
|
|
4065
|
+
mlPlanGrowths: low(76),
|
|
4066
|
+
mlPlanBytes: low(80),
|
|
4067
|
+
};
|
|
4068
|
+
} finally {
|
|
4069
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
/// Every node of the active lens that is not doing what its manifest asked,
|
|
4074
|
+
/// beside how many diagnostics could not be recorded at all. An empty list
|
|
4075
|
+
/// with a non-zero lost count means the lens degraded in ways the session
|
|
4076
|
+
/// could not write down, which is not the same as a lens that is fine.
|
|
4077
|
+
nodeReports(): { reports: GossNodeReport[]; lost: number } {
|
|
4078
|
+
const scratch = this.mod.ccall("goss_alloc", "number", ["number"], [8]) as number;
|
|
4079
|
+
let count = 0;
|
|
4080
|
+
let lost = 0;
|
|
4081
|
+
try {
|
|
4082
|
+
const status = this.mod.ccall(
|
|
4083
|
+
"goss_session_node_report_count",
|
|
4084
|
+
"number",
|
|
4085
|
+
["number", "number", "number"],
|
|
4086
|
+
[this.handle, scratch, scratch + 4],
|
|
4087
|
+
) as number;
|
|
4088
|
+
if (status !== 0) return { reports: [], lost: 0 };
|
|
4089
|
+
count = this.mod.HEAPU32[scratch >> 2];
|
|
4090
|
+
lost = this.mod.HEAPU32[(scratch + 4) >> 2];
|
|
4091
|
+
} finally {
|
|
4092
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [scratch, 8]);
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
const reports: GossNodeReport[] = [];
|
|
4096
|
+
// One report is three u32; the id buffer is sized for a manifest node id.
|
|
4097
|
+
const record = this.mod.ccall("goss_alloc", "number", ["number"], [12]) as number;
|
|
4098
|
+
const idBytes = 256;
|
|
4099
|
+
const idPtr = this.mod.ccall("goss_alloc", "number", ["number"], [idBytes + 4]) as number;
|
|
4100
|
+
try {
|
|
4101
|
+
for (let at = 0; at < count; at += 1) {
|
|
4102
|
+
if (this.mod.ccall("goss_session_node_report_at", "number", ["number", "number", "number"], [this.handle, at, record]) !== 0) break;
|
|
4103
|
+
const nodeIndex = this.mod.HEAPU32[record >> 2];
|
|
4104
|
+
const state = this.mod.HEAPU32[(record + 4) >> 2];
|
|
4105
|
+
const reason = this.mod.HEAPU32[(record + 8) >> 2];
|
|
4106
|
+
let id = "";
|
|
4107
|
+
const lenPtr = idPtr + idBytes;
|
|
4108
|
+
if (
|
|
4109
|
+
this.mod.ccall(
|
|
4110
|
+
"goss_session_node_report_id",
|
|
4111
|
+
"number",
|
|
4112
|
+
["number", "number", "number", "number", "number"],
|
|
4113
|
+
[this.handle, at, idPtr, idBytes, lenPtr],
|
|
4114
|
+
) === 0
|
|
4115
|
+
) {
|
|
4116
|
+
const len = this.mod.HEAPU32[lenPtr >> 2];
|
|
4117
|
+
if (len > 0 && len <= idBytes) {
|
|
4118
|
+
id = new TextDecoder().decode(this.mod.HEAPU8.subarray(idPtr, idPtr + len));
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
reports.push({ id, nodeIndex, state: state as GossNodeState, reason: reason as GossNodeReason });
|
|
4122
|
+
}
|
|
4123
|
+
} finally {
|
|
4124
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [record, 12]);
|
|
4125
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [idPtr, idBytes + 4]);
|
|
4126
|
+
}
|
|
4127
|
+
return { reports, lost };
|
|
4128
|
+
}
|
|
4129
|
+
|
|
2881
4130
|
/// Submits a bare camera pose and projection, for a host driving a scan with
|
|
2882
4131
|
/// no platform world session behind it: a selfie scan on the front camera,
|
|
2883
4132
|
/// where the depth comes from a lens's own net rather than a sensor. Both
|
|
@@ -2952,7 +4201,10 @@ export class GossSession {
|
|
|
2952
4201
|
/// Reports one finished frame: measured whole-pipeline time plus
|
|
2953
4202
|
/// thermal pressure (nominal by default - no browser API surfaces
|
|
2954
4203
|
/// device thermal state). Returns the degradation level in effect
|
|
2955
|
-
/// for the next frame.
|
|
4204
|
+
/// for the next frame. A page that never calls this still degrades:
|
|
4205
|
+
/// the engine derives the frame period from the camera timestamps it
|
|
4206
|
+
/// is handed. Calling it lets the page account for its own work
|
|
4207
|
+
/// outside the engine as well.
|
|
2956
4208
|
reportFrame(frameTimeUs: number, thermal: GossThermal = GossThermal.Nominal): GossDegradeLevel {
|
|
2957
4209
|
return this.mod.ccall("goss_session_report_frame", "number", ["number", "number", "number"], [this.handle, frameTimeUs, thermal]);
|
|
2958
4210
|
}
|
|
@@ -3066,12 +4318,20 @@ export class GossPreviewSession {
|
|
|
3066
4318
|
this.session.setBlush(amount);
|
|
3067
4319
|
}
|
|
3068
4320
|
|
|
3069
|
-
|
|
3070
|
-
|
|
4321
|
+
/// Awaited, because activation suspends on the WebGPU build. A caller that
|
|
4322
|
+
/// fires and forgets leaves the module unwound and the next render reenters
|
|
4323
|
+
/// it.
|
|
4324
|
+
activateLens(manifestJson: string): Promise<boolean> {
|
|
4325
|
+
return this.session.activateLens(manifestJson);
|
|
3071
4326
|
}
|
|
3072
4327
|
|
|
3073
|
-
deactivateLens(): void {
|
|
3074
|
-
this.session.deactivateLens();
|
|
4328
|
+
deactivateLens(): Promise<void> {
|
|
4329
|
+
return this.session.deactivateLens();
|
|
4330
|
+
}
|
|
4331
|
+
|
|
4332
|
+
/// True while a lens change is unwound; a frame loop skips its render.
|
|
4333
|
+
get isLensChangeInFlight(): boolean {
|
|
4334
|
+
return this.session.isLensChangeInFlight;
|
|
3075
4335
|
}
|
|
3076
4336
|
|
|
3077
4337
|
tickLens(dtUs: number, signals: GossLensSignals = {}): void {
|
|
@@ -3212,3 +4472,5 @@ export { GossVideoTexture } from "./video-texture.js";
|
|
|
3212
4472
|
// the way the Swift and Kotlin SDKs carry theirs.
|
|
3213
4473
|
export { GossFaceTracker, GossHandTracker, GossPoseTracker, GossSegmenter, GOSS_FACE_BLENDSHAPE_COUNT, GOSS_MAX_HANDS } from "./tracking.js";
|
|
3214
4474
|
export type { GossFaceResult, GossHand, GossHandResult, GossPoseResult } from "./tracking.js";
|
|
4475
|
+
|
|
4476
|
+
export { shareScreen, type GossScreenShare, type GossDisplaySurface } from "./screen-capture.js";
|