@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/dist/src/index.js
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
// everything else (the frame graph, all six beauty effects, mirror and
|
|
7
7
|
// rotation) straight to the engine.
|
|
8
8
|
export const GOSS_OK = 0;
|
|
9
|
+
/// The session's scope does not carry the verb an op needs. A host can grant this
|
|
10
|
+
/// one; GOSS_UNSUPPORTED it cannot.
|
|
11
|
+
export const GOSS_OUT_OF_SCOPE = 9;
|
|
12
|
+
/// The mask for a set of verbs, which is what setScope takes.
|
|
13
|
+
export function gossVerbMask(verbs) {
|
|
14
|
+
return verbs.reduce((mask, verb) => mask | (1 << verb), 0);
|
|
15
|
+
}
|
|
16
|
+
export const GOSS_AGAIN = 7;
|
|
9
17
|
const FRAME_FLAG_MIRROR = 0x1;
|
|
10
18
|
const FRAME_ROTATION_SHIFT = 8;
|
|
11
19
|
const LENS_SIGNALS_BYTES = 232;
|
|
@@ -164,6 +172,25 @@ export class Gosslens {
|
|
|
164
172
|
// use fit a number, and a number is what every caller masks against.
|
|
165
173
|
return Number(this.mod.ccall("goss_capabilities", "number", [], []));
|
|
166
174
|
}
|
|
175
|
+
/// The rails a lens declares that this build lacks, as GOSS_CAP_ bits; zero means
|
|
176
|
+
/// every one it asked for is here. A catalogue filters on this rather than
|
|
177
|
+
/// activating a lens to find out.
|
|
178
|
+
lensCapabilitiesMissing(manifestJson) {
|
|
179
|
+
const bytes = new TextEncoder().encode(manifestJson);
|
|
180
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
181
|
+
const out = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
182
|
+
try {
|
|
183
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
184
|
+
const status = this.mod.ccall("goss_lens_capabilities_missing", "number", ["number", "number", "number"], [ptr, bytes.length, out]);
|
|
185
|
+
if (status !== GOSS_OK)
|
|
186
|
+
return 0;
|
|
187
|
+
return this.mod.HEAPU32[out >> 2];
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [out, 8]);
|
|
191
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
167
194
|
/// Loads gosslens_web.js and checks its ABI major version. A
|
|
168
195
|
/// dynamic import, not static: bun's bundler would otherwise inline
|
|
169
196
|
/// this file, breaking Emscripten's own import.meta.url-relative
|
|
@@ -171,9 +198,12 @@ export class Gosslens {
|
|
|
171
198
|
static async load(canvas, wasmJsUrl) {
|
|
172
199
|
const imported = (await import(/* @vite-ignore */ String(wasmJsUrl)));
|
|
173
200
|
const mod = await imported.default({ canvas });
|
|
201
|
+
// The engine decides, rather than this file comparing against a literal major
|
|
202
|
+
// that would go stale the day the major moves.
|
|
174
203
|
const version = mod.ccall("goss_abi_version", "number", [], []) >>> 0;
|
|
175
|
-
if (
|
|
204
|
+
if (mod.ccall("goss_abi_check", "number", ["number"], [GOSS_ABI_MAJOR << 16]) !== GOSS_OK) {
|
|
176
205
|
throw new Error(`gosslens abi major mismatch: ${version >> 16}`);
|
|
206
|
+
}
|
|
177
207
|
return new Gosslens(mod, version);
|
|
178
208
|
}
|
|
179
209
|
/// The YCbCr to RGB conversion for a standard and range as one
|
|
@@ -788,6 +818,137 @@ export class GossEngine {
|
|
|
788
818
|
sum += value;
|
|
789
819
|
return sum;
|
|
790
820
|
}
|
|
821
|
+
/// What the engine is doing now, as against what it was asked for. Every
|
|
822
|
+
/// field is measured. The u64 fields are read as their low word, which holds
|
|
823
|
+
/// the whole count at any rate this engine reaches.
|
|
824
|
+
/// What this build's media backend declares it encodes, as bit sets over the
|
|
825
|
+
/// codec and container enums, so a page asks rather than assuming.
|
|
826
|
+
mediaCapabilities() {
|
|
827
|
+
const bytes = 8 * 4;
|
|
828
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
829
|
+
try {
|
|
830
|
+
if (this.mod.ccall("goss_engine_media_capabilities", "number", ["number", "number"], [this.handle, ptr]) !== GOSS_OK)
|
|
831
|
+
return null;
|
|
832
|
+
const w = (offset) => this.mod.HEAPU32[(ptr + offset) >> 2];
|
|
833
|
+
return {
|
|
834
|
+
videoCodecs: w(0),
|
|
835
|
+
audioCodecs: w(4),
|
|
836
|
+
containers: w(8),
|
|
837
|
+
maxWidth: w(12),
|
|
838
|
+
maxHeight: w(16),
|
|
839
|
+
maxBitDepth: w(20),
|
|
840
|
+
hdr: w(24) !== 0,
|
|
841
|
+
zeroCopy: w(28) !== 0,
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
finally {
|
|
845
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/// Everything capturable. Empty where permission has not been granted, so
|
|
849
|
+
/// prompt rather than treating it as an error.
|
|
850
|
+
screens() {
|
|
851
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
852
|
+
const entryPtr = this.mod.ccall("goss_alloc", "number", ["number"], [40]);
|
|
853
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
854
|
+
const out = [];
|
|
855
|
+
try {
|
|
856
|
+
if (this.mod.ccall("goss_engine_screen_count", "number", ["number", "number"], [this.handle, countPtr]) !== GOSS_OK)
|
|
857
|
+
return out;
|
|
858
|
+
const count = this.mod.HEAPU32[countPtr >> 2];
|
|
859
|
+
for (let i = 0; i < count; i += 1) {
|
|
860
|
+
if (this.mod.ccall("goss_engine_screen_at", "number", ["number", "number", "number"], [this.handle, i, entryPtr]) !== GOSS_OK)
|
|
861
|
+
continue;
|
|
862
|
+
const f = (off) => this.mod.HEAPF32[(entryPtr + off) >> 2];
|
|
863
|
+
const u = (off) => this.mod.HEAPU32[(entryPtr + off) >> 2];
|
|
864
|
+
this.mod.ccall("goss_engine_screen_title", "number", ["number", "number", "number", "number", "number"], [this.handle, i, 0, 0, lenPtr]);
|
|
865
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
866
|
+
let title = "";
|
|
867
|
+
if (needed > 0) {
|
|
868
|
+
const titlePtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
869
|
+
try {
|
|
870
|
+
if (this.mod.ccall("goss_engine_screen_title", "number", ["number", "number", "number", "number", "number"], [this.handle, i, titlePtr, needed, lenPtr]) === GOSS_OK) {
|
|
871
|
+
title = new TextDecoder().decode(this.mod.HEAPU8.subarray(titlePtr, titlePtr + needed));
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
finally {
|
|
875
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [titlePtr, needed]);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
out.push({ id: u(0), kind: u(8), title, logicalWidth: f(12), logicalHeight: f(16), originX: f(20), originY: f(24), scale: f(28) });
|
|
879
|
+
}
|
|
880
|
+
return out;
|
|
881
|
+
}
|
|
882
|
+
finally {
|
|
883
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
884
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [entryPtr, 40]);
|
|
885
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
/// The operators a model needs that this build does not implement. An empty
|
|
889
|
+
/// list means the model runs; anything in it names exactly what is missing.
|
|
890
|
+
mlOpSupport(model) {
|
|
891
|
+
const modelPtr = this.mod.ccall("goss_alloc", "number", ["number"], [model.length]);
|
|
892
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
893
|
+
this.mod.HEAPU8.set(model, modelPtr);
|
|
894
|
+
let capacity = 256;
|
|
895
|
+
try {
|
|
896
|
+
while (capacity <= 1 << 16) {
|
|
897
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [capacity]);
|
|
898
|
+
try {
|
|
899
|
+
const status = this.mod.ccall("goss_ml_op_support", "number", ["number", "number", "number", "number", "number"], [modelPtr, model.length, outPtr, capacity, lenPtr]);
|
|
900
|
+
if (status !== GOSS_OK && status !== GOSS_AGAIN)
|
|
901
|
+
return [];
|
|
902
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
903
|
+
if (needed <= capacity) {
|
|
904
|
+
const text = new TextDecoder().decode(this.mod.HEAPU8.subarray(outPtr, outPtr + needed));
|
|
905
|
+
return text.split("\n").filter((line) => line.length !== 0);
|
|
906
|
+
}
|
|
907
|
+
capacity = needed;
|
|
908
|
+
}
|
|
909
|
+
finally {
|
|
910
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, capacity]);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return [];
|
|
914
|
+
}
|
|
915
|
+
finally {
|
|
916
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [modelPtr, model.length]);
|
|
917
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
engineReport() {
|
|
921
|
+
const bytes = ENGINE_REPORT_BYTES;
|
|
922
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
923
|
+
try {
|
|
924
|
+
if (this.mod.ccall("goss_engine_read_report", "number", ["number", "number"], [this.handle, ptr]) !== 0)
|
|
925
|
+
return null;
|
|
926
|
+
// The low word of each u64 is the whole count at any size this engine
|
|
927
|
+
// reaches, and reading it avoids a BigInt in the hot read.
|
|
928
|
+
const w = (offset) => this.mod.HEAPU32[(ptr + offset) >> 2];
|
|
929
|
+
return {
|
|
930
|
+
rendererBackend: w(0),
|
|
931
|
+
zeroCopyImport: w(4) !== 0,
|
|
932
|
+
texturePoolCapacity: w(8),
|
|
933
|
+
texturePoolLive: w(12),
|
|
934
|
+
texturePoolPeak: w(16),
|
|
935
|
+
texturePoolExhausted: w(20),
|
|
936
|
+
stagingPoolCapacity: w(24),
|
|
937
|
+
stagingPoolLive: w(28),
|
|
938
|
+
stagingPoolPeak: w(32),
|
|
939
|
+
stagingPoolExhausted: w(36),
|
|
940
|
+
texturePoolBins: w(40),
|
|
941
|
+
texturePoolBinsRefused: w(44),
|
|
942
|
+
stagingPoolBins: w(48),
|
|
943
|
+
bgfxLiveBytes: w(56),
|
|
944
|
+
bgfxAllocCallsLastFrame: w(64),
|
|
945
|
+
bgfxBytesLastFrame: w(72),
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
finally {
|
|
949
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
791
952
|
destroy() {
|
|
792
953
|
this.mod.ccall("goss_engine_destroy", null, ["number"], [this.handle]);
|
|
793
954
|
// The renderer is gone now, so bgfx no longer references the selector.
|
|
@@ -797,10 +958,35 @@ export class GossEngine {
|
|
|
797
958
|
this.selectorCapacity = 0;
|
|
798
959
|
}
|
|
799
960
|
}
|
|
800
|
-
///
|
|
801
|
-
///
|
|
802
|
-
///
|
|
803
|
-
|
|
961
|
+
/// The ABI major these bindings were generated against: handing the engine its own
|
|
962
|
+
/// version back would always match and test nothing. The major, not the whole version,
|
|
963
|
+
/// because only the major decides compatibility. Held to the header by the abi gate.
|
|
964
|
+
const GOSS_ABI_MAJOR = 0;
|
|
965
|
+
/// What a report struct measures, checked against the engine's own layout.
|
|
966
|
+
const ENGINE_REPORT_BYTES = 80;
|
|
967
|
+
const SESSION_REPORT_BYTES = 88;
|
|
968
|
+
/// What a lens node is doing, as against what its manifest asked for.
|
|
969
|
+
export var GossNodeState;
|
|
970
|
+
(function (GossNodeState) {
|
|
971
|
+
GossNodeState[GossNodeState["Ready"] = 0] = "Ready";
|
|
972
|
+
GossNodeState[GossNodeState["Degraded"] = 1] = "Degraded";
|
|
973
|
+
GossNodeState[GossNodeState["Failed"] = 2] = "Failed";
|
|
974
|
+
})(GossNodeState || (GossNodeState = {}));
|
|
975
|
+
/// Why a node is not ready.
|
|
976
|
+
export var GossNodeReason;
|
|
977
|
+
(function (GossNodeReason) {
|
|
978
|
+
GossNodeReason[GossNodeReason["None"] = 0] = "None";
|
|
979
|
+
GossNodeReason[GossNodeReason["OutOfMemory"] = 1] = "OutOfMemory";
|
|
980
|
+
GossNodeReason[GossNodeReason["AssetMissing"] = 2] = "AssetMissing";
|
|
981
|
+
GossNodeReason[GossNodeReason["AssetMalformed"] = 3] = "AssetMalformed";
|
|
982
|
+
GossNodeReason[GossNodeReason["AssetTooLarge"] = 4] = "AssetTooLarge";
|
|
983
|
+
GossNodeReason[GossNodeReason["ShaderMissing"] = 5] = "ShaderMissing";
|
|
984
|
+
GossNodeReason[GossNodeReason["ShaderLinkFailed"] = 6] = "ShaderLinkFailed";
|
|
985
|
+
GossNodeReason[GossNodeReason["ModelRejected"] = 7] = "ModelRejected";
|
|
986
|
+
GossNodeReason[GossNodeReason["ModelUnsupported"] = 8] = "ModelUnsupported";
|
|
987
|
+
GossNodeReason[GossNodeReason["CapabilityUnavailable"] = 9] = "CapabilityUnavailable";
|
|
988
|
+
GossNodeReason[GossNodeReason["ConstraintFailed"] = 10] = "ConstraintFailed";
|
|
989
|
+
})(GossNodeReason || (GossNodeReason = {}));
|
|
804
990
|
export class GossSession {
|
|
805
991
|
mod;
|
|
806
992
|
handle;
|
|
@@ -834,6 +1020,7 @@ export class GossSession {
|
|
|
834
1020
|
/// freed in destroy() - no per-call wasm heap churn.
|
|
835
1021
|
scratchPtr = 0;
|
|
836
1022
|
scratchCapacity = 0;
|
|
1023
|
+
lensChangeInFlight = false;
|
|
837
1024
|
constructor(mod, handle) {
|
|
838
1025
|
this.mod = mod;
|
|
839
1026
|
this.handle = handle;
|
|
@@ -905,15 +1092,40 @@ export class GossSession {
|
|
|
905
1092
|
/// A lens built entirely from beauty.* nodes (beauty-baseline, say)
|
|
906
1093
|
/// activates and runs for real regardless, since those go through
|
|
907
1094
|
/// applyWebBeautyChain's own embedded shaders, not a per-lens one.
|
|
908
|
-
|
|
1095
|
+
/// Activates a lens, resolving false when it is live but a node the manifest did
|
|
1096
|
+
/// not mark optional could not do what it asked; nodeReports says which and why.
|
|
1097
|
+
/// On the WebGPU build this suspends and creates work that reaches Dawn, so not
|
|
1098
|
+
/// awaiting left the module suspended and the next render reentered it.
|
|
1099
|
+
async activateLens(manifestJson) {
|
|
909
1100
|
const bytes = new TextEncoder().encode(manifestJson);
|
|
910
1101
|
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
911
1102
|
this.mod.HEAPU8.set(bytes, ptr);
|
|
912
|
-
this.
|
|
913
|
-
|
|
1103
|
+
this.lensChangeInFlight = true;
|
|
1104
|
+
try {
|
|
1105
|
+
const status = (await this.mod.ccall("goss_session_activate_lens", "number", ["number", "number", "number"], [this.handle, ptr, bytes.length], { async: true }));
|
|
1106
|
+
return status === GOSS_OK;
|
|
1107
|
+
}
|
|
1108
|
+
finally {
|
|
1109
|
+
this.lensChangeInFlight = false;
|
|
1110
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
1111
|
+
}
|
|
914
1112
|
}
|
|
915
|
-
|
|
916
|
-
|
|
1113
|
+
/// Deactivation destroys the same resources, so it suspends for the same
|
|
1114
|
+
/// reason and is awaited for the same reason.
|
|
1115
|
+
async deactivateLens() {
|
|
1116
|
+
this.lensChangeInFlight = true;
|
|
1117
|
+
try {
|
|
1118
|
+
await this.mod.ccall("goss_session_deactivate_lens", null, ["number"], [this.handle], { async: true });
|
|
1119
|
+
}
|
|
1120
|
+
finally {
|
|
1121
|
+
this.lensChangeInFlight = false;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
/// True while an activation or deactivation is unwound. A render during that
|
|
1125
|
+
/// window reenters a suspended module, which Asyncify cannot do, so a caller
|
|
1126
|
+
/// driving its own frame loop skips the frame rather than calling in.
|
|
1127
|
+
get isLensChangeInFlight() {
|
|
1128
|
+
return this.lensChangeInFlight;
|
|
917
1129
|
}
|
|
918
1130
|
/// Advances the active lens's triggers/param ramps by dtUs, evaluating
|
|
919
1131
|
/// them against signals - omitted fields read as false/zero, so a bare
|
|
@@ -975,6 +1187,234 @@ export class GossSession {
|
|
|
975
1187
|
/// Casts a world-space ray against the submitted world mesh, returning the
|
|
976
1188
|
/// nearest surface hit `{ point, distance }`, or null when no mesh is
|
|
977
1189
|
/// submitted or the ray misses. A tap-to-place lens anchors content there.
|
|
1190
|
+
/// Decodes a PNG to packed RGBA8 through the decoder the engine already carries,
|
|
1191
|
+
/// for a caller holding an encoded image and none of its own.
|
|
1192
|
+
decodePng(bytes) {
|
|
1193
|
+
const inPtr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
1194
|
+
const wPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1195
|
+
const hPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1196
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
1197
|
+
let outPtr = 0;
|
|
1198
|
+
let needed = 0;
|
|
1199
|
+
try {
|
|
1200
|
+
this.mod.HEAPU8.set(bytes, inPtr);
|
|
1201
|
+
const args = ["number", "number", "number", "number", "number", "number", "number"];
|
|
1202
|
+
this.mod.ccall("goss_engine_decode_png", "number", args, [inPtr, bytes.length, 0, 0, wPtr, hPtr, lenPtr]);
|
|
1203
|
+
needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
1204
|
+
if (needed === 0)
|
|
1205
|
+
return null;
|
|
1206
|
+
outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
1207
|
+
if (this.mod.ccall("goss_engine_decode_png", "number", args, [inPtr, bytes.length, outPtr, needed, wPtr, hPtr, lenPtr]) !== GOSS_OK)
|
|
1208
|
+
return null;
|
|
1209
|
+
return {
|
|
1210
|
+
rgba: this.mod.HEAPU8.slice(outPtr, outPtr + needed),
|
|
1211
|
+
width: this.mod.HEAPU32[wPtr >> 2],
|
|
1212
|
+
height: this.mod.HEAPU32[hPtr >> 2],
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
finally {
|
|
1216
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [inPtr, bytes.length]);
|
|
1217
|
+
if (outPtr !== 0)
|
|
1218
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, needed]);
|
|
1219
|
+
for (const [ptr, size] of [[wPtr, 4], [hPtr, 4], [lenPtr, 8]]) {
|
|
1220
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, size]);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
/// A walkable route over the submitted world mesh, so an agent walks content
|
|
1225
|
+
/// across real scanned ground. Null when no mesh is submitted or no route exists.
|
|
1226
|
+
pathAcrossWorld(start, goal) {
|
|
1227
|
+
const capacity = 256;
|
|
1228
|
+
const aPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
1229
|
+
const bPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
1230
|
+
const outBytes = capacity * 12;
|
|
1231
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [outBytes]);
|
|
1232
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
1233
|
+
try {
|
|
1234
|
+
this.mod.HEAPF32.set(start, aPtr / 4);
|
|
1235
|
+
this.mod.HEAPF32.set(goal, bPtr / 4);
|
|
1236
|
+
const args = ["number", "number", "number", "number", "number", "number"];
|
|
1237
|
+
if (this.mod.ccall("goss_session_path_across_world", "number", args, [this.handle, aPtr, bPtr, outPtr, capacity, countPtr]) !== GOSS_OK)
|
|
1238
|
+
return null;
|
|
1239
|
+
const found = Math.min(this.mod.HEAPU32[countPtr >> 2], capacity);
|
|
1240
|
+
const out = [];
|
|
1241
|
+
for (let i = 0; i < found; i += 1) {
|
|
1242
|
+
const w = (outPtr >> 2) + i * 3;
|
|
1243
|
+
out.push([this.mod.HEAPF32[w], this.mod.HEAPF32[w + 1], this.mod.HEAPF32[w + 2]]);
|
|
1244
|
+
}
|
|
1245
|
+
return out;
|
|
1246
|
+
}
|
|
1247
|
+
finally {
|
|
1248
|
+
for (const [ptr, bytes] of [[aPtr, 12], [bPtr, 12], [outPtr, outBytes], [countPtr, 8]]) {
|
|
1249
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
/// What a submitted plane is, as a named kind rather than the platform's own
|
|
1254
|
+
/// number, and whether a thing can rest on it.
|
|
1255
|
+
planeKind(planeId) {
|
|
1256
|
+
const kindPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1257
|
+
const bearingPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1258
|
+
try {
|
|
1259
|
+
const args = ["number", "number", "number", "number"];
|
|
1260
|
+
if (this.mod.ccall("goss_session_plane_kind", "number", args, [this.handle, planeId, kindPtr, bearingPtr]) !== GOSS_OK)
|
|
1261
|
+
return null;
|
|
1262
|
+
return { kind: this.mod.HEAPU32[kindPtr >> 2], bearing: this.mod.HEAPU32[bearingPtr >> 2] === 1 };
|
|
1263
|
+
}
|
|
1264
|
+
finally {
|
|
1265
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [kindPtr, 4]);
|
|
1266
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [bearingPtr, 4]);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
/// The plane this session would call the floor: the lowest bearing surface it
|
|
1270
|
+
/// has been shown, or null when it has been shown none.
|
|
1271
|
+
floorPlaneId() {
|
|
1272
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
1273
|
+
try {
|
|
1274
|
+
if (this.mod.ccall("goss_session_floor_plane", "number", ["number", "number"], [this.handle, ptr]) !== GOSS_OK)
|
|
1275
|
+
return null;
|
|
1276
|
+
return this.mod.HEAPU32[ptr >> 2];
|
|
1277
|
+
}
|
|
1278
|
+
finally {
|
|
1279
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, 8]);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
/// Where a footprint fits, best surface first: the bearing plane with the most
|
|
1283
|
+
/// room left afterwards. An empty array is an answer.
|
|
1284
|
+
placeOn(item, occupants = []) {
|
|
1285
|
+
const itemPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
1286
|
+
const occupantBytes = occupants.length * 24;
|
|
1287
|
+
const occupantPtr = occupants.length === 0 ? 0 : this.mod.ccall("goss_alloc", "number", ["number"], [occupantBytes]);
|
|
1288
|
+
const capacity = 32;
|
|
1289
|
+
const outBytes = capacity * 24;
|
|
1290
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [outBytes]);
|
|
1291
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
1292
|
+
try {
|
|
1293
|
+
this.mod.HEAPF32.set([item.width, item.depth, item.height ?? 0], itemPtr / 4);
|
|
1294
|
+
// Each occupant is a u64 id then four floats: the id is written as two words
|
|
1295
|
+
// because the heap view is 32-bit and an id past 2^32 is not a plane id any
|
|
1296
|
+
// platform hands out.
|
|
1297
|
+
occupants.forEach((o, i) => {
|
|
1298
|
+
const base = occupantPtr + i * 24;
|
|
1299
|
+
this.mod.HEAPU32[base >> 2] = o.planeId;
|
|
1300
|
+
this.mod.HEAPU32[(base >> 2) + 1] = 0;
|
|
1301
|
+
this.mod.HEAPF32.set([o.x, o.z, o.width, o.depth], (base + 8) / 4);
|
|
1302
|
+
});
|
|
1303
|
+
const args = ["number", "number", "number", "number", "number", "number", "number"];
|
|
1304
|
+
const status = this.mod.ccall("goss_session_place_on", "number", args, [this.handle, itemPtr, occupantPtr, occupants.length, outPtr, capacity, countPtr]);
|
|
1305
|
+
if (status !== GOSS_OK && status !== GOSS_AGAIN)
|
|
1306
|
+
return [];
|
|
1307
|
+
const found = Math.min(this.mod.HEAPU32[countPtr >> 2], capacity);
|
|
1308
|
+
const out = [];
|
|
1309
|
+
for (let i = 0; i < found; i += 1) {
|
|
1310
|
+
const base = outPtr + i * 24;
|
|
1311
|
+
const floats = (base + 8) / 4;
|
|
1312
|
+
out.push({
|
|
1313
|
+
planeId: this.mod.HEAPU32[base >> 2],
|
|
1314
|
+
position: [this.mod.HEAPF32[floats], this.mod.HEAPF32[floats + 1], this.mod.HEAPF32[floats + 2]],
|
|
1315
|
+
freeFraction: this.mod.HEAPF32[floats + 3],
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
return out;
|
|
1319
|
+
}
|
|
1320
|
+
finally {
|
|
1321
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [itemPtr, 12]);
|
|
1322
|
+
if (occupantPtr !== 0)
|
|
1323
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [occupantPtr, occupantBytes]);
|
|
1324
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, outBytes]);
|
|
1325
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 8]);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
/// Point to point in metres with its uncertainty. `known` is false when either
|
|
1329
|
+
/// end vouched for no accuracy, so a sigma of zero is never read as certainty.
|
|
1330
|
+
measureBetween(from, to, fromAccuracyM = 0, toAccuracyM = 0) {
|
|
1331
|
+
const aPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
1332
|
+
const bPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
1333
|
+
const valuePtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1334
|
+
const sigmaPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1335
|
+
const knownPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1336
|
+
try {
|
|
1337
|
+
this.mod.HEAPF32.set(from, aPtr / 4);
|
|
1338
|
+
this.mod.HEAPF32.set(to, bPtr / 4);
|
|
1339
|
+
const args = ["number", "number", "number", "number", "number", "number", "number", "number"];
|
|
1340
|
+
const status = this.mod.ccall("goss_session_measure_between", "number", args, [this.handle, aPtr, fromAccuracyM, bPtr, toAccuracyM, valuePtr, sigmaPtr, knownPtr]);
|
|
1341
|
+
if (status !== GOSS_OK)
|
|
1342
|
+
return null;
|
|
1343
|
+
return {
|
|
1344
|
+
metres: this.mod.HEAPF32[valuePtr >> 2],
|
|
1345
|
+
sigma: this.mod.HEAPF32[sigmaPtr >> 2],
|
|
1346
|
+
known: this.mod.HEAPU32[knownPtr >> 2] === 1,
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
finally {
|
|
1350
|
+
for (const [ptr, bytes] of [[aPtr, 12], [bPtr, 12], [valuePtr, 4], [sigmaPtr, 4], [knownPtr, 4]]) {
|
|
1351
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
/// What this device can offer another: one landmark per world anchor it holds,
|
|
1356
|
+
/// in its own frame. A pose never crosses.
|
|
1357
|
+
sharedLandmarks() {
|
|
1358
|
+
const capacity = 32;
|
|
1359
|
+
const bytes = capacity * 24;
|
|
1360
|
+
const outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
1361
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
1362
|
+
try {
|
|
1363
|
+
const args = ["number", "number", "number", "number"];
|
|
1364
|
+
const status = this.mod.ccall("goss_session_shared_landmarks", "number", args, [this.handle, outPtr, capacity, countPtr]);
|
|
1365
|
+
if (status !== GOSS_OK && status !== GOSS_AGAIN)
|
|
1366
|
+
return [];
|
|
1367
|
+
const found = Math.min(this.mod.HEAPU32[countPtr >> 2], capacity);
|
|
1368
|
+
const out = [];
|
|
1369
|
+
for (let i = 0; i < found; i += 1) {
|
|
1370
|
+
const base = outPtr + i * 24;
|
|
1371
|
+
const floats = (base + 8) / 4;
|
|
1372
|
+
out.push({
|
|
1373
|
+
id: this.mod.HEAPU32[base >> 2],
|
|
1374
|
+
position: [this.mod.HEAPF32[floats], this.mod.HEAPF32[floats + 1], this.mod.HEAPF32[floats + 2]],
|
|
1375
|
+
confidence: this.mod.HEAPF32[floats + 3],
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
return out;
|
|
1379
|
+
}
|
|
1380
|
+
finally {
|
|
1381
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, bytes]);
|
|
1382
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 8]);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
/// The transform from the sender's origin into this one, column-major, with the
|
|
1386
|
+
/// fit it achieved. Null when fewer than three landmarks matched, which cannot
|
|
1387
|
+
/// fix a rigid transform.
|
|
1388
|
+
alignShared(theirs) {
|
|
1389
|
+
const bytes = Math.max(theirs.length, 1) * 24;
|
|
1390
|
+
const listPtr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
1391
|
+
const transformPtr = this.mod.ccall("goss_alloc", "number", ["number"], [64]);
|
|
1392
|
+
const rmsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1393
|
+
const matchedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
1394
|
+
try {
|
|
1395
|
+
theirs.forEach((l, i) => {
|
|
1396
|
+
const base = listPtr + i * 24;
|
|
1397
|
+
this.mod.HEAPU32[base >> 2] = l.id;
|
|
1398
|
+
this.mod.HEAPU32[(base >> 2) + 1] = 0;
|
|
1399
|
+
this.mod.HEAPF32.set([l.position[0], l.position[1], l.position[2], l.confidence ?? 1], (base + 8) / 4);
|
|
1400
|
+
});
|
|
1401
|
+
const args = ["number", "number", "number", "number", "number", "number"];
|
|
1402
|
+
const status = this.mod.ccall("goss_session_align_shared", "number", args, [this.handle, listPtr, theirs.length, transformPtr, rmsPtr, matchedPtr]);
|
|
1403
|
+
if (status !== GOSS_OK)
|
|
1404
|
+
return null;
|
|
1405
|
+
return {
|
|
1406
|
+
transform: this.mod.HEAPF32.slice(transformPtr >> 2, (transformPtr >> 2) + 16),
|
|
1407
|
+
rmsError: this.mod.HEAPF32[rmsPtr >> 2],
|
|
1408
|
+
matched: this.mod.HEAPU32[matchedPtr >> 2],
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
finally {
|
|
1412
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [listPtr, bytes]);
|
|
1413
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [transformPtr, 64]);
|
|
1414
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [rmsPtr, 4]);
|
|
1415
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [matchedPtr, 4]);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
978
1418
|
raycastWorldMesh(origin, direction) {
|
|
979
1419
|
const oPtr = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
980
1420
|
this.mod.HEAPF32.set(origin, oPtr / 4);
|
|
@@ -2366,6 +2806,621 @@ export class GossSession {
|
|
|
2366
2806
|
this.mod.ccall("goss_session_write_reconstruction", "number", ["number", "number", "number"], [this.handle, ptr, gaussians.length / 14]);
|
|
2367
2807
|
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
2368
2808
|
}
|
|
2809
|
+
/// Every section this build writes, asked of the engine rather than assumed: a
|
|
2810
|
+
/// hand-written mask excluded the embedding section the day it was added.
|
|
2811
|
+
selectAll() {
|
|
2812
|
+
return this.mod.ccall("goss_perception_select_all", "number", [], []);
|
|
2813
|
+
}
|
|
2814
|
+
/// How many verbs this engine build knows, asked of the engine rather than taken
|
|
2815
|
+
/// from the enum, so a newer engine behind this wrapper is not misread.
|
|
2816
|
+
verbCount() {
|
|
2817
|
+
return this.mod.ccall("goss_scope_verb_count", "number", [], []);
|
|
2818
|
+
}
|
|
2819
|
+
/// The engine's own name for a verb, so a refusal reads as a sentence and a
|
|
2820
|
+
/// permission prompt reads as words rather than a bitmask.
|
|
2821
|
+
verbName(verb) {
|
|
2822
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
2823
|
+
let outPtr = 0;
|
|
2824
|
+
let needed = 0;
|
|
2825
|
+
try {
|
|
2826
|
+
const args = ["number", "number", "number", "number"];
|
|
2827
|
+
this.mod.ccall("goss_scope_verb_name", "number", args, [verb, 0, 0, lenPtr]);
|
|
2828
|
+
needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
2829
|
+
if (needed === 0)
|
|
2830
|
+
return null;
|
|
2831
|
+
outPtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
2832
|
+
if (this.mod.ccall("goss_scope_verb_name", "number", args, [verb, outPtr, needed, lenPtr]) !== GOSS_OK)
|
|
2833
|
+
return null;
|
|
2834
|
+
return new TextDecoder().decode(this.mod.HEAPU8.subarray(outPtr, outPtr + needed));
|
|
2835
|
+
}
|
|
2836
|
+
finally {
|
|
2837
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
2838
|
+
if (outPtr !== 0)
|
|
2839
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [outPtr, needed]);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
/// One versioned record of what the engine currently sees. Every section
|
|
2843
|
+
/// carries its own tag, version and byte length, so a consumer built against an
|
|
2844
|
+
/// older schema steps over what it does not know. Sized in one retry.
|
|
2845
|
+
perceptionSnapshot(select = this.selectAll()) {
|
|
2846
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
2847
|
+
try {
|
|
2848
|
+
this.mod.ccall("goss_session_perception_snapshot", "number", ["number", "number", "number", "number", "number"], [this.handle, select, 0, 0, lenPtr]);
|
|
2849
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
2850
|
+
if (needed === 0)
|
|
2851
|
+
return null;
|
|
2852
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
2853
|
+
try {
|
|
2854
|
+
const status = this.mod.ccall("goss_session_perception_snapshot", "number", ["number", "number", "number", "number", "number"], [this.handle, select, ptr, needed, lenPtr]);
|
|
2855
|
+
if (status !== GOSS_OK)
|
|
2856
|
+
return null;
|
|
2857
|
+
const written = this.mod.HEAPU32[lenPtr >> 2];
|
|
2858
|
+
return this.mod.HEAPU8.slice(ptr, ptr + written);
|
|
2859
|
+
}
|
|
2860
|
+
finally {
|
|
2861
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
finally {
|
|
2865
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 4]);
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
/// The same record as compact JSON, for a gateway that speaks it.
|
|
2869
|
+
perceptionJson(select = this.selectAll()) {
|
|
2870
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
2871
|
+
try {
|
|
2872
|
+
this.mod.ccall("goss_session_perception_json", "number", ["number", "number", "number", "number", "number"], [this.handle, select, 0, 0, lenPtr]);
|
|
2873
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
2874
|
+
if (needed === 0)
|
|
2875
|
+
return null;
|
|
2876
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
2877
|
+
try {
|
|
2878
|
+
const status = this.mod.ccall("goss_session_perception_json", "number", ["number", "number", "number", "number", "number"], [this.handle, select, ptr, needed, lenPtr]);
|
|
2879
|
+
if (status !== GOSS_OK)
|
|
2880
|
+
return null;
|
|
2881
|
+
const written = this.mod.HEAPU32[lenPtr >> 2];
|
|
2882
|
+
return new TextDecoder().decode(this.mod.HEAPU8.slice(ptr, ptr + written));
|
|
2883
|
+
}
|
|
2884
|
+
finally {
|
|
2885
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
finally {
|
|
2889
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 4]);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
/// Opens a screen as a source. A scale of zero takes the surface's own.
|
|
2893
|
+
openScreen(surfaceId, scale = 0) {
|
|
2894
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
2895
|
+
try {
|
|
2896
|
+
if (this.mod.ccall("goss_session_open_screen", "number", ["number", "number", "number", "number"], [this.handle, surfaceId, scale, ptr]) !== GOSS_OK)
|
|
2897
|
+
return null;
|
|
2898
|
+
return this.mod.HEAPU32[ptr >> 2];
|
|
2899
|
+
}
|
|
2900
|
+
finally {
|
|
2901
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, 4]);
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
closeScreen(screen) {
|
|
2905
|
+
return this.mod.ccall("goss_session_close_screen", "number", ["number", "number"], [this.handle, screen]) === GOSS_OK;
|
|
2906
|
+
}
|
|
2907
|
+
/// Submits the newest frame. False when the screen has not changed, so a still
|
|
2908
|
+
/// desktop costs nothing.
|
|
2909
|
+
stepScreen(screen, source = "") {
|
|
2910
|
+
const bytes = new TextEncoder().encode(source);
|
|
2911
|
+
const ptr = bytes.length === 0 ? 0 : this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
2912
|
+
try {
|
|
2913
|
+
if (ptr !== 0)
|
|
2914
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
2915
|
+
return this.mod.ccall("goss_session_step_screen", "number", ["number", "number", "number", "number"], [this.handle, screen, ptr, bytes.length]) === GOSS_OK;
|
|
2916
|
+
}
|
|
2917
|
+
finally {
|
|
2918
|
+
if (ptr !== 0)
|
|
2919
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
/// Where a normalized point lands: logical points, backing pixels, desktop.
|
|
2923
|
+
screenPoint(screen, x, y) {
|
|
2924
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [24]);
|
|
2925
|
+
try {
|
|
2926
|
+
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)
|
|
2927
|
+
return null;
|
|
2928
|
+
const f = (off) => this.mod.HEAPF32[(ptr + off) >> 2];
|
|
2929
|
+
return { logical: [f(0), f(4)], pixel: [f(8), f(12)], desktop: [f(16), f(20)] };
|
|
2930
|
+
}
|
|
2931
|
+
finally {
|
|
2932
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, 24]);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
/// Narrows what this session answers. A session opens fully permissive; a read
|
|
2936
|
+
/// out of scope is dropped from the record rather than failing, and a verb out
|
|
2937
|
+
/// of scope is refused.
|
|
2938
|
+
setScope(sections, verbs) {
|
|
2939
|
+
return this.mod.ccall("goss_session_set_scope", "number", ["number", "number", "number"], [this.handle, sections, verbs]) === GOSS_OK;
|
|
2940
|
+
}
|
|
2941
|
+
scope() {
|
|
2942
|
+
const sectionsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
2943
|
+
const verbsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
2944
|
+
try {
|
|
2945
|
+
if (this.mod.ccall("goss_session_scope", "number", ["number", "number", "number"], [this.handle, sectionsPtr, verbsPtr]) !== GOSS_OK)
|
|
2946
|
+
return { sections: 0, verbs: 0 };
|
|
2947
|
+
return { sections: this.mod.HEAPU32[sectionsPtr >> 2], verbs: this.mod.HEAPU32[verbsPtr >> 2] };
|
|
2948
|
+
}
|
|
2949
|
+
finally {
|
|
2950
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [sectionsPtr, 4]);
|
|
2951
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [verbsPtr, 4]);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
/// Opens the memory plane. Nothing is remembered until this is called, and
|
|
2955
|
+
/// the bound is yours, so the memory you were promised is the memory you get.
|
|
2956
|
+
memoryOpen(dim, maxEntries = 4096) {
|
|
2957
|
+
return this.mod.ccall("goss_session_memory_open", "number", ["number", "number", "number"], [this.handle, dim, maxEntries]) === GOSS_OK;
|
|
2958
|
+
}
|
|
2959
|
+
memoryClose() {
|
|
2960
|
+
return this.mod.ccall("goss_session_memory_close", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
2961
|
+
}
|
|
2962
|
+
/// Remembers one embedding; the same id replaces rather than duplicating.
|
|
2963
|
+
remember(id, embedding) {
|
|
2964
|
+
const bytes = embedding.length * 4;
|
|
2965
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
2966
|
+
try {
|
|
2967
|
+
this.mod.HEAPF32.set(embedding, ptr >> 2);
|
|
2968
|
+
return this.mod.ccall("goss_session_memory_remember", "number", ["number", "number", "number", "number"], [this.handle, id, ptr, embedding.length]) === GOSS_OK;
|
|
2969
|
+
}
|
|
2970
|
+
finally {
|
|
2971
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
forget(id) {
|
|
2975
|
+
return this.mod.ccall("goss_session_memory_forget", "number", ["number", "number"], [this.handle, id]) === GOSS_OK;
|
|
2976
|
+
}
|
|
2977
|
+
/// The nearest remembered embeddings, fewer than k on a smaller memory rather
|
|
2978
|
+
/// than padded with nothing.
|
|
2979
|
+
memorySearch(query, k = 8) {
|
|
2980
|
+
const queryBytes = query.length * 4;
|
|
2981
|
+
const queryPtr = this.mod.ccall("goss_alloc", "number", ["number"], [queryBytes]);
|
|
2982
|
+
const idsPtr = this.mod.ccall("goss_alloc", "number", ["number"], [k * 8]);
|
|
2983
|
+
const scoresPtr = this.mod.ccall("goss_alloc", "number", ["number"], [k * 4]);
|
|
2984
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
2985
|
+
try {
|
|
2986
|
+
this.mod.HEAPF32.set(query, queryPtr >> 2);
|
|
2987
|
+
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)
|
|
2988
|
+
return [];
|
|
2989
|
+
const count = this.mod.HEAPU32[countPtr >> 2];
|
|
2990
|
+
const out = [];
|
|
2991
|
+
for (let i = 0; i < count; i += 1) {
|
|
2992
|
+
// The low word of the id is the whole value at any count this engine
|
|
2993
|
+
// reaches, and reading it avoids a BigInt in the hot read.
|
|
2994
|
+
out.push({ id: this.mod.HEAPU32[(idsPtr + i * 8) >> 2], score: this.mod.HEAPF32[(scoresPtr + i * 4) >> 2] });
|
|
2995
|
+
}
|
|
2996
|
+
return out;
|
|
2997
|
+
}
|
|
2998
|
+
finally {
|
|
2999
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [queryPtr, queryBytes]);
|
|
3000
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [idsPtr, k * 8]);
|
|
3001
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [scoresPtr, k * 4]);
|
|
3002
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
memoryStats() {
|
|
3006
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
3007
|
+
const bytesPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3008
|
+
try {
|
|
3009
|
+
if (this.mod.ccall("goss_session_memory_stats", "number", ["number", "number", "number"], [this.handle, countPtr, bytesPtr]) !== GOSS_OK)
|
|
3010
|
+
return { count: 0, bytes: 0 };
|
|
3011
|
+
return { count: this.mod.HEAPU32[countPtr >> 2], bytes: this.mod.HEAPU32[bytesPtr >> 2] };
|
|
3012
|
+
}
|
|
3013
|
+
finally {
|
|
3014
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3015
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [bytesPtr, 8]);
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
/// The whole memory as bytes, so a cold start is instant.
|
|
3019
|
+
memorySave() {
|
|
3020
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3021
|
+
try {
|
|
3022
|
+
this.mod.ccall("goss_session_memory_save", "number", ["number", "number", "number", "number"], [this.handle, 0, 0, lenPtr]);
|
|
3023
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3024
|
+
if (needed === 0)
|
|
3025
|
+
return new Uint8Array(0);
|
|
3026
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
3027
|
+
try {
|
|
3028
|
+
if (this.mod.ccall("goss_session_memory_save", "number", ["number", "number", "number", "number"], [this.handle, ptr, needed, lenPtr]) !== GOSS_OK)
|
|
3029
|
+
return new Uint8Array(0);
|
|
3030
|
+
return new Uint8Array(this.mod.HEAPU8.subarray(ptr, ptr + needed));
|
|
3031
|
+
}
|
|
3032
|
+
finally {
|
|
3033
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
finally {
|
|
3037
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
/// The memory sealed under a host key. The nonce is yours: reusing one under
|
|
3041
|
+
/// the same key breaks the cipher.
|
|
3042
|
+
memorySaveSealed(key, nonce) {
|
|
3043
|
+
const keyPtr = this.mod.ccall("goss_alloc", "number", ["number"], [key.length]);
|
|
3044
|
+
const noncePtr = this.mod.ccall("goss_alloc", "number", ["number"], [nonce.length]);
|
|
3045
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3046
|
+
try {
|
|
3047
|
+
this.mod.HEAPU8.set(key, keyPtr);
|
|
3048
|
+
this.mod.HEAPU8.set(nonce, noncePtr);
|
|
3049
|
+
this.mod.ccall("goss_session_memory_save_sealed", "number", ["number", "number", "number", "number", "number", "number"], [this.handle, keyPtr, noncePtr, 0, 0, lenPtr]);
|
|
3050
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3051
|
+
if (needed === 0)
|
|
3052
|
+
return new Uint8Array(0);
|
|
3053
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
3054
|
+
try {
|
|
3055
|
+
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)
|
|
3056
|
+
return new Uint8Array(0);
|
|
3057
|
+
return new Uint8Array(this.mod.HEAPU8.subarray(ptr, ptr + needed));
|
|
3058
|
+
}
|
|
3059
|
+
finally {
|
|
3060
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, needed]);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
finally {
|
|
3064
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [keyPtr, key.length]);
|
|
3065
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [noncePtr, nonce.length]);
|
|
3066
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
memoryLoadSealed(key, bytes) {
|
|
3070
|
+
const keyPtr = this.mod.ccall("goss_alloc", "number", ["number"], [key.length]);
|
|
3071
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
3072
|
+
try {
|
|
3073
|
+
this.mod.HEAPU8.set(key, keyPtr);
|
|
3074
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
3075
|
+
return this.mod.ccall("goss_session_memory_load_sealed", "number", ["number", "number", "number", "number"], [this.handle, keyPtr, ptr, bytes.length]) === GOSS_OK;
|
|
3076
|
+
}
|
|
3077
|
+
finally {
|
|
3078
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [keyPtr, key.length]);
|
|
3079
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
memoryLoad(bytes) {
|
|
3083
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes.length]);
|
|
3084
|
+
try {
|
|
3085
|
+
this.mod.HEAPU8.set(bytes, ptr);
|
|
3086
|
+
return this.mod.ccall("goss_session_memory_load", "number", ["number", "number", "number"], [this.handle, ptr, bytes.length]) === GOSS_OK;
|
|
3087
|
+
}
|
|
3088
|
+
finally {
|
|
3089
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes.length]);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
/// Turns on the text rail. A detector alone finds where the text is, which
|
|
3093
|
+
/// is what a redaction or a rectified crop needs; pass a recogniser and its
|
|
3094
|
+
/// dictionary to get strings back.
|
|
3095
|
+
enableText(detector, recognizer, dictionary, detectSide = 320) {
|
|
3096
|
+
const rec = recognizer ?? new Uint8Array(0);
|
|
3097
|
+
const dict = dictionary ?? new Uint8Array(0);
|
|
3098
|
+
const detPtr = this.mod.ccall("goss_alloc", "number", ["number"], [detector.length]);
|
|
3099
|
+
const recPtr = rec.length === 0 ? 0 : this.mod.ccall("goss_alloc", "number", ["number"], [rec.length]);
|
|
3100
|
+
const dictPtr = dict.length === 0 ? 0 : this.mod.ccall("goss_alloc", "number", ["number"], [dict.length]);
|
|
3101
|
+
try {
|
|
3102
|
+
this.mod.HEAPU8.set(detector, detPtr);
|
|
3103
|
+
if (recPtr !== 0)
|
|
3104
|
+
this.mod.HEAPU8.set(rec, recPtr);
|
|
3105
|
+
if (dictPtr !== 0)
|
|
3106
|
+
this.mod.HEAPU8.set(dict, dictPtr);
|
|
3107
|
+
return this.mod.ccall("goss_session_enable_text", "number", ["number", "number", "number", "number", "number", "number", "number", "number"], [this.handle, detPtr, detector.length, recPtr, rec.length, dictPtr, dict.length, detectSide]) === GOSS_OK;
|
|
3108
|
+
}
|
|
3109
|
+
finally {
|
|
3110
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [detPtr, detector.length]);
|
|
3111
|
+
if (recPtr !== 0)
|
|
3112
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [recPtr, rec.length]);
|
|
3113
|
+
if (dictPtr !== 0)
|
|
3114
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [dictPtr, dict.length]);
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
disableText() {
|
|
3118
|
+
return this.mod.ccall("goss_session_disable_text", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3119
|
+
}
|
|
3120
|
+
/// How many readings the frame holds and how many the bound turned away.
|
|
3121
|
+
textCount() {
|
|
3122
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
3123
|
+
const refusedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3124
|
+
try {
|
|
3125
|
+
if (this.mod.ccall("goss_session_text_count", "number", ["number", "number", "number"], [this.handle, countPtr, refusedPtr]) !== GOSS_OK) {
|
|
3126
|
+
return { live: 0, refused: 0 };
|
|
3127
|
+
}
|
|
3128
|
+
return { live: this.mod.HEAPU32[countPtr >> 2], refused: this.mod.HEAPU32[refusedPtr >> 2] };
|
|
3129
|
+
}
|
|
3130
|
+
finally {
|
|
3131
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3132
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [refusedPtr, 8]);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
/// Everything the frame says, each reading with its quadrilateral and string.
|
|
3136
|
+
readings() {
|
|
3137
|
+
const { live } = this.textCount();
|
|
3138
|
+
const entryBytes = 64;
|
|
3139
|
+
const entryPtr = this.mod.ccall("goss_alloc", "number", ["number"], [entryBytes]);
|
|
3140
|
+
const lenPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3141
|
+
const out = [];
|
|
3142
|
+
try {
|
|
3143
|
+
for (let i = 0; i < live; i += 1) {
|
|
3144
|
+
if (this.mod.ccall("goss_session_text_at", "number", ["number", "number", "number"], [this.handle, i, entryPtr]) !== GOSS_OK)
|
|
3145
|
+
continue;
|
|
3146
|
+
const f32 = (off) => this.mod.HEAPF32[(entryPtr + off) >> 2];
|
|
3147
|
+
const u32 = (off) => this.mod.HEAPU32[(entryPtr + off) >> 2];
|
|
3148
|
+
this.mod.ccall("goss_session_text_string", "number", ["number", "number", "number", "number", "number"], [this.handle, i, 0, 0, lenPtr]);
|
|
3149
|
+
const needed = this.mod.HEAPU32[lenPtr >> 2];
|
|
3150
|
+
let text = "";
|
|
3151
|
+
if (needed > 0) {
|
|
3152
|
+
const textPtr = this.mod.ccall("goss_alloc", "number", ["number"], [needed]);
|
|
3153
|
+
try {
|
|
3154
|
+
if (this.mod.ccall("goss_session_text_string", "number", ["number", "number", "number", "number", "number"], [this.handle, i, textPtr, needed, lenPtr]) === GOSS_OK) {
|
|
3155
|
+
text = new TextDecoder().decode(this.mod.HEAPU8.subarray(textPtr, textPtr + needed));
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
finally {
|
|
3159
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [textPtr, needed]);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
out.push({
|
|
3163
|
+
text,
|
|
3164
|
+
quad: [f32(0), f32(4), f32(8), f32(12), f32(16), f32(20), f32(24), f32(28)],
|
|
3165
|
+
confidence: f32(32),
|
|
3166
|
+
origin: u32(36),
|
|
3167
|
+
script: u32(40),
|
|
3168
|
+
direction: u32(44),
|
|
3169
|
+
trackId: u32(48),
|
|
3170
|
+
line: u32(52),
|
|
3171
|
+
paragraph: u32(56),
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
return out;
|
|
3175
|
+
}
|
|
3176
|
+
finally {
|
|
3177
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [entryPtr, entryBytes]);
|
|
3178
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [lenPtr, 8]);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
/// Adds or updates one annotation. The same id replaces rather than
|
|
3182
|
+
/// duplicating, so moving one box every frame leaks no entry per frame.
|
|
3183
|
+
annotate(a, text = "") {
|
|
3184
|
+
const bytes = 64;
|
|
3185
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
3186
|
+
const encoded = new TextEncoder().encode(text);
|
|
3187
|
+
const textPtr = encoded.length === 0 ? 0 : this.mod.ccall("goss_alloc", "number", ["number"], [encoded.length]);
|
|
3188
|
+
try {
|
|
3189
|
+
const u32 = (off, v) => { this.mod.HEAPU32[(ptr + off) >> 2] = v; };
|
|
3190
|
+
const f32 = (off, v) => { this.mod.HEAPF32[(ptr + off) >> 2] = v; };
|
|
3191
|
+
u32(0, a.id);
|
|
3192
|
+
u32(4, a.kind);
|
|
3193
|
+
u32(8, a.space ?? 0);
|
|
3194
|
+
for (let i = 0; i < 4; i += 1)
|
|
3195
|
+
f32(12 + i * 4, a.rect?.[i] ?? 0);
|
|
3196
|
+
u32(28, a.trackId ?? 0);
|
|
3197
|
+
this.mod.HEAPU8[ptr + 32] = a.colour?.[0] ?? 255;
|
|
3198
|
+
this.mod.HEAPU8[ptr + 33] = a.colour?.[1] ?? 255;
|
|
3199
|
+
this.mod.HEAPU8[ptr + 34] = a.colour?.[2] ?? 255;
|
|
3200
|
+
this.mod.HEAPU8[ptr + 35] = a.colour?.[3] ?? 255;
|
|
3201
|
+
this.mod.HEAP32[(ptr + 36) >> 2] = a.z ?? 0;
|
|
3202
|
+
f32(40, a.opacity ?? 1);
|
|
3203
|
+
u32(44, a.lifetimeKind ?? 0);
|
|
3204
|
+
u32(48, a.lifetimeValue ?? 0);
|
|
3205
|
+
u32(52, 0);
|
|
3206
|
+
u32(56, a.onLost ?? 0);
|
|
3207
|
+
f32(60, a.value ?? 0);
|
|
3208
|
+
if (textPtr !== 0)
|
|
3209
|
+
this.mod.HEAPU8.set(encoded, textPtr);
|
|
3210
|
+
return this.mod.ccall("goss_session_annotate", "number", ["number", "number", "number", "number"], [this.handle, ptr, textPtr, encoded.length]) === GOSS_OK;
|
|
3211
|
+
}
|
|
3212
|
+
finally {
|
|
3213
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3214
|
+
if (textPtr !== 0)
|
|
3215
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [textPtr, encoded.length]);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
annotationRemove(id) {
|
|
3219
|
+
return this.mod.ccall("goss_session_annotation_remove", "number", ["number", "number"], [this.handle, id]) === GOSS_OK;
|
|
3220
|
+
}
|
|
3221
|
+
annotationClear() {
|
|
3222
|
+
return this.mod.ccall("goss_session_annotation_clear", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3223
|
+
}
|
|
3224
|
+
/// Live count and how many adds the bound turned away.
|
|
3225
|
+
annotationCount() {
|
|
3226
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
3227
|
+
const refusedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3228
|
+
try {
|
|
3229
|
+
if (this.mod.ccall("goss_session_annotation_count", "number", ["number", "number", "number"], [this.handle, countPtr, refusedPtr]) !== GOSS_OK) {
|
|
3230
|
+
return { live: 0, refused: 0 };
|
|
3231
|
+
}
|
|
3232
|
+
return { live: this.mod.HEAPU32[countPtr >> 2], refused: this.mod.HEAPU32[refusedPtr >> 2] };
|
|
3233
|
+
}
|
|
3234
|
+
finally {
|
|
3235
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3236
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [refusedPtr, 8]);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
/// Installs the egress policy: what the brain sees and what it costs.
|
|
3240
|
+
egressConfigure(config) {
|
|
3241
|
+
const bytes = 48;
|
|
3242
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
3243
|
+
try {
|
|
3244
|
+
const u32 = (off, v) => { this.mod.HEAPU32[(ptr + off) >> 2] = v; };
|
|
3245
|
+
u32(0, config.targetLongEdge ?? 0);
|
|
3246
|
+
u32(4, config.format ?? 0);
|
|
3247
|
+
u32(8, config.quality ?? 80);
|
|
3248
|
+
u32(12, config.maxFps ?? 0);
|
|
3249
|
+
u32(16, config.maxBytesPerSecond ?? 0);
|
|
3250
|
+
u32(20, 0);
|
|
3251
|
+
u32(24, config.source ?? 0);
|
|
3252
|
+
u32(28, config.trigger ?? 0b1010);
|
|
3253
|
+
this.mod.HEAPF32[(ptr + 32) >> 2] = config.changeThreshold ?? 0.02;
|
|
3254
|
+
u32(40, config.keyframeIntervalUs ?? 5_000_000);
|
|
3255
|
+
u32(44, 0);
|
|
3256
|
+
return this.mod.ccall("goss_session_egress_configure", "number", ["number", "number"], [this.handle, ptr]) === GOSS_OK;
|
|
3257
|
+
}
|
|
3258
|
+
finally {
|
|
3259
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
/// One frame, whatever the change score says.
|
|
3263
|
+
egressRequest() {
|
|
3264
|
+
return this.mod.ccall("goss_session_egress_request", "number", ["number"], [this.handle]) === GOSS_OK;
|
|
3265
|
+
}
|
|
3266
|
+
/// Whether this frame is worth sending and why. Null when there are no pixels
|
|
3267
|
+
/// to score, which is not a failure.
|
|
3268
|
+
egressDecide() {
|
|
3269
|
+
const bytes = 40;
|
|
3270
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
3271
|
+
try {
|
|
3272
|
+
if (this.mod.ccall("goss_session_egress_decide", "number", ["number", "number"], [this.handle, ptr]) !== GOSS_OK)
|
|
3273
|
+
return null;
|
|
3274
|
+
const w = (off) => this.mod.HEAPU32[(ptr + off) >> 2];
|
|
3275
|
+
return {
|
|
3276
|
+
send: w(0) !== 0,
|
|
3277
|
+
reason: w(4),
|
|
3278
|
+
changeScore: this.mod.HEAPF32[(ptr + 8) >> 2],
|
|
3279
|
+
sinceLastUs: w(16),
|
|
3280
|
+
sentTotal: w(24),
|
|
3281
|
+
heldTotal: w(32),
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
finally {
|
|
3285
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
/// The session's events as an async iterable, which is how a page wants them:
|
|
3289
|
+
/// `for await (const e of session.eventStream())`. Polls on an interval because
|
|
3290
|
+
/// the ring is drained by the caller and there is nothing to await on the
|
|
3291
|
+
/// engine side. A drop is yielded as an event of its own rather than swallowed.
|
|
3292
|
+
async *eventStream(pollMs = 16, batch = 64) {
|
|
3293
|
+
for (;;) {
|
|
3294
|
+
const drained = this.pollEvents(batch);
|
|
3295
|
+
for (const event of drained.events)
|
|
3296
|
+
yield event;
|
|
3297
|
+
if (drained.dropped > 0) {
|
|
3298
|
+
yield { kind: 0, sequence: 0, timestampUs: 0, a: drained.dropped, b: 0, value: 0 };
|
|
3299
|
+
}
|
|
3300
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
/// Drains the session's event ring in order. `dropped` says whether anything
|
|
3304
|
+
/// was missed since the last drain, and is cleared by the read.
|
|
3305
|
+
pollEvents(capacity = 64) {
|
|
3306
|
+
// kind u32 at 0, sequence u64 at 8, timestamp i64 at 16, a u32 at 24,
|
|
3307
|
+
// b u32 at 28, value f32 at 32; forty bytes with the tail padding.
|
|
3308
|
+
const stride = 40;
|
|
3309
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [capacity * stride]);
|
|
3310
|
+
const countPtr = this.mod.ccall("goss_alloc", "number", ["number"], [4]);
|
|
3311
|
+
const droppedPtr = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3312
|
+
try {
|
|
3313
|
+
if (this.mod.ccall("goss_session_poll_events", "number", ["number", "number", "number", "number", "number"], [this.handle, ptr, capacity, countPtr, droppedPtr]) !== GOSS_OK) {
|
|
3314
|
+
return { events: [], dropped: 0 };
|
|
3315
|
+
}
|
|
3316
|
+
const n = this.mod.HEAPU32[countPtr >> 2];
|
|
3317
|
+
const events = [];
|
|
3318
|
+
for (let i = 0; i < n; i += 1) {
|
|
3319
|
+
const base = ptr + i * stride;
|
|
3320
|
+
events.push({
|
|
3321
|
+
kind: this.mod.HEAPU32[base >> 2],
|
|
3322
|
+
// The low word of the sequence is the whole count at any rate a session
|
|
3323
|
+
// reaches, and reading it avoids a BigInt in the hot drain.
|
|
3324
|
+
sequence: this.mod.HEAPU32[(base + 8) >> 2],
|
|
3325
|
+
timestampUs: this.mod.HEAPU32[(base + 16) >> 2],
|
|
3326
|
+
a: this.mod.HEAPU32[(base + 24) >> 2],
|
|
3327
|
+
b: this.mod.HEAPU32[(base + 28) >> 2],
|
|
3328
|
+
value: this.mod.HEAPF32[(base + 32) >> 2],
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
return { events, dropped: this.mod.HEAPU32[droppedPtr >> 2] };
|
|
3332
|
+
}
|
|
3333
|
+
finally {
|
|
3334
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, capacity * stride]);
|
|
3335
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [countPtr, 4]);
|
|
3336
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [droppedPtr, 8]);
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
/// A break the page saw and the engine cannot: the camera track ended, the
|
|
3340
|
+
/// tab was hidden. Declared so the gap it leaves is the break rather than
|
|
3341
|
+
/// drift counted against the engine.
|
|
3342
|
+
reportInterruption(kind) {
|
|
3343
|
+
return this.mod.ccall("goss_session_report_interruption", "number", ["number", "number"], [this.handle, kind]) === GOSS_OK;
|
|
3344
|
+
}
|
|
3345
|
+
/// This session's counters: frames in and out, the rung and how often it
|
|
3346
|
+
/// moved, the analysis each modality ran, the nodes that are not ready.
|
|
3347
|
+
sessionReport() {
|
|
3348
|
+
const bytes = SESSION_REPORT_BYTES;
|
|
3349
|
+
const ptr = this.mod.ccall("goss_alloc", "number", ["number"], [bytes]);
|
|
3350
|
+
try {
|
|
3351
|
+
if (this.mod.ccall("goss_session_read_report", "number", ["number", "number"], [this.handle, ptr]) !== 0)
|
|
3352
|
+
return null;
|
|
3353
|
+
// The low word of each u64 is the whole count at any rate this engine
|
|
3354
|
+
// reaches, and reading it avoids a BigInt in the hot read.
|
|
3355
|
+
const low = (offset) => this.mod.HEAPU32[(ptr + offset) >> 2];
|
|
3356
|
+
return {
|
|
3357
|
+
framesSubmitted: low(0),
|
|
3358
|
+
framesRendered: low(8),
|
|
3359
|
+
degradeLevel: low(16),
|
|
3360
|
+
degradeTransitions: low(20),
|
|
3361
|
+
faceAnalysis: low(24),
|
|
3362
|
+
handAnalysis: low(32),
|
|
3363
|
+
poseAnalysis: low(40),
|
|
3364
|
+
segmentationAnalysis: low(48),
|
|
3365
|
+
mlAnalysis: low(56),
|
|
3366
|
+
nodesDegraded: low(64),
|
|
3367
|
+
nodeReportsLost: low(68),
|
|
3368
|
+
scriptFaults: low(72),
|
|
3369
|
+
mlPlanGrowths: low(76),
|
|
3370
|
+
mlPlanBytes: low(80),
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
finally {
|
|
3374
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [ptr, bytes]);
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
/// Every node of the active lens that is not doing what its manifest asked,
|
|
3378
|
+
/// beside how many diagnostics could not be recorded at all. An empty list
|
|
3379
|
+
/// with a non-zero lost count means the lens degraded in ways the session
|
|
3380
|
+
/// could not write down, which is not the same as a lens that is fine.
|
|
3381
|
+
nodeReports() {
|
|
3382
|
+
const scratch = this.mod.ccall("goss_alloc", "number", ["number"], [8]);
|
|
3383
|
+
let count = 0;
|
|
3384
|
+
let lost = 0;
|
|
3385
|
+
try {
|
|
3386
|
+
const status = this.mod.ccall("goss_session_node_report_count", "number", ["number", "number", "number"], [this.handle, scratch, scratch + 4]);
|
|
3387
|
+
if (status !== 0)
|
|
3388
|
+
return { reports: [], lost: 0 };
|
|
3389
|
+
count = this.mod.HEAPU32[scratch >> 2];
|
|
3390
|
+
lost = this.mod.HEAPU32[(scratch + 4) >> 2];
|
|
3391
|
+
}
|
|
3392
|
+
finally {
|
|
3393
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [scratch, 8]);
|
|
3394
|
+
}
|
|
3395
|
+
const reports = [];
|
|
3396
|
+
// One report is three u32; the id buffer is sized for a manifest node id.
|
|
3397
|
+
const record = this.mod.ccall("goss_alloc", "number", ["number"], [12]);
|
|
3398
|
+
const idBytes = 256;
|
|
3399
|
+
const idPtr = this.mod.ccall("goss_alloc", "number", ["number"], [idBytes + 4]);
|
|
3400
|
+
try {
|
|
3401
|
+
for (let at = 0; at < count; at += 1) {
|
|
3402
|
+
if (this.mod.ccall("goss_session_node_report_at", "number", ["number", "number", "number"], [this.handle, at, record]) !== 0)
|
|
3403
|
+
break;
|
|
3404
|
+
const nodeIndex = this.mod.HEAPU32[record >> 2];
|
|
3405
|
+
const state = this.mod.HEAPU32[(record + 4) >> 2];
|
|
3406
|
+
const reason = this.mod.HEAPU32[(record + 8) >> 2];
|
|
3407
|
+
let id = "";
|
|
3408
|
+
const lenPtr = idPtr + idBytes;
|
|
3409
|
+
if (this.mod.ccall("goss_session_node_report_id", "number", ["number", "number", "number", "number", "number"], [this.handle, at, idPtr, idBytes, lenPtr]) === 0) {
|
|
3410
|
+
const len = this.mod.HEAPU32[lenPtr >> 2];
|
|
3411
|
+
if (len > 0 && len <= idBytes) {
|
|
3412
|
+
id = new TextDecoder().decode(this.mod.HEAPU8.subarray(idPtr, idPtr + len));
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
reports.push({ id, nodeIndex, state: state, reason: reason });
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
finally {
|
|
3419
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [record, 12]);
|
|
3420
|
+
this.mod.ccall("goss_free", null, ["number", "number"], [idPtr, idBytes + 4]);
|
|
3421
|
+
}
|
|
3422
|
+
return { reports, lost };
|
|
3423
|
+
}
|
|
2369
3424
|
/// Submits a bare camera pose and projection, for a host driving a scan with
|
|
2370
3425
|
/// no platform world session behind it: a selfie scan on the front camera,
|
|
2371
3426
|
/// where the depth comes from a lens's own net rather than a sensor. Both
|
|
@@ -2437,7 +3492,10 @@ export class GossSession {
|
|
|
2437
3492
|
/// Reports one finished frame: measured whole-pipeline time plus
|
|
2438
3493
|
/// thermal pressure (nominal by default - no browser API surfaces
|
|
2439
3494
|
/// device thermal state). Returns the degradation level in effect
|
|
2440
|
-
/// for the next frame.
|
|
3495
|
+
/// for the next frame. A page that never calls this still degrades:
|
|
3496
|
+
/// the engine derives the frame period from the camera timestamps it
|
|
3497
|
+
/// is handed. Calling it lets the page account for its own work
|
|
3498
|
+
/// outside the engine as well.
|
|
2441
3499
|
reportFrame(frameTimeUs, thermal = 0 /* GossThermal.Nominal */) {
|
|
2442
3500
|
return this.mod.ccall("goss_session_report_frame", "number", ["number", "number", "number"], [this.handle, frameTimeUs, thermal]);
|
|
2443
3501
|
}
|
|
@@ -2542,11 +3600,18 @@ export class GossPreviewSession {
|
|
|
2542
3600
|
setBlush(amount) {
|
|
2543
3601
|
this.session.setBlush(amount);
|
|
2544
3602
|
}
|
|
3603
|
+
/// Awaited, because activation suspends on the WebGPU build. A caller that
|
|
3604
|
+
/// fires and forgets leaves the module unwound and the next render reenters
|
|
3605
|
+
/// it.
|
|
2545
3606
|
activateLens(manifestJson) {
|
|
2546
|
-
this.session.activateLens(manifestJson);
|
|
3607
|
+
return this.session.activateLens(manifestJson);
|
|
2547
3608
|
}
|
|
2548
3609
|
deactivateLens() {
|
|
2549
|
-
this.session.deactivateLens();
|
|
3610
|
+
return this.session.deactivateLens();
|
|
3611
|
+
}
|
|
3612
|
+
/// True while a lens change is unwound; a frame loop skips its render.
|
|
3613
|
+
get isLensChangeInFlight() {
|
|
3614
|
+
return this.session.isLensChangeInFlight;
|
|
2550
3615
|
}
|
|
2551
3616
|
tickLens(dtUs, signals = {}) {
|
|
2552
3617
|
this.session.tickLens(dtUs, signals);
|
|
@@ -2668,4 +3733,5 @@ export { GossVideoTexture } from "./video-texture.js";
|
|
|
2668
3733
|
// The trackers the tracking module runs, so a page reaches them through the one package entry
|
|
2669
3734
|
// the way the Swift and Kotlin SDKs carry theirs.
|
|
2670
3735
|
export { GossFaceTracker, GossHandTracker, GossPoseTracker, GossSegmenter, GOSS_FACE_BLENDSHAPE_COUNT, GOSS_MAX_HANDS } from "./tracking.js";
|
|
3736
|
+
export { shareScreen } from "./screen-capture.js";
|
|
2671
3737
|
//# sourceMappingURL=index.js.map
|