@threenative/core 0.3.2 → 0.3.4
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/capabilities.json +1891 -101
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CqvE429w.d.ts} +52 -3
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-DDmC_VVF.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-CljaDv4D.d.ts} +503 -11
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CqJEfWNQ.d.ts} +16 -6
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1496 -43
- package/dist/index.js +8345 -753
- package/dist/playtest.d.ts +11 -5
- package/dist/playtest.js +96 -11
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-CfsS2hxi.d.ts} +436 -14
- package/dist/ui-layer.d.ts +15 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +900 -14
- package/dist/world.js +10876 -126
- package/gpl/fixtures/make_world_fixture.py +184 -0
- package/gpl/recipes/_common.py +11 -0
- package/gpl/recipes/decimate.py +11 -5
- package/gpl/recipes/export_world.py +682 -0
- package/mcp/blender-server.mjs +55 -1
- package/mcp/engine-server.mjs +84 -22
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +2228 -105
- package/scripts/apply-three-patch.mjs +61 -37
|
@@ -1,6 +1,162 @@
|
|
|
1
|
-
import { Object3D, Matrix4, Camera } from 'three';
|
|
1
|
+
import { Object3D, Matrix4, Camera, BufferGeometry } from 'three';
|
|
2
2
|
import { MRTNode, Node } from 'three/webgpu';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Host-boundary counters, default-off, on the frame budget's own record.
|
|
6
|
+
*
|
|
7
|
+
* Two of the three hypotheses about the render phase's unattributed time are about crossings and
|
|
8
|
+
* bytes — "the V8↔host boundary dominates" and "a buffer write per object per pass dominates" —
|
|
9
|
+
* and neither can be answered by a JS CPU profile, because the time is spent on the other side of a
|
|
10
|
+
* call the profiler attributes to a single frame. This counts the calls instead.
|
|
11
|
+
*
|
|
12
|
+
* What it counts, precisely, because a counter that overstates itself is worse than none:
|
|
13
|
+
*
|
|
14
|
+
* - `hostCalls` — every WebGPU method invoked on the device's command encoders and on its queue.
|
|
15
|
+
* On a native host each of those is one crossing; in a browser it is one API call. It is not
|
|
16
|
+
* every crossing a frame makes: `mapAsync`, `getCurrentTexture` and the presentation path are
|
|
17
|
+
* issued by three and by the host outside these objects, and they are named here rather than
|
|
18
|
+
* folded in so the number can be checked against a host that counts its own.
|
|
19
|
+
* - `gpuBytes` — the byte count handed to `queue.writeBuffer`, exactly. Texture uploads are not
|
|
20
|
+
* included: a `writeTexture` size is a function of the destination's format, and estimating it
|
|
21
|
+
* from `data.byteLength` would be a number nobody could reconcile.
|
|
22
|
+
* - `jsAllocBytes` — the change in `performance.memory.usedJSHeapSize`, where the platform has it.
|
|
23
|
+
* Absent on a host without `performance.memory`, never zero: an unmeasurable allocation rate and
|
|
24
|
+
* a frame that allocated nothing are different facts.
|
|
25
|
+
*
|
|
26
|
+
* Wrapping every method of a command encoder is not free — it is one extra JS call per command, and
|
|
27
|
+
* this is why the counters ride the same opt-in flag as the spans rather than being always on.
|
|
28
|
+
*/
|
|
29
|
+
/** One frame's boundary counts. Every field is absent when the platform cannot report it. */
|
|
30
|
+
interface IFrameCounters {
|
|
31
|
+
readonly gpuBytes?: number;
|
|
32
|
+
readonly hostCalls?: number;
|
|
33
|
+
readonly jsAllocBytes?: number;
|
|
34
|
+
}
|
|
35
|
+
/** The slice of a WebGPU device this reads. Structural, so a test can stand in a fake. */
|
|
36
|
+
interface ICounterDevice {
|
|
37
|
+
createCommandEncoder?(): unknown;
|
|
38
|
+
createRenderBundleEncoder?(): unknown;
|
|
39
|
+
queue?: unknown;
|
|
40
|
+
}
|
|
41
|
+
/** The WebGPU device behind a raw three renderer, when the backend exposes one. */
|
|
42
|
+
declare function counterDeviceOf(raw: unknown): unknown;
|
|
43
|
+
/**
|
|
44
|
+
* Counts the frame's boundary crossings and bytes, and reads what the platform will say about
|
|
45
|
+
* allocation. Install once per renderer; `read` returns the counts since the previous `read`.
|
|
46
|
+
*/
|
|
47
|
+
declare class FrameCounters {
|
|
48
|
+
#private;
|
|
49
|
+
private constructor();
|
|
50
|
+
/** Installs the counters on a device, or answers `undefined` when it has nothing to count. */
|
|
51
|
+
static install(device: unknown): FrameCounters | undefined;
|
|
52
|
+
/** The counts since the previous read. */
|
|
53
|
+
read(): IFrameCounters;
|
|
54
|
+
/** Removes every wrapper. A game that disposes its renderer must not leave the counters behind. */
|
|
55
|
+
uninstall(): void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Per-pass draw and triangle attribution, on by default, for every platform.
|
|
60
|
+
*
|
|
61
|
+
* The frame budget already owned the milliseconds; this owns the submissions. Three's
|
|
62
|
+
* `renderer.info.reset()` runs once per frame before the world render, and a nested shadow or
|
|
63
|
+
* reflection `renderer.render(...)` shares that same `info`, so `info.render` reports main plus
|
|
64
|
+
* every nested pass combined. The aggregate cannot tell a 316-draw shadow lane from a 1,418-draw
|
|
65
|
+
* colour pass, and every optimisation attempt had to hand-roll the split. This is the split: each
|
|
66
|
+
* `render()` is one entry on a stack, and its own submissions are the counter delta across its
|
|
67
|
+
* call minus the deltas of the nested calls it made, so the innermost active render call owns
|
|
68
|
+
* exactly what it submitted.
|
|
69
|
+
*
|
|
70
|
+
* Three names its own shadow pass by temporarily renaming the scene to `Shadow Map [ ... ]`
|
|
71
|
+
* (`renderShadow`), and a reflection pass names its scene with `Reflector` in it. Those are
|
|
72
|
+
* borrowed, not invented: the classifier reads three's vocabulary and falls back to `nested`.
|
|
73
|
+
*
|
|
74
|
+
* Measurement, not policy: installing this alters no draw. `install` answers `undefined` on a
|
|
75
|
+
* renderer whose `info.render` cannot be read, and a consumer must then report the pass split as
|
|
76
|
+
* absent rather than zero.
|
|
77
|
+
*/
|
|
78
|
+
/** The named kinds of a render pass. Closed on purpose: a consumer bounds a known kind. */
|
|
79
|
+
declare const FRAME_PASS_KINDS: readonly ["main", "shadow", "reflection", "nested"];
|
|
80
|
+
type FramePassKind = (typeof FRAME_PASS_KINDS)[number];
|
|
81
|
+
/** One render call's own submissions, attributed to its innermost active render call. */
|
|
82
|
+
interface IRenderPassSample {
|
|
83
|
+
readonly draws: number;
|
|
84
|
+
readonly kind: FramePassKind;
|
|
85
|
+
readonly triangles: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type PlatformRuntime = "web" | "native";
|
|
89
|
+
type PlatformOS = "android" | "ios" | "linux" | "macos" | "windows" | "unknown";
|
|
90
|
+
type PlatformFormFactor = "mobile" | "desktop" | "unknown";
|
|
91
|
+
interface IPlatformInfo {
|
|
92
|
+
readonly runtime: PlatformRuntime;
|
|
93
|
+
readonly os: PlatformOS;
|
|
94
|
+
readonly formFactor: PlatformFormFactor;
|
|
95
|
+
readonly maxTouchPoints: number;
|
|
96
|
+
}
|
|
97
|
+
declare function getPlatform(): Readonly<IPlatformInfo>;
|
|
98
|
+
declare function isWeb(): boolean;
|
|
99
|
+
declare function isNative(): boolean;
|
|
100
|
+
declare function isMobile(): boolean;
|
|
101
|
+
declare function isTouchscreenAvailable(): boolean;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* One rule for what `display.maxFps` means when a game does not say.
|
|
105
|
+
*
|
|
106
|
+
* A template that shipped `maxFps: 60` taught every agent to copy the line, and every 120 Hz
|
|
107
|
+
* desktop then ran its game at half the panel it was sitting on — a default that is a constant the
|
|
108
|
+
* author is told to revisit later is a bug, not an option. So the default follows the display,
|
|
109
|
+
* capped at 120 on desktop and web, and stays 60 on mobile where the ceiling is power and heat
|
|
110
|
+
* rather than pixels. An explicit number still wins and `0` still removes the ceiling.
|
|
111
|
+
*
|
|
112
|
+
* **A browser has no refresh-rate API**, so "follows the display" is a measurement: the median
|
|
113
|
+
* interval between presented frames over the frame budget's first window is the panel's period,
|
|
114
|
+
* because on the web one rAF callback is one vblank. A native host says its own rate through the
|
|
115
|
+
* present counter, which is the only series there that counts displays rather than loop
|
|
116
|
+
* iterations. Both arrive as `measuredRefreshHz`; until one does, the answer is 60 and says so.
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
/** What a game gets before anything has been measured, and all a mobile gets. */
|
|
120
|
+
declare const DEFAULT_TARGET_FPS = 60;
|
|
121
|
+
/** The desktop and web ceiling, whatever the panel runs at. */
|
|
122
|
+
declare const MAX_TARGET_FPS = 120;
|
|
123
|
+
/**
|
|
124
|
+
* Where a resolved target came from, so a harness can assert the *rule* and not just the number:
|
|
125
|
+
* `"config"` is the game naming it, `"display"` a measured panel rate, `"mobile-default"` the
|
|
126
|
+
* mobile power ceiling, and `"fallback"` the 60 held until a measurement exists. An unmeasured 60
|
|
127
|
+
* is not the display's rate and does not report as one.
|
|
128
|
+
*/
|
|
129
|
+
type TargetFpsSource = "config" | "display" | "fallback" | "mobile-default";
|
|
130
|
+
/** A resolved frame budget: the rate the loop holds, and what decided it. */
|
|
131
|
+
interface ITargetFps {
|
|
132
|
+
readonly targetFps: number;
|
|
133
|
+
readonly source: TargetFpsSource;
|
|
134
|
+
}
|
|
135
|
+
/** Whatever holds `display.maxFps` — `IGameConfig`, `IThreeNativeConfig`, or a bare object. */
|
|
136
|
+
interface ITargetFpsConfig {
|
|
137
|
+
readonly display?: {
|
|
138
|
+
readonly maxFps?: number | undefined;
|
|
139
|
+
} | undefined;
|
|
140
|
+
}
|
|
141
|
+
/** Only the one field the rule reads, so a caller need not hold a whole `IPlatformInfo`. */
|
|
142
|
+
type ITargetFpsPlatform = Pick<IPlatformInfo, "formFactor">;
|
|
143
|
+
/**
|
|
144
|
+
* The nearest common refresh rate, never above `MAX_TARGET_FPS`.
|
|
145
|
+
*
|
|
146
|
+
* A measured 59.94 Hz panel and a measured 60 Hz one are the same panel, and a value the engine
|
|
147
|
+
* invents rather than reads is a value no display can be asked to hold.
|
|
148
|
+
*/
|
|
149
|
+
declare function snapRefreshRate(refreshHz: number): number;
|
|
150
|
+
/**
|
|
151
|
+
* The single reader of `display.maxFps`, used by the loop, the resolution scaler, the scene-shape
|
|
152
|
+
* warning, the frame-budget marker and the templates.
|
|
153
|
+
*
|
|
154
|
+
* `measuredRefreshHz` is the display's own rate when the platform can say it, and is what turns
|
|
155
|
+
* `"fallback"` into `"display"`. A negative or non-finite configured rate throws rather than
|
|
156
|
+
* quietly falling back: a config the engine ignored is worse than a config it rejected.
|
|
157
|
+
*/
|
|
158
|
+
declare function resolveTargetFps(config: ITargetFpsConfig | undefined, platform: ITargetFpsPlatform | undefined, measuredRefreshHz?: number): ITargetFps;
|
|
159
|
+
|
|
4
160
|
/**
|
|
5
161
|
* Per-presented-frame cost attribution, on by default, for every platform.
|
|
6
162
|
*
|
|
@@ -23,6 +179,7 @@ import { MRTNode, Node } from 'three/webgpu';
|
|
|
23
179
|
* and a phase that was never measured reports zero samples so a consumer asserting on it fails
|
|
24
180
|
* instead of skipping.
|
|
25
181
|
*/
|
|
182
|
+
|
|
26
183
|
/** Marker printed once per report window. */
|
|
27
184
|
declare const FRAME_BUDGET_MARKER = "TN_FRAME_BUDGET";
|
|
28
185
|
/** Marker printed the moment a gap between presented frames exceeds `hitchMs`. */
|
|
@@ -30,9 +187,14 @@ declare const FRAME_HITCH_MARKER = "TN_FRAME_HITCH";
|
|
|
30
187
|
/**
|
|
31
188
|
* The named parts of one presented frame. They partition the frame: `hostGap` is the time before
|
|
32
189
|
* the callback (present wait plus whatever the host did between callbacks), and `update`,
|
|
33
|
-
* `render`, `overlay` and `residual` sum to the callback's own duration.
|
|
190
|
+
* `render`, `overlay`, `ui` and `residual` sum to the callback's own duration.
|
|
191
|
+
*
|
|
192
|
+
* `overlay` and `ui` are two different draws that happen to sit next to each other. `overlay` is
|
|
193
|
+
* the three.js HUD pass; `ui` is the native UI layer's composite of the page's pixels into the
|
|
194
|
+
* game's own frame — one upload and one quad — which is why it is a phase of its own and not part
|
|
195
|
+
* of `overlay`.
|
|
34
196
|
*/
|
|
35
|
-
declare const FRAME_BUDGET_PHASES: readonly ["hostGap", "update", "render", "overlay", "residual"];
|
|
197
|
+
declare const FRAME_BUDGET_PHASES: readonly ["hostGap", "update", "render", "overlay", "ui", "residual"];
|
|
36
198
|
type FrameBudgetPhase = (typeof FRAME_BUDGET_PHASES)[number];
|
|
37
199
|
/** One frame's cost, split by phase. Every field is milliseconds. */
|
|
38
200
|
interface IFramePhaseSample {
|
|
@@ -40,6 +202,7 @@ interface IFramePhaseSample {
|
|
|
40
202
|
readonly update: number;
|
|
41
203
|
readonly render: number;
|
|
42
204
|
readonly overlay: number;
|
|
205
|
+
readonly ui: number;
|
|
43
206
|
readonly residual: number;
|
|
44
207
|
}
|
|
45
208
|
/**
|
|
@@ -82,16 +245,63 @@ interface IFrameBudgetSummary {
|
|
|
82
245
|
readonly p99: number;
|
|
83
246
|
readonly max: number;
|
|
84
247
|
}
|
|
248
|
+
/** Where one resolved frame's GPU milliseconds went. Closed on purpose, like the phases. */
|
|
249
|
+
declare const FRAME_GPU_BUCKETS: readonly ["main", "shadow", "other", "compute"];
|
|
250
|
+
type FrameGpuBucket = (typeof FRAME_GPU_BUCKETS)[number];
|
|
251
|
+
/** One frame's GPU milliseconds per bucket; a bucket the device did not resolve is absent. */
|
|
252
|
+
type IFrameGpuBucketSample = Partial<Record<FrameGpuBucket, number>>;
|
|
253
|
+
/**
|
|
254
|
+
* One render-pass kind's submissions across a window, so a change that trades triangles for CPU is
|
|
255
|
+
* visible in the same report as the milliseconds it traded for.
|
|
256
|
+
*/
|
|
257
|
+
interface IFrameBudgetPassSummary {
|
|
258
|
+
readonly draws: IFrameBudgetSummary;
|
|
259
|
+
/** Frames in the window that submitted a pass of this kind. */
|
|
260
|
+
readonly frames: number;
|
|
261
|
+
readonly triangles: IFrameBudgetSummary;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* One reported window of the frame meter.
|
|
265
|
+
*
|
|
266
|
+
* **The cadence here is the loop's, not necessarily the display's.** `endFrame` is called once per
|
|
267
|
+
* frame the game's loop runs, and on the web that is one `requestAnimationFrame` per vblank, so the
|
|
268
|
+
* interval is a presented frame's. On a native host the loop's dispatch is what drives it — the host
|
|
269
|
+
* says so itself: "the JavaScript budget reads the same interval as presentedDelta"
|
|
270
|
+
* (`runtime-native/src/runtime.cpp`, `executeAnimationFrameCallbacks`). Its presentation cap paces
|
|
271
|
+
* the **present**, never the loop, so a loop that outruns 60 Hz iterates many times per present and
|
|
272
|
+
* this window's `fps` is inflated by exactly that ratio. Measured on midway's native launch:
|
|
273
|
+
* `fps 2631.58` beside the host's own `TN_PRESENTS_TICK:{"frames":1740,"presents":133,"capHz":60}`.
|
|
274
|
+
* The display's rate is the host's series; a reader that has both must not print this one as though
|
|
275
|
+
* it were a player's — `threenative-playtest perf` refuses to (`TN_PERF_VIRTUAL_DISPLAY`).
|
|
276
|
+
*/
|
|
85
277
|
interface IFrameBudgetWindow {
|
|
86
278
|
/** 1 for the first reported window, incrementing thereafter. */
|
|
87
279
|
readonly window: number;
|
|
88
|
-
/**
|
|
280
|
+
/** Loop frames counted in this window, hitches excluded — one per present on the web. */
|
|
89
281
|
readonly frames: number;
|
|
90
282
|
/** Frames excluded from the window because their present gap exceeded `hitchMs`. */
|
|
91
283
|
readonly hitches: number;
|
|
92
|
-
/**
|
|
284
|
+
/**
|
|
285
|
+
* Derived from the mean interval between loop frames. The number a player would read off a
|
|
286
|
+
* counter only where the loop's cadence is the display's, as it is on the web via rAF.
|
|
287
|
+
*/
|
|
93
288
|
readonly fps: number;
|
|
94
|
-
/**
|
|
289
|
+
/**
|
|
290
|
+
* Frames that reached the display in this window, when the host reports them.
|
|
291
|
+
*
|
|
292
|
+
* Absent on the web, where rAF *is* the display's cadence and a second number would be the same
|
|
293
|
+
* one. On a native host the presentation cap lets the loop dispatch many times per present, so
|
|
294
|
+
* this is the count a player saw and `fps` is not. **Zero is a reading**: a window of loop frames
|
|
295
|
+
* can be shorter than one present period, and the display genuinely showed nothing in it.
|
|
296
|
+
*/
|
|
297
|
+
readonly presents?: number;
|
|
298
|
+
/**
|
|
299
|
+
* Presents per second over this window's own duration. Absent when the window counted none —
|
|
300
|
+
* a rate needs at least one present, and a window too short to contain one carries no rate to
|
|
301
|
+
* assess rather than a zero.
|
|
302
|
+
*/
|
|
303
|
+
readonly presentedFps?: number;
|
|
304
|
+
/** Interval between loop frames — the honest frame period where the loop presents every frame. */
|
|
95
305
|
readonly presented: IFrameBudgetSummary;
|
|
96
306
|
/** Duration of the frame callback itself, entry to exit. */
|
|
97
307
|
readonly frame: IFrameBudgetSummary;
|
|
@@ -100,6 +310,26 @@ interface IFrameBudgetWindow {
|
|
|
100
310
|
readonly phases: Readonly<Record<FrameBudgetPhase, IFrameBudgetSummary>>;
|
|
101
311
|
/** Each phase's mean as a fraction of the mean presented interval. */
|
|
102
312
|
readonly shares: Readonly<Record<FrameBudgetPhase, number>>;
|
|
313
|
+
/**
|
|
314
|
+
* Draw calls and triangles submitted per render pass, when a pass recorder was installed.
|
|
315
|
+
*
|
|
316
|
+
* Absent rather than defaulted: a renderer whose submissions nothing measured and a frame that
|
|
317
|
+
* submitted nothing are different facts, and a zero would merge them. A kind no frame submitted
|
|
318
|
+
* is absent; `frames` says how many frames did.
|
|
319
|
+
*/
|
|
320
|
+
readonly passes?: Readonly<Partial<Record<FramePassKind, IFrameBudgetPassSummary>>>;
|
|
321
|
+
/**
|
|
322
|
+
* Instances and triangles the GPU actually selected in the main pass, from the streamed world's
|
|
323
|
+
* own indirect-args tally when it has one.
|
|
324
|
+
*
|
|
325
|
+
* These are the honest counterpart of `passes.main`: three's `renderer.info.render.triangles`
|
|
326
|
+
* counts a mesh's CPU window/capacity for an indirect draw, so `passes.main.triangles` is an upper
|
|
327
|
+
* bound over what the GPU could draw, while `mainGpuTriangles` is the sum over the indirect
|
|
328
|
+
* records of `instanceCount x indexCount / 3` — what the kernel selected. Main pass only: shadow
|
|
329
|
+
* passes are not tallied. Absent, never zero, before the first sample lands.
|
|
330
|
+
*/
|
|
331
|
+
readonly mainGpuInstances?: number;
|
|
332
|
+
readonly mainGpuTriangles?: number;
|
|
103
333
|
/**
|
|
104
334
|
* The resolution and sampling this window's frames were drawn at, when the loop reported one.
|
|
105
335
|
* Absent rather than defaulted: a consumer asserting on it must fail loudly instead of reading
|
|
@@ -107,14 +337,66 @@ interface IFrameBudgetWindow {
|
|
|
107
337
|
*/
|
|
108
338
|
readonly surface?: IFrameSurfaceState;
|
|
109
339
|
/**
|
|
110
|
-
*
|
|
340
|
+
* The rate the loop is holding, and what decided it, when the engine resolved one.
|
|
111
341
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
342
|
+
* Two fields rather than an object, and a harness reads them without unwrapping anything: a
|
|
343
|
+
* number nobody can place (`targetSource: "fallback"`) is the difference between a game holding
|
|
344
|
+
* its panel and a game settling for 60, and only this line says which one happened.
|
|
345
|
+
*/
|
|
346
|
+
readonly targetFps?: number;
|
|
347
|
+
readonly targetSource?: TargetFpsSource;
|
|
348
|
+
/**
|
|
349
|
+
* GPU milliseconds per resolved frame in this window, from `timestamp-query`, summarised like a
|
|
350
|
+
* phase — mean/p50/p95/p99/max over the frames the device actually reported.
|
|
351
|
+
*
|
|
352
|
+
* A single instantaneous `info.render.timestamp` read is lagged by up to `gpuAgeFrames` and
|
|
353
|
+
* spread 3.5x between consecutive reads of one steady frame, so it is not the frame's GPU cost
|
|
354
|
+
* and is not what this reports. Absent rather than zero when no frame resolved a reading: an
|
|
355
|
+
* adapter without timestamps and a frame that genuinely cost no GPU time are different facts,
|
|
356
|
+
* and a zero would merge them.
|
|
357
|
+
*/
|
|
358
|
+
readonly gpu?: IFrameBudgetSummary;
|
|
359
|
+
/**
|
|
360
|
+
* Frames in the window whose GPU reading had not advanced since the previous frame, or was
|
|
361
|
+
* absent. `gpu.samples + gpuStale` is the frames the device was asked about; a window where
|
|
362
|
+
* every frame is stale reports `gpu` absent rather than the last reading looking current.
|
|
363
|
+
*/
|
|
364
|
+
readonly gpuStale: number;
|
|
365
|
+
/**
|
|
366
|
+
* The window mean of `gpu`, when present. The scalar the resolution scaler reads.
|
|
367
|
+
*
|
|
368
|
+
* It is the same number as `gpu.mean`; a single field keeps the scaler and the perf report on
|
|
369
|
+
* one series rather than a second instantaneous read.
|
|
114
370
|
*/
|
|
115
371
|
readonly gpuMs?: number;
|
|
116
|
-
/** Age of the resolved GPU timestamp in Three.js frame IDs; absent means unobservable. */
|
|
372
|
+
/** Age of the most recent resolved GPU timestamp in Three.js frame IDs; absent means unobservable. */
|
|
117
373
|
readonly gpuAgeFrames?: number;
|
|
374
|
+
/**
|
|
375
|
+
* GPU milliseconds per resolved frame, split by where the device spent them: the main scene
|
|
376
|
+
* pass, every shadow pass, everything else in the render pool (post chain, reflections, HUD) and
|
|
377
|
+
* the compute pool. Each is the window's p50 over the frames that resolved it, one decimal.
|
|
378
|
+
*
|
|
379
|
+
* `gpu` is their sum's series; these say which pass owns it. Absent rather than zero when that
|
|
380
|
+
* bucket never resolved — an unmeasured pass and a free one are different facts — so
|
|
381
|
+
* `gpuMain + gpuShadow + gpuOther` reconciles with `gpuMs` only when all three are present.
|
|
382
|
+
*/
|
|
383
|
+
readonly gpuMain?: number;
|
|
384
|
+
readonly gpuShadow?: number;
|
|
385
|
+
readonly gpuOther?: number;
|
|
386
|
+
readonly gpuCompute?: number;
|
|
387
|
+
/**
|
|
388
|
+
* The frame's boundary counts, when something counted them.
|
|
389
|
+
*
|
|
390
|
+
* Each series is absent when nothing measured it, and a series present with zero samples is not
|
|
391
|
+
* possible: an uncounted frame and a frame that crossed the boundary zero times are different
|
|
392
|
+
* facts, and a fabricated zero merges them. `hostCalls` and `gpuBytes` come from
|
|
393
|
+
* `FrameCounters`; `jsAllocBytes` needs a platform that publishes `performance.memory`.
|
|
394
|
+
*/
|
|
395
|
+
readonly counters?: {
|
|
396
|
+
readonly hostCalls?: IFrameBudgetSummary;
|
|
397
|
+
readonly gpuBytes?: IFrameBudgetSummary;
|
|
398
|
+
readonly jsAllocBytes?: IFrameBudgetSummary;
|
|
399
|
+
};
|
|
118
400
|
}
|
|
119
401
|
interface IFrameBudgetOptions {
|
|
120
402
|
/** Presented frames per report window. Default 300. */
|
|
@@ -138,16 +420,39 @@ interface IFrameBudgetOptions {
|
|
|
138
420
|
* loop, which is the only place that knows both the renderer and the window boundary.
|
|
139
421
|
*/
|
|
140
422
|
readonly readSurface?: () => IFrameSurfaceState;
|
|
141
|
-
/**
|
|
142
|
-
|
|
423
|
+
/**
|
|
424
|
+
* The engine's resolved frame budget, read once per reported window.
|
|
425
|
+
*
|
|
426
|
+
* Wired by the game, which owns the rule and the panel measurement behind it. A window with no
|
|
427
|
+
* resolver reports no target rather than a default that looks measured.
|
|
428
|
+
*/
|
|
429
|
+
readonly readTarget?: () => ITargetFps | undefined;
|
|
430
|
+
/**
|
|
431
|
+
* Frames the display has presented so far, when the platform can say.
|
|
432
|
+
*
|
|
433
|
+
* Defaults to the native host's `__tnPresentedCount`, whose presence is the whole signal: the
|
|
434
|
+
* web has no such seam and needs none. A counter that goes backwards or non-finite is ignored
|
|
435
|
+
* for the window rather than reported as a negative rate.
|
|
436
|
+
*/
|
|
437
|
+
readonly readPresentCount?: () => number | undefined;
|
|
143
438
|
/** Reads the successful GPU query frame age, not the age of the last resolve attempt. */
|
|
144
439
|
readonly readGpuAgeFrames?: () => number | undefined;
|
|
440
|
+
/**
|
|
441
|
+
* The GPU-selected main-pass instance and triangle count, read once per reported window.
|
|
442
|
+
*
|
|
443
|
+
* Wired by the game from the streamed world's own tally; `undefined` until its first sample lands
|
|
444
|
+
* or when the world has none, so a window reports absent rather than a fabricated zero.
|
|
445
|
+
*/
|
|
446
|
+
readonly readGpuTally?: () => {
|
|
447
|
+
readonly instances: number;
|
|
448
|
+
readonly triangles: number;
|
|
449
|
+
} | undefined;
|
|
145
450
|
}
|
|
146
451
|
/**
|
|
147
452
|
* Accumulates one frame at a time and reports windowed attribution.
|
|
148
453
|
*
|
|
149
454
|
* The caller is the frame loop; the sequence per frame is
|
|
150
|
-
* `beginFrame` → `markSimulationEnd` → (`addRender` / `addOverlay`) → `endFrame`.
|
|
455
|
+
* `beginFrame` → `markSimulationEnd` → (`addRender` / `addOverlay` / `addUi`) → `endFrame`.
|
|
151
456
|
* Calling them out of order throws rather than producing a plausible-looking split.
|
|
152
457
|
*/
|
|
153
458
|
declare class FrameBudget {
|
|
@@ -165,6 +470,43 @@ declare class FrameBudget {
|
|
|
165
470
|
markSimulationEnd(nowMs: number, substeps: number): void;
|
|
166
471
|
addRender(ms: number): void;
|
|
167
472
|
addOverlay(ms: number): void;
|
|
473
|
+
addUi(ms: number): void;
|
|
474
|
+
/**
|
|
475
|
+
* Records one presented frame's GPU duration, from a resolved `timestamp-query`.
|
|
476
|
+
*
|
|
477
|
+
* `ms` is `undefined` when the device reported no reading for the frame. `frame` is the
|
|
478
|
+
* Three.js frame id the duration belongs to — `gpuFrameSample`/`gpuFrameAge` on the renderer.
|
|
479
|
+
* A reading whose `frame` has not advanced since the previous frame is the previous frame's
|
|
480
|
+
* resolve still in flight, so it is counted as stale and not pushed again; that repetition was
|
|
481
|
+
* what made one lagged sample read as the current frame's cost. Without a `frame` a reading is
|
|
482
|
+
* always taken as fresh, since there is nothing to tell repeats from a genuine re-measurement.
|
|
483
|
+
*
|
|
484
|
+
* Once per frame. A window with no reading at all reports `gpu` absent, never a zero.
|
|
485
|
+
*/
|
|
486
|
+
addGpuMs(ms: number | undefined, frame?: number): void;
|
|
487
|
+
/**
|
|
488
|
+
* Records where one resolved frame's GPU time went, from the per-pass timestamp split.
|
|
489
|
+
*
|
|
490
|
+
* Alongside `addGpuMs`, once per frame. A bucket the producer could not attribute is left out
|
|
491
|
+
* rather than passed as zero: an unmeasured pass and a free one are different facts, and the
|
|
492
|
+
* p50 of a bucket that was never measured is absent from the window. An unknown bucket name
|
|
493
|
+
* throws rather than being dropped, the same fail-closed rule as a phase.
|
|
494
|
+
*/
|
|
495
|
+
addGpuBucketMs(sample: IFrameGpuBucketSample): void;
|
|
496
|
+
/**
|
|
497
|
+
* Records the frame's host-boundary counts, from `FrameCounters` or any other source.
|
|
498
|
+
*
|
|
499
|
+
* At most one call per frame; a second replaces the first rather than summing, because the
|
|
500
|
+
* counter's own reader already returns the frame's totals and summing two reads of one frame
|
|
501
|
+
* would double it. A field the platform cannot report is left out of the series entirely.
|
|
502
|
+
*/
|
|
503
|
+
addCounters(counters: IFrameCounters): void;
|
|
504
|
+
/**
|
|
505
|
+
* Records the frame's per-pass submissions, from `RenderPassBudget` or any other source. At most
|
|
506
|
+
* one entry per kind per frame is meaningful; a second of the same kind is summed by the caller.
|
|
507
|
+
* An unknown kind throws rather than being dropped, the same fail-closed rule as a phase.
|
|
508
|
+
*/
|
|
509
|
+
addRenderPasses(passes: readonly IRenderPassSample[]): void;
|
|
168
510
|
/**
|
|
169
511
|
* Closes the frame and returns its phase split, or `undefined` when the frame was a hitch and
|
|
170
512
|
* therefore excluded — a 27-second startup stall is not a frame time and must not enter a
|
|
@@ -179,6 +521,16 @@ declare class FrameBudget {
|
|
|
179
521
|
endFrame(nowMs: number, wantSample?: boolean): IFramePhaseSample | undefined;
|
|
180
522
|
/** Reads the window in progress without disturbing it. */
|
|
181
523
|
window(): IFrameBudgetWindow;
|
|
524
|
+
/**
|
|
525
|
+
* The render phase of the frame that just closed, or `undefined` when that frame was a hitch and
|
|
526
|
+
* therefore not counted.
|
|
527
|
+
*
|
|
528
|
+
* It exists because the phase split object is optional — `endFrame` builds one only when a
|
|
529
|
+
* consumer asked for per-frame samples, which shipping games do not — and a reader that needs
|
|
530
|
+
* the number must not be forced to turn that allocation on to get it. Reading a measurement and
|
|
531
|
+
* collecting a sample are different requests.
|
|
532
|
+
*/
|
|
533
|
+
get lastRenderMs(): number | undefined;
|
|
182
534
|
}
|
|
183
535
|
|
|
184
536
|
/**
|
|
@@ -663,7 +1015,33 @@ interface IRendererLike {
|
|
|
663
1015
|
* the `TN_ALPHA_ANTIALIASING` marker is printed either way, so nothing is only readable here.
|
|
664
1016
|
*/
|
|
665
1017
|
alphaAntialiasing?: () => IAlphaAntialiasingReport;
|
|
1018
|
+
/**
|
|
1019
|
+
* The `adapter.info` field value that identifies a CPU rasteriser — `swiftshader`, `llvmpipe`,
|
|
1020
|
+
* a `Microsoft Basic Render Driver` — when the adapter named one, else absent.
|
|
1021
|
+
*
|
|
1022
|
+
* Reading it needs `navigator.gpu`, so it is read here rather than in a game's render source.
|
|
1023
|
+
* It is a fact about the machine, not a look: which tier a game runs on a software adapter is
|
|
1024
|
+
* that game's own decision, and every tier name it might pick is already in its own
|
|
1025
|
+
* `src/render/quality.ts`. What this removes is the reason it could not make that decision
|
|
1026
|
+
* before its first expensive frame — a CPU rasteriser running a desktop render chain can lose
|
|
1027
|
+
* the device on the very first frame, which no adaptation after it survives.
|
|
1028
|
+
*
|
|
1029
|
+
* Absent means no software name was found, never that the adapter is hardware. The native host
|
|
1030
|
+
* exposes the same four `adapter.info` fields, so the same read works on every target.
|
|
1031
|
+
*/
|
|
1032
|
+
readonly softwareAdapter?: string;
|
|
666
1033
|
compute(node: unknown): void;
|
|
1034
|
+
/**
|
|
1035
|
+
* Creates the GPU buffers these geometries draw from, through the backend's own attribute path,
|
|
1036
|
+
* and reports how many it created.
|
|
1037
|
+
*
|
|
1038
|
+
* `compileAsync` builds pipelines, not buffers: a streamed mesh's first draw is where its
|
|
1039
|
+
* attributes reach the device, and one chunk's first draw measured 230 ms of a frame for it. This
|
|
1040
|
+
* moves that to admission, one chunk at a time. WebGPU only — the WebGL fallback has no seam
|
|
1041
|
+
* this can call without inventing a GL enum — and absent or throwing answers 0, so the first
|
|
1042
|
+
* draw uploads exactly as it did before.
|
|
1043
|
+
*/
|
|
1044
|
+
uploadAttributes?(geometries: Iterable<BufferGeometry>): number;
|
|
667
1045
|
/**
|
|
668
1046
|
* Copies one GPU storage attribute back to the CPU, asynchronously.
|
|
669
1047
|
*
|
|
@@ -704,6 +1082,44 @@ interface IRendererLike {
|
|
|
704
1082
|
gpuFrameMs(): number | undefined;
|
|
705
1083
|
/** Age in Three.js frame IDs of the resolved render timestamp; absent when unobservable. */
|
|
706
1084
|
gpuFrameAge?(): number | undefined;
|
|
1085
|
+
/**
|
|
1086
|
+
* The last resolved GPU frame's duration and the Three.js frame id it belongs to, or
|
|
1087
|
+
* `undefined` when no resolved reading is available.
|
|
1088
|
+
*
|
|
1089
|
+
* `gpuFrameMs` is that duration alone; the frame id is what tells a reading still in flight
|
|
1090
|
+
* from the current frame's cost, so a caller building a per-frame series never measures one
|
|
1091
|
+
* resolve twice. Optional like `gpuFrameAge`, for stubs that implement only the drawing
|
|
1092
|
+
* contract. `createRenderer` always provides it.
|
|
1093
|
+
*/
|
|
1094
|
+
gpuFrameSample?(): {
|
|
1095
|
+
readonly frame: number;
|
|
1096
|
+
readonly ms: number;
|
|
1097
|
+
} | undefined;
|
|
1098
|
+
/**
|
|
1099
|
+
* GPU milliseconds of the last resolved compute frame, when the adapter reports one.
|
|
1100
|
+
*
|
|
1101
|
+
* The compute pool is a separate series from the render pool and `resolveGpuFrame` resolves it,
|
|
1102
|
+
* so a GPU simulation's cost is measurable instead of being charged to whatever render frame
|
|
1103
|
+
* happened to overlap. `undefined` for a WebGL2 fallback, an adapter without timestamps, or a
|
|
1104
|
+
* frame that ran no compute.
|
|
1105
|
+
*/
|
|
1106
|
+
gpuComputeMs?(): number | undefined;
|
|
1107
|
+
/**
|
|
1108
|
+
* The main render pass's GPU milliseconds, smoothed over fresh resolved samples, or `undefined`
|
|
1109
|
+
* while no reading is fresh.
|
|
1110
|
+
*
|
|
1111
|
+
* `gpuFrameMs` is the whole render pool, main plus every shadow, reflection, post and HUD pass;
|
|
1112
|
+
* the adaptive LOD control loop needs the main-pass share alone. `game.ts` splits the resolved
|
|
1113
|
+
* frame through the pass recorder and feeds the sample here with {@link noteGpuMainMs}. A
|
|
1114
|
+
* repeated frame id is a resolve still in flight and not a new reading, and with no fresh sample
|
|
1115
|
+
* for too long the value reads absent, so a caller never adapts on a stale number.
|
|
1116
|
+
*/
|
|
1117
|
+
gpuMainMs?(): number | undefined;
|
|
1118
|
+
/**
|
|
1119
|
+
* Records one resolved frame's main-pass GPU milliseconds into {@link gpuMainMs}. Called once a
|
|
1120
|
+
* frame by `game.ts`; `ms` is `undefined` when the frame attributed no main-pass reading.
|
|
1121
|
+
*/
|
|
1122
|
+
noteGpuMainMs?(ms: number | undefined, frame?: number): void;
|
|
707
1123
|
/** Starts a resolve of the GPU timestamps for the frames drawn since the last call. */
|
|
708
1124
|
resolveGpuFrame(): void;
|
|
709
1125
|
/**
|
|
@@ -717,6 +1133,11 @@ interface IRendererLike {
|
|
|
717
1133
|
* session because nothing in the measurement could say which one produced it.
|
|
718
1134
|
*/
|
|
719
1135
|
surface(): IFrameSurfaceState;
|
|
1136
|
+
/**
|
|
1137
|
+
* The drawing buffer height on its own, for a caller that wants one number every frame and no
|
|
1138
|
+
* record. Optional so a platform or test double can keep exposing only `surface()`.
|
|
1139
|
+
*/
|
|
1140
|
+
surfaceDrawingBufferHeight?(): number;
|
|
720
1141
|
dispose(): void;
|
|
721
1142
|
}
|
|
722
1143
|
interface IRendererPlatformSource {
|
|
@@ -736,6 +1157,7 @@ interface IRendererOptions {
|
|
|
736
1157
|
/** Requests multisample antialiasing from the renderer. Defaults to true. */
|
|
737
1158
|
antialias?: boolean;
|
|
738
1159
|
canvas?: HTMLCanvasElement;
|
|
1160
|
+
gpuTimestampFrameInterval?: number;
|
|
739
1161
|
preferWebGPU?: boolean;
|
|
740
1162
|
/** CSS-pixel multiplier for the drawing buffer. The default is intentional DPR 1. */
|
|
741
1163
|
resolutionScale?: number;
|
|
@@ -767,4 +1189,4 @@ interface IRendererOptions {
|
|
|
767
1189
|
}>) => unknown;
|
|
768
1190
|
}
|
|
769
1191
|
|
|
770
|
-
export {
|
|
1192
|
+
export { type RenderChainStageId as $, type IRenderChainRequest as A, type IRenderChainStage as B, type IRenderChainStageContext as C, DEFAULT_PIPELINE_CENSUS_LIMIT as D, type IRenderChainVelocityMeasurement as E, type FramePassKind as F, type IRenderChainVelocityReport as G, type IRenderChainVelocityRequest as H, type IRendererLike as I, type IRenderChainVelocityResult as J, type IRenderPassSample as K, type ITargetFps as L, type IVelocityRenderPass as M, MAX_TARGET_FPS as N, PIPELINE_CENSUS_VERSION as O, PIPELINE_CENSUS_CAPABILITY as P, PipelineCensus as Q, type PipelineCensusMode as R, type PipelineCensusStatus as S, type PlatformFormFactor as T, type PlatformOS as U, type PlatformRuntime as V, RENDER_CHAIN_MARKER as W, RENDER_CHAIN_STAGE_ORDER as X, RENDER_CHAIN_TIERS as Y, RenderChain as Z, type RenderChainSource as _, type IFrameBudgetWindow as a, type RenderChainStageName as a0, type RenderChainTier as a1, type RenderChainTierRequest as a2, type RenderChainVelocitySource as a3, type TargetFpsSource as a4, VELOCITY_OUTPUT_NAME as a5, VELOCITY_PREVIOUS_BONE_MATRICES as a6, VELOCITY_PREVIOUS_INSTANCE_MATRICES as a7, VELOCITY_PREVIOUS_WORLD_MATRIX as a8, VelocityTracker as a9, counterDeviceOf as aa, createPipelineCensus as ab, ensureVelocityOutput as ac, getPlatform as ad, isMobile as ae, isNative as af, isTouchscreenAvailable as ag, isWeb as ah, prewarm as ai, readRenderChainObservation as aj, readRenderChainReport as ak, readVelocityPreviousBoneMatrices as al, readVelocityPreviousMatrices as am, readVelocityPreviousWorldMatrix as an, resolveTargetFps as ao, snapRefreshRate as ap, velocityTexture as aq, withVelocityContext as ar, type IRendererOptions as as, DEFAULT_TARGET_FPS as b, FRAME_BUDGET_MARKER as c, FRAME_BUDGET_PHASES as d, FRAME_HITCH_MARKER as e, FrameBudget as f, type FrameBudgetPhase as g, FrameCounters as h, type ICounterDevice as i, type IFrameBudgetOptions as j, type IFrameBudgetPassSummary as k, type IFrameBudgetSummary as l, type IFrameCounters as m, type IFramePhaseSample as n, type IPipelineCensus as o, type IPipelineCensusCounts as p, type IPipelineCensusEvent as q, type IPipelineCensusOptions as r, type IPipelineProvenance as s, type IPipelineShaderObservation as t, type IPlatformInfo as u, type IRenderChainApplied as v, type IRenderChainBudgetWindow as w, type IRenderChainDroppedStage as x, type IRenderChainOptions as y, type IRenderChainRenderer as z };
|
package/dist/ui-layer.d.ts
CHANGED
|
@@ -57,6 +57,15 @@ declare const HIT_REGIONS_MESSAGE = "tn:hit-regions";
|
|
|
57
57
|
declare const GAME_STATE_MESSAGE = "tn:state";
|
|
58
58
|
/** The message the UI end sends when the player acts on a control. */
|
|
59
59
|
declare const UI_INTENT_MESSAGE = "tn:intent";
|
|
60
|
+
/**
|
|
61
|
+
* The scene-shape verdict the game end publishes when it was launched in dev mode.
|
|
62
|
+
*
|
|
63
|
+
* Separate from the state stream because it is not game state: it is the engine's own measurement
|
|
64
|
+
* of the scene, and a game that never asked for dev mode never sends it. No frame rate rides it —
|
|
65
|
+
* the loop's rAF rate reads throttled under a compositor or a virtual display, so a number drawn
|
|
66
|
+
* from it lies in exactly the sessions where somebody is trying to measure.
|
|
67
|
+
*/
|
|
68
|
+
declare const UI_DEV_METRICS_MESSAGE = "tn:dev-metrics";
|
|
60
69
|
/**
|
|
61
70
|
* The intent the UI layer sends once, when its tree has rendered and its rectangles are published.
|
|
62
71
|
*
|
|
@@ -118,16 +127,15 @@ declare function connectUiBridge(options: IConnectOptions): IUiBridge;
|
|
|
118
127
|
* the mirror behaves identically on both, because on web the mirror is fed by the same
|
|
119
128
|
* publication through an in-process channel.
|
|
120
129
|
*
|
|
121
|
-
* Publications are **coalesced**: many
|
|
122
|
-
*
|
|
130
|
+
* Publications are **coalesced**: many simulation writes produce one state message per rendered
|
|
131
|
+
* frame. React consumes that snapshot in the UI realm, independently of the game's render work.
|
|
123
132
|
*/
|
|
124
133
|
/**
|
|
125
134
|
* The minimum a store must offer to be published.
|
|
126
135
|
*
|
|
127
136
|
* `getPublishedState` is optional and preferred when present: ThreeNative's game store keeps a
|
|
128
|
-
* live `getState()` that moves every tick and a `getPublishedState()`
|
|
129
|
-
*
|
|
130
|
-
* every tick, which is the thing React must never do.
|
|
137
|
+
* live `getState()` that moves every tick and a stable `getPublishedState()` snapshot published
|
|
138
|
+
* once per rendered frame, unless the game selected a slower `stateFlushMs` interval.
|
|
131
139
|
*/
|
|
132
140
|
interface IPublishableStore<T> {
|
|
133
141
|
getState(): T;
|
|
@@ -162,7 +170,7 @@ interface IPublishOptions {
|
|
|
162
170
|
* directly would be a second source of truth that only diverges on the platform where the two
|
|
163
171
|
* are actually separate processes.
|
|
164
172
|
* @situation publish game state to a HUD in another realm
|
|
165
|
-
* @situation keep a web and native UI mirror on the same
|
|
173
|
+
* @situation keep a web and native UI mirror on the same coalesced state stream
|
|
166
174
|
* @example const publisher = publishUiState(bridge, store);
|
|
167
175
|
*/
|
|
168
176
|
declare function publishUiState<T>(bridge: IUiBridge, store: IPublishableStore<T>, options?: IPublishOptions): IUiStatePublisher;
|
|
@@ -303,4 +311,4 @@ interface IScopeLike extends IEventTargetLike {
|
|
|
303
311
|
*/
|
|
304
312
|
declare function publishHitRegions(options: IRegistryOptions): IHitRegionRegistry;
|
|
305
313
|
|
|
306
|
-
export { GAME_STATE_MESSAGE, HIT_REGIONS_MESSAGE, type IHitRegion, type IHitRegionRegistry, INTERACTIVE_ATTRIBUTE, type IPublishableStore, type IUiBridge, type IUiMessage, type IUiStateMirror, type IUiStatePublisher, UI_BRIDGE_GLOBALS, UI_INTENT_MESSAGE, UI_READY_INTENT, type UiBridgeEnd, type UiBridgeTransport, connectUiBridge, onUiIntent, publishHitRegions, publishUiState, sendUiIntent, subscribeUiState };
|
|
314
|
+
export { GAME_STATE_MESSAGE, HIT_REGIONS_MESSAGE, type IHitRegion, type IHitRegionRegistry, INTERACTIVE_ATTRIBUTE, type IPublishableStore, type IUiBridge, type IUiMessage, type IUiStateMirror, type IUiStatePublisher, UI_BRIDGE_GLOBALS, UI_DEV_METRICS_MESSAGE, UI_INTENT_MESSAGE, UI_READY_INTENT, type UiBridgeEnd, type UiBridgeTransport, connectUiBridge, onUiIntent, publishHitRegions, publishUiState, sendUiIntent, subscribeUiState };
|
package/dist/ui-layer.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var HIT_REGIONS_MESSAGE = "tn:hit-regions";
|
|
3
3
|
var GAME_STATE_MESSAGE = "tn:state";
|
|
4
4
|
var UI_INTENT_MESSAGE = "tn:intent";
|
|
5
|
+
var UI_DEV_METRICS_MESSAGE = "tn:dev-metrics";
|
|
5
6
|
var UI_READY_INTENT = "tn:ready";
|
|
6
7
|
var UI_BRIDGE_GLOBALS = {
|
|
7
8
|
/** UI end, inbound: the host calls this with one JSON string. */
|
|
@@ -422,4 +423,4 @@ function isDevelopment() {
|
|
|
422
423
|
return import.meta.env?.DEV === true;
|
|
423
424
|
}
|
|
424
425
|
|
|
425
|
-
export { GAME_STATE_MESSAGE, HIT_REGIONS_MESSAGE, INTERACTIVE_ATTRIBUTE, UI_BRIDGE_GLOBALS, UI_INTENT_MESSAGE, UI_READY_INTENT, connectUiBridge, onUiIntent, publishHitRegions, publishUiState, sendUiIntent, subscribeUiState };
|
|
426
|
+
export { GAME_STATE_MESSAGE, HIT_REGIONS_MESSAGE, INTERACTIVE_ATTRIBUTE, UI_BRIDGE_GLOBALS, UI_DEV_METRICS_MESSAGE, UI_INTENT_MESSAGE, UI_READY_INTENT, connectUiBridge, onUiIntent, publishHitRegions, publishUiState, sendUiIntent, subscribeUiState };
|