@threenative/core 0.3.3 → 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 +555 -21
- package/dist/{assets-CYKk2WTu.d.ts → assets-CqvE429w.d.ts} +45 -3
- package/dist/{canvas-layer-C1SnMoJ-.d.ts → canvas-layer-DDmC_VVF.d.ts} +1 -1
- package/dist/{game-D_6r-k4Y.d.ts → game-CljaDv4D.d.ts} +144 -10
- package/dist/{gpu-readback-CMklJs6r.d.ts → gpu-readback-CqJEfWNQ.d.ts} +16 -6
- package/dist/hot.d.ts +4 -4
- package/dist/index.d.ts +467 -35
- package/dist/index.js +3208 -257
- package/dist/playtest.d.ts +11 -5
- package/dist/playtest.js +55 -5
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-Cy4qeBOA.d.ts → renderer-CfsS2hxi.d.ts} +190 -2
- package/dist/ui-layer.d.ts +5 -3
- 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 +54 -20
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +2101 -138
- package/scripts/apply-three-patch.mjs +61 -37
package/dist/playtest.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { JsonValue, JsonPrimitive } from '@threenative/playtest/protocol';
|
|
2
|
-
import { b as IGamePluginHooks } from './game-
|
|
3
|
-
import './assets-
|
|
2
|
+
import { b as IGamePluginHooks } from './game-CljaDv4D.js';
|
|
3
|
+
import './assets-CqvE429w.js';
|
|
4
4
|
import 'three';
|
|
5
|
-
import './renderer-
|
|
5
|
+
import './renderer-CfsS2hxi.js';
|
|
6
6
|
import 'three/webgpu';
|
|
7
|
-
import './canvas-layer-
|
|
7
|
+
import './canvas-layer-DDmC_VVF.js';
|
|
8
8
|
import 'zustand/vanilla';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -31,6 +31,12 @@ declare const PLAYTEST_ATTACH_TIMEOUT_MS = 30000;
|
|
|
31
31
|
*/
|
|
32
32
|
/** The global a playtest runner sets before the page loads, so a game can tell one is coming. */
|
|
33
33
|
declare const PLAYTEST_RUNNER_EXPECTED_GLOBAL = "__THREENATIVE_PLAYTEST_RUNNER_EXPECTED__";
|
|
34
|
+
/**
|
|
35
|
+
* The clock a consumer asks this game to run on, named in the protocol's own vocabulary. Set before
|
|
36
|
+
* the game module evaluates — a production profile does it in the instrumentation it injects ahead
|
|
37
|
+
* of the bundle, on the browser and on native alike.
|
|
38
|
+
*/
|
|
39
|
+
declare const PLAYTEST_CLOCK_GLOBAL = "__THREENATIVE_PLAYTEST_CLOCK__";
|
|
34
40
|
interface IPlaytestOptions {
|
|
35
41
|
readonly events?: () => JsonValue[];
|
|
36
42
|
/**
|
|
@@ -63,4 +69,4 @@ interface IPlaytestTransition {
|
|
|
63
69
|
to: JsonPrimitive;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
export { type IPlaytestOptions, type IPlaytestTransition, PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
|
72
|
+
export { type IPlaytestOptions, type IPlaytestTransition, PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_CLOCK_GLOBAL, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
package/dist/playtest.js
CHANGED
|
@@ -42,7 +42,7 @@ new Vector3();
|
|
|
42
42
|
new Vector3();
|
|
43
43
|
|
|
44
44
|
// src/version.ts
|
|
45
|
-
var CORE_VERSION = "0.3.
|
|
45
|
+
var CORE_VERSION = "0.3.4";
|
|
46
46
|
|
|
47
47
|
// src/pipeline-census.ts
|
|
48
48
|
var PIPELINE_CENSUS_CAPABILITY = "runtime.pipelineCensus";
|
|
@@ -92,6 +92,8 @@ function playtest(options = {}) {
|
|
|
92
92
|
setup: async (ctx, runtime) => {
|
|
93
93
|
readTick = runtime?.tick;
|
|
94
94
|
const seed = runtime?.seed ?? null;
|
|
95
|
+
const clockMode = requestedClockMode();
|
|
96
|
+
const wallClock = clockMode === "wall-clock";
|
|
95
97
|
const replayRuntime = runtime?.seed === null || runtime?.seed === void 0 ? void 0 : {
|
|
96
98
|
agent: currentAgent,
|
|
97
99
|
core: CORE_VERSION,
|
|
@@ -101,9 +103,15 @@ function playtest(options = {}) {
|
|
|
101
103
|
};
|
|
102
104
|
const installation = installThreePlaytestBridge({
|
|
103
105
|
camera: ctx.camera,
|
|
106
|
+
...clockMode === void 0 ? {} : { clockMode },
|
|
104
107
|
entities: () => bridgeEntities(ctx),
|
|
105
108
|
components: () => componentObservations(ctx.entities.snapshot()),
|
|
106
|
-
|
|
109
|
+
// The live clock has to be driven by the host's own frame pump, so a live run's `advance`
|
|
110
|
+
// waits the span of wall time the count names and reports the ticks that pump ran.
|
|
111
|
+
...runtime === void 0 ? {} : {
|
|
112
|
+
fixedStep: wallClock ? wallClockAdvance(runtime) : runtime.fixedStep,
|
|
113
|
+
tick: runtime.tick
|
|
114
|
+
},
|
|
107
115
|
...runtime?.runtimeDiagnosticsSeries === void 0 ? {} : { runtimeDiagnosticsSeries: runtime.runtimeDiagnosticsSeries },
|
|
108
116
|
...options.events === void 0 ? {} : { events: options.events },
|
|
109
117
|
gameplay: () => gameplayObservations(ctx, contactHistory, seed, replayRuntime, watcher),
|
|
@@ -154,7 +162,10 @@ function playtest(options = {}) {
|
|
|
154
162
|
});
|
|
155
163
|
}
|
|
156
164
|
installRuntimeChannels(installation.bridge, runtime);
|
|
157
|
-
if (runnerAnnounced())
|
|
165
|
+
if (runnerAnnounced()) {
|
|
166
|
+
runtime?.enableRuntimeDiagnostics?.();
|
|
167
|
+
if (!wallClock) runtime?.freezeClock?.();
|
|
168
|
+
}
|
|
158
169
|
dispose = installation.dispose;
|
|
159
170
|
attached = holdUntilAttached(installation.bridge, options, () => startSceneEntered);
|
|
160
171
|
const cleanup = () => {
|
|
@@ -187,6 +198,27 @@ function playtest(options = {}) {
|
|
|
187
198
|
}
|
|
188
199
|
var PLAYTEST_ATTACH_TIMEOUT_MS = 3e4;
|
|
189
200
|
var PLAYTEST_RUNNER_EXPECTED_GLOBAL = "__THREENATIVE_PLAYTEST_RUNNER_EXPECTED__";
|
|
201
|
+
var PLAYTEST_CLOCK_GLOBAL = "__THREENATIVE_PLAYTEST_CLOCK__";
|
|
202
|
+
function requestedClockMode() {
|
|
203
|
+
const requested = globalThis[PLAYTEST_CLOCK_GLOBAL];
|
|
204
|
+
if (requested === void 0) return void 0;
|
|
205
|
+
if (requested === "wall-clock") return "wall-clock";
|
|
206
|
+
throw new Error(
|
|
207
|
+
`TN_PLAYTEST_CLOCK_UNSUPPORTED: '${PLAYTEST_CLOCK_GLOBAL}' is ${JSON.stringify(requested)}; this protocol knows 'wall-clock' and nothing else.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
var WALL_CLOCK_PUMP_STEPS = 4;
|
|
211
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
+
function wallClockAdvance(runtime) {
|
|
213
|
+
const stepMs = runtime.step * 1e3;
|
|
214
|
+
return async (ticks) => {
|
|
215
|
+
const before = runtime.tick();
|
|
216
|
+
await sleep(ticks * stepMs);
|
|
217
|
+
for (let waited = 0; waited < WALL_CLOCK_PUMP_STEPS && runtime.tick() === before; waited += 1)
|
|
218
|
+
await sleep(stepMs / 2);
|
|
219
|
+
return runtime.tick() - before;
|
|
220
|
+
};
|
|
221
|
+
}
|
|
190
222
|
function shouldHoldUntilAttached(options) {
|
|
191
223
|
if (options.holdUntilAttached !== void 0) return options.holdUntilAttached;
|
|
192
224
|
return runnerAnnounced();
|
|
@@ -469,11 +501,29 @@ function stateResources(ctx) {
|
|
|
469
501
|
// `state` is canonical. Keep `GameState` as a compatibility alias until published
|
|
470
502
|
// scenarios have migrated, then remove the alias in a future breaking release.
|
|
471
503
|
["state", value],
|
|
472
|
-
["GameState", value]
|
|
504
|
+
["GameState", value],
|
|
505
|
+
// Where every asset was actually served from, on every target and not only in a browser
|
|
506
|
+
// console log: a game whose manifest never loaded runs on the uncompiled source directory
|
|
507
|
+
// and looks healthy, and without this nothing observable says so.
|
|
508
|
+
["assets", assetResolutions(ctx.assets)]
|
|
473
509
|
]);
|
|
474
510
|
}
|
|
475
511
|
};
|
|
476
512
|
}
|
|
513
|
+
function assetResolutions(assets) {
|
|
514
|
+
const nested = {};
|
|
515
|
+
for (const [logical, record] of assets.resolved) {
|
|
516
|
+
const parts = logical.split(".");
|
|
517
|
+
let node = nested;
|
|
518
|
+
for (const part of parts.slice(0, -1)) {
|
|
519
|
+
const child = node[part] ?? {};
|
|
520
|
+
node[part] = child;
|
|
521
|
+
node = child;
|
|
522
|
+
}
|
|
523
|
+
node[parts.at(-1) ?? logical] = { url: record.url, via: record.via };
|
|
524
|
+
}
|
|
525
|
+
return nested;
|
|
526
|
+
}
|
|
477
527
|
function cloneJsonValue(value) {
|
|
478
528
|
if (Array.isArray(value)) return value.map((item) => cloneJsonValue(item));
|
|
479
529
|
if (typeof value === "object" && value !== null) {
|
|
@@ -484,4 +534,4 @@ function cloneJsonValue(value) {
|
|
|
484
534
|
return value;
|
|
485
535
|
}
|
|
486
536
|
|
|
487
|
-
export { PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
|
537
|
+
export { PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_CLOCK_GLOBAL, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { C as CanvasLayer } from './canvas-layer-
|
|
2
|
+
import { C as CanvasLayer } from './canvas-layer-DDmC_VVF.js';
|
|
3
3
|
import 'three';
|
|
4
|
-
import './renderer-
|
|
4
|
+
import './renderer-CfsS2hxi.js';
|
|
5
5
|
import 'three/webgpu';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
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
4
|
/**
|
|
@@ -85,6 +85,78 @@ interface IRenderPassSample {
|
|
|
85
85
|
readonly triangles: number;
|
|
86
86
|
}
|
|
87
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
|
+
|
|
88
160
|
/**
|
|
89
161
|
* Per-presented-frame cost attribution, on by default, for every platform.
|
|
90
162
|
*
|
|
@@ -173,6 +245,11 @@ interface IFrameBudgetSummary {
|
|
|
173
245
|
readonly p99: number;
|
|
174
246
|
readonly max: number;
|
|
175
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>>;
|
|
176
253
|
/**
|
|
177
254
|
* One render-pass kind's submissions across a window, so a change that trades triangles for CPU is
|
|
178
255
|
* visible in the same report as the milliseconds it traded for.
|
|
@@ -241,12 +318,33 @@ interface IFrameBudgetWindow {
|
|
|
241
318
|
* is absent; `frames` says how many frames did.
|
|
242
319
|
*/
|
|
243
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;
|
|
244
333
|
/**
|
|
245
334
|
* The resolution and sampling this window's frames were drawn at, when the loop reported one.
|
|
246
335
|
* Absent rather than defaulted: a consumer asserting on it must fail loudly instead of reading
|
|
247
336
|
* a fabricated `1.0` that no frame was ever drawn at.
|
|
248
337
|
*/
|
|
249
338
|
readonly surface?: IFrameSurfaceState;
|
|
339
|
+
/**
|
|
340
|
+
* The rate the loop is holding, and what decided it, when the engine resolved one.
|
|
341
|
+
*
|
|
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;
|
|
250
348
|
/**
|
|
251
349
|
* GPU milliseconds per resolved frame in this window, from `timestamp-query`, summarised like a
|
|
252
350
|
* phase — mean/p50/p95/p99/max over the frames the device actually reported.
|
|
@@ -273,6 +371,19 @@ interface IFrameBudgetWindow {
|
|
|
273
371
|
readonly gpuMs?: number;
|
|
274
372
|
/** Age of the most recent resolved GPU timestamp in Three.js frame IDs; absent means unobservable. */
|
|
275
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;
|
|
276
387
|
/**
|
|
277
388
|
* The frame's boundary counts, when something counted them.
|
|
278
389
|
*
|
|
@@ -309,6 +420,13 @@ interface IFrameBudgetOptions {
|
|
|
309
420
|
* loop, which is the only place that knows both the renderer and the window boundary.
|
|
310
421
|
*/
|
|
311
422
|
readonly readSurface?: () => IFrameSurfaceState;
|
|
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;
|
|
312
430
|
/**
|
|
313
431
|
* Frames the display has presented so far, when the platform can say.
|
|
314
432
|
*
|
|
@@ -319,6 +437,16 @@ interface IFrameBudgetOptions {
|
|
|
319
437
|
readonly readPresentCount?: () => number | undefined;
|
|
320
438
|
/** Reads the successful GPU query frame age, not the age of the last resolve attempt. */
|
|
321
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;
|
|
322
450
|
}
|
|
323
451
|
/**
|
|
324
452
|
* Accumulates one frame at a time and reports windowed attribution.
|
|
@@ -356,6 +484,15 @@ declare class FrameBudget {
|
|
|
356
484
|
* Once per frame. A window with no reading at all reports `gpu` absent, never a zero.
|
|
357
485
|
*/
|
|
358
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;
|
|
359
496
|
/**
|
|
360
497
|
* Records the frame's host-boundary counts, from `FrameCounters` or any other source.
|
|
361
498
|
*
|
|
@@ -878,7 +1015,33 @@ interface IRendererLike {
|
|
|
878
1015
|
* the `TN_ALPHA_ANTIALIASING` marker is printed either way, so nothing is only readable here.
|
|
879
1016
|
*/
|
|
880
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;
|
|
881
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;
|
|
882
1045
|
/**
|
|
883
1046
|
* Copies one GPU storage attribute back to the CPU, asynchronously.
|
|
884
1047
|
*
|
|
@@ -932,6 +1095,31 @@ interface IRendererLike {
|
|
|
932
1095
|
readonly frame: number;
|
|
933
1096
|
readonly ms: number;
|
|
934
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;
|
|
935
1123
|
/** Starts a resolve of the GPU timestamps for the frames drawn since the last call. */
|
|
936
1124
|
resolveGpuFrame(): void;
|
|
937
1125
|
/**
|
|
@@ -1001,4 +1189,4 @@ interface IRendererOptions {
|
|
|
1001
1189
|
}>) => unknown;
|
|
1002
1190
|
}
|
|
1003
1191
|
|
|
1004
|
-
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
|
@@ -58,10 +58,12 @@ 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
60
|
/**
|
|
61
|
-
* The
|
|
61
|
+
* The scene-shape verdict the game end publishes when it was launched in dev mode.
|
|
62
62
|
*
|
|
63
|
-
* Separate from the state stream because it is not game state:
|
|
64
|
-
*
|
|
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.
|
|
65
67
|
*/
|
|
66
68
|
declare const UI_DEV_METRICS_MESSAGE = "tn:dev-metrics";
|
|
67
69
|
/**
|