@moku-labs/game 0.15.0 → 0.15.2

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.
Files changed (39) hide show
  1. package/dist/app/page.d.mts +1 -1
  2. package/dist/app/page.mjs +2 -2
  3. package/dist/app/system.d.mts +3 -3
  4. package/dist/app.d.mts +3 -3
  5. package/dist/app.mjs +1 -1
  6. package/dist/assets.d.mts +1 -1
  7. package/dist/cli.mjs +75 -36
  8. package/dist/{commands-DAETmK86.d.mts → commands-C_GccOFq.d.mts} +7 -6
  9. package/dist/control.d.mts +30 -18
  10. package/dist/control.mjs +2 -2
  11. package/dist/{define-BASd9ZUh.d.mts → define-DnO7ZLGl.d.mts} +2 -2
  12. package/dist/{dev-DbHgx-R-.mjs → dev-BNndV-hU.mjs} +4 -4
  13. package/dist/{headless-cFveciuY.d.mts → headless-D6BrEwCf.d.mts} +1 -1
  14. package/dist/{index-DedbED3T.d.mts → index-w2uyRKkj.d.mts} +16 -4
  15. package/dist/index.d.mts +5 -5
  16. package/dist/index.mjs +2 -2
  17. package/dist/inspect.d.mts +6 -6
  18. package/dist/inspect.mjs +1 -1
  19. package/dist/jsx-dev-runtime.d.mts +1 -1
  20. package/dist/jsx-runtime.d.mts +1 -1
  21. package/dist/project.d.mts +4 -4
  22. package/dist/project.mjs +1 -1
  23. package/dist/{run-BbipFRc6.mjs → run-C07NXds7.mjs} +35 -25
  24. package/dist/{runtime-DoSlRx8X.d.mts → runtime-kOH8InAs.d.mts} +1 -1
  25. package/dist/{sources-Cdvb0rOS.mjs → sources-OvHvyeJS.mjs} +1 -1
  26. package/dist/{src-BEbJ_sSx.mjs → src-Ca8W8Dvv.mjs} +5331 -1719
  27. package/dist/testing.d.mts +4 -4
  28. package/dist/testing.mjs +1 -1
  29. package/dist/{types-BGcda5Le.d.mts → types-BHgJ1F6f.d.mts} +940 -86
  30. package/dist/{types-7GXcXxxv.d.mts → types-Bm7DVtA_.d.mts} +36 -10
  31. package/dist/{types-C73hbmxi.d.mts → types-DEYQF_tT.d.mts} +4 -4
  32. package/dist/{types-GHE4J5Uf.d.mts → types-fpFKYm6E.d.mts} +142 -43
  33. package/dist/{types-DngFQJro.d.mts → types-jiWJhLPE.d.mts} +2 -2
  34. package/dist/{visual-_XhoGeIC.d.mts → visual-BvkKJOSz.d.mts} +6 -6
  35. package/dist/visual.d.mts +1 -1
  36. package/dist/visual.mjs +2 -2
  37. package/dist/{watch-BEdrp80i.mjs → watch-BSgptowj.mjs} +16 -6
  38. package/llms.txt +6 -4
  39. package/package.json +4 -2
@@ -189,11 +189,16 @@ type ThermalStatus = "none" | "light" | "moderate" | "severe" | "critical" | "em
189
189
  *
190
190
  * @example
191
191
  * ```ts
192
- * app.time.rate(); // { fps: 60, mode: "auto", reason: "missed", displayHz: 120, ladder: [120, 60], thermal: "none" }
192
+ * app.time.rate(); // { fps: 60, mode: "auto", reason: "missed", displayHz: 120, ladder: [120, 60], thermal: "none", asked: null, heldBy: null }
193
193
  * ```
194
194
  */
195
195
  type FrameRate = {
196
- /** Frames per second the loop runs at, the idle cap left out. */fps: number; /** Who decides the rate. */
196
+ /**
197
+ * Frames per second the loop really runs at, the idle cap left out: the cap on the known display. A cap above the
198
+ * display's rate runs at the display's rate (`heldBy: "panel"`); a cap between two vsyncs runs at the next divisor
199
+ * below it (60 on a 144 Hz panel runs at 48). The cap itself while the display is unknown.
200
+ */
201
+ fps: number; /** Who decides the rate. */
197
202
  mode: RateMode; /** Why the rate is what it is. */
198
203
  reason: RateReason;
199
204
  /**
@@ -204,6 +209,17 @@ type FrameRate = {
204
209
  displayHz: number | null; /** The rates the adaptive rule chooses from, highest first; empty until `displayHz` is known, and without `adaptive`. */
205
210
  ladder: readonly number[]; /** The native shell's last thermal status; `null` without a shell, before its first reading, and without `adaptive`. */
206
211
  thermal: ThermalStatus | null;
212
+ /**
213
+ * The rate the loop is told to hold: `maxFps` in mode `"fixed"`, the `setRate` value in mode `"forced"` (never above
214
+ * `maxFps`); `null` in mode `"auto"`, where the adaptive rule decides.
215
+ */
216
+ asked: Exclude<RateChoice, "auto"> | null;
217
+ /**
218
+ * `"panel"` when the asked rate is above the known `displayHz` (120 asked on a 60 Hz panel): the loop cannot run
219
+ * faster than the panel, and `fps` is the panel's rate. `null` otherwise, in mode `"auto"` and while the display is
220
+ * unknown.
221
+ */
222
+ heldBy: "panel" | null;
207
223
  };
208
224
  /**
209
225
  * Payload of `time:rate`: a change of the loop's rate or of its mode, with the evidence the rule read.
@@ -241,7 +257,12 @@ type FrameMeasure = {
241
257
  /** `Time.frame` of the measured frame. */frame: number; /** Start of the frame on the page clock (`performance` timeline), ms: the `requestAnimationFrame` timestamp, or the moment `step()` began. */
242
258
  at: number; /** `at` minus `at` of the previous measured frame; `NaN` for the first frame, the first after `resume()`, and a stepped frame. */
243
259
  gapMs: number; /** `1000 / cap` of this frame: the loop's rate, or `idleFps` while idle. */
244
- capMs: number; /** `1000 / fps` of the loop's rate in this frame, the idle cap left out: `1000 / time.rate().fps`. */
260
+ capMs: number;
261
+ /**
262
+ * One frame of the cap of the loop's rate in this frame, the idle cap left out: `1000 / time.rate().asked` in mode
263
+ * `"fixed"` and `"forced"`, the adaptive rule's rate in `"auto"`. Equal to `1000 / time.rate().fps` while the cap is
264
+ * a divisor of the display rate.
265
+ */
245
266
  rateMs: number;
246
267
  /**
247
268
  * One period of the display, `1000 / time.rate().displayHz`, ms; `NaN` until the display rate is known. No frame
@@ -610,6 +631,24 @@ type Api$7 = {
610
631
  * ```
611
632
  */
612
633
  wakes(): number;
634
+ /**
635
+ * How many `onFrame` callbacks are registered now, over the six phases. The measure listeners of `measure` are not
636
+ * counted. A number, no allocation: a leak check reads it once a second, and a count that grows each time a screen
637
+ * is entered and left is a callback nobody unsubscribed.
638
+ *
639
+ * @returns The count.
640
+ * @example
641
+ * ```ts
642
+ * // The renderer's monitor reads it once a second for the leak counts of a debug build.
643
+ * const time = ctx.require(timePlugin);
644
+ * const before = time.frameListeners();
645
+ * const off = time.onFrame("animate", frame => counter.advance(frame.delta));
646
+ * time.frameListeners() - before; // 1
647
+ * off();
648
+ * time.frameListeners() - before; // 0
649
+ * ```
650
+ */
651
+ frameListeners(): number;
613
652
  /**
614
653
  * Hands what `time` measured of every frame to `listener`, after the `render` phase: the gap, the skipped ticks, the
615
654
  * six phase durations and the whole work. Agent builds only (`__MOKU_GAME_AGENT__`, true in dev and `--debug`
@@ -659,13 +698,18 @@ type Api$7 = {
659
698
  * adaptive rule chooses from, and the native shell's thermal status. One frozen object, the same until something in
660
699
  * it changes: cheap to read every frame.
661
700
  *
701
+ * `fps` is the rate the loop really runs at; `asked` the rate it is told to hold outside the adaptive rule, and
702
+ * `heldBy: "panel"` says the panel is slower than that.
703
+ *
662
704
  * @returns The frame rate.
663
705
  * @example
664
706
  * ```ts
665
707
  * // The settings screen shows the rate the game runs at on this phone.
666
- * app.time.rate(); // { fps: 120, mode: "auto", reason: "start", displayHz: 120, ladder: [120, 60], thermal: "none" }
708
+ * app.time.rate(); // { fps: 120, mode: "auto", reason: "start", displayHz: 120, ladder: [120, 60], thermal: "none", asked: null, heldBy: null }
667
709
  * // on the memory screen of a weak phone, a second later:
668
- * app.time.rate(); // { fps: 60, mode: "auto", reason: "missed", displayHz: 120, ladder: [120, 60], thermal: "none" }
710
+ * app.time.rate(); // { fps: 60, mode: "auto", reason: "missed", displayHz: 120, ladder: [120, 60], thermal: "none", asked: null, heldBy: null }
711
+ * // a game with maxFps 120 and no adaptive, on a 60 Hz phone:
712
+ * app.time.rate(); // { fps: 60, mode: "fixed", reason: "start", displayHz: 60, ladder: [], thermal: null, asked: 120, heldBy: "panel" }
669
713
  * ```
670
714
  */
671
715
  rate(): Readonly<FrameRate>;
@@ -682,9 +726,11 @@ type Api$7 = {
682
726
  * @example
683
727
  * ```ts
684
728
  * // The player picked "60" in the settings to save battery.
685
- * app.time.setRate(60); // { fps: 60, mode: "forced", reason: "forced", displayHz: 120, ladder: [120, 60], thermal: "none" }
729
+ * app.time.setRate(60); // { fps: 60, mode: "forced", reason: "forced", displayHz: 120, ladder: [120, 60], thermal: "none", asked: 60, heldBy: null }
686
730
  * // …and later "Auto" again, on a 120 Hz phone:
687
- * app.time.setRate("auto"); // { fps: 120, mode: "auto", reason: "auto", displayHz: 120, ladder: [120, 60], thermal: "none" }
731
+ * app.time.setRate("auto"); // { fps: 120, mode: "auto", reason: "auto", displayHz: 120, ladder: [120, 60], thermal: "none", asked: null, heldBy: null }
732
+ * // A pin of 120 on a 60 Hz phone runs at the panel's rate:
733
+ * app.time.setRate(120); // { fps: 60, mode: "forced", reason: "forced", displayHz: 60, ladder: [60], thermal: "none", asked: 120, heldBy: "panel" }
688
734
  * ```
689
735
  */
690
736
  setRate(choice: RateChoice): Readonly<FrameRate>;
@@ -1736,6 +1782,40 @@ type Api$4 = {
1736
1782
  * ```
1737
1783
  */
1738
1784
  dueAt(): number | undefined;
1785
+ /**
1786
+ * How many scheduled timers are not yet due. The clock holds one pending moment, so the answer
1787
+ * is 0 or 1: a moment a screen scheduled and never cancelled reads 1 after the screen closed,
1788
+ * where the count was 0 before it opened. It allocates nothing; the renderer's monitor reads it
1789
+ * once a second in a debug build.
1790
+ *
1791
+ * @returns 1 while a moment is scheduled, else 0.
1792
+ * @example
1793
+ * ```ts
1794
+ * app.clock.pending(); // 0
1795
+ * app.clock.scheduleAt(app.clock.now() + 60_000);
1796
+ * app.clock.pending(); // 1
1797
+ * app.clock.scheduleAt(undefined);
1798
+ * app.clock.pending(); // 0
1799
+ * ```
1800
+ */
1801
+ pending(): number;
1802
+ /**
1803
+ * How many `onElapsed` listeners are registered. A screen that registers one on every visit and
1804
+ * never calls the remover adds one per visit; a screen that cleans up leaves the count where it
1805
+ * was. It allocates nothing; the renderer's monitor reads it once a second in a debug build.
1806
+ *
1807
+ * @returns The number of registered listeners.
1808
+ * @example
1809
+ * ```ts
1810
+ * // A leak check over one visit of the screen that shows the refill timer.
1811
+ * const before = app.clock.listeners();
1812
+ * const off = app.clock.onElapsed(({ now }) => refillTimer.update(now));
1813
+ * app.clock.listeners() - before; // 1
1814
+ * off(); // the screen closed
1815
+ * app.clock.listeners() - before; // 0
1816
+ * ```
1817
+ */
1818
+ listeners(): number;
1739
1819
  };
1740
1820
  /**
1741
1821
  * Domain context of the clock plugin.
@@ -4195,7 +4275,8 @@ type Cause = "edge" | "rollback" | "restore" | "load" | "mount" | "rerun";
4195
4275
  type View$1 = {
4196
4276
  entity: Entity;
4197
4277
  projection: string;
4198
- key: string;
4278
+ key: string; /** `{ projection, key }`, made once with the view: what `keyOf` answers, every call the same object. */
4279
+ readonly address: ProjectionKey;
4199
4280
  item: unknown; /** The last output of `view(item)`, by component name: what the picture equals at rest. */
4200
4281
  rest: Map<string, AnyComponentValue>;
4201
4282
  handles: MotionHandle[];
@@ -4283,6 +4364,13 @@ type Dirty = {
4283
4364
  roots: Set<Root>;
4284
4365
  force: boolean;
4285
4366
  };
4367
+ /**
4368
+ * A mute's fields joined with a type's owned fields, and the owned set they were joined with.
4369
+ */
4370
+ type JoinedFields = {
4371
+ readonly owned: ReadonlySet<string>;
4372
+ readonly joined: ReadonlySet<string>;
4373
+ };
4286
4374
  /**
4287
4375
  * projection module state.
4288
4376
  */
@@ -4306,10 +4394,28 @@ type ProjectionState = {
4306
4394
  * entity that is gone is dropped with its rest pose, and its side of `keys` with it.
4307
4395
  */
4308
4396
  keysByEntity: Map<Entity, ProjectionKey>; /** Entity to component name to the fields another writer owns. */
4309
- mutes: Map<Entity, Map<string, Set<string>>>; /** The `onConverged` listeners: the owners of elements, told after a restore or a load. */
4397
+ mutes: Map<Entity, Map<string, Set<string>>>; /** The owned fields of each component type as a set, by the type's frozen `owned` list: made once. */
4398
+ ownedSets: WeakMap<readonly string[], ReadonlySet<string>>; /** The muted and the owned fields joined, by the mute's field set: dropped when `mute()` or its release changes it. */
4399
+ joinedMutes: WeakMap<ReadonlySet<string>, JoinedFields>; /** The `onConverged` listeners: the owners of elements, told after a restore or a load. */
4310
4400
  converged: Array<() => void>;
4311
4401
  offHints: Array<() => void>; /** The cause of every view despawned since the last sweep, so a removal hook can still ask. */
4312
4402
  leaving: Map<Entity, MotionCause>;
4403
+ /**
4404
+ * Moves on every time a view enters `live`, leaves it, or leaves `leaving`, and on a clear:
4405
+ * `ui` hosts the views of a projection again only when it moved (`projection.membershipVersion()`).
4406
+ */
4407
+ membershipVersion: number;
4408
+ /**
4409
+ * A live view may hold a running handle: set by every view handle made, cleared by a sweep that
4410
+ * walked the live views and found none moving. While it is false the sweep walks no live view.
4411
+ */
4412
+ moving: boolean; /** Views were marked as owing their loop since the last start of the owed loops. */
4413
+ owedLoops: number;
4414
+ /**
4415
+ * The despawn count of the ecs when the sweep last looked for dead elements; -1 after a new
4416
+ * key, so the next sweep looks.
4417
+ */
4418
+ sweptDespawns: number;
4313
4419
  };
4314
4420
  /**
4315
4421
  * projection module API, `app.world.projection`: the bridge from the model to entities.
@@ -4463,7 +4569,8 @@ type ProjectionApi = {
4463
4569
  lift(entity: Entity, on: boolean): void;
4464
4570
  /**
4465
4571
  * The projection and key of an entity. Also answers for a view in the despawn queue and for an
4466
- * element registered with `registerKey`.
4572
+ * element registered with `registerKey`. A view answers the address it keeps for its life, the
4573
+ * same frozen object every call, so asking every frame allocates nothing.
4467
4574
  *
4468
4575
  * @param entity - The entity to ask about.
4469
4576
  * @returns The projection name and the key, or `undefined`.
@@ -4471,10 +4578,27 @@ type ProjectionApi = {
4471
4578
  * ```ts
4472
4579
  * // `renderer.sync` labels its display objects for the inspector.
4473
4580
  * app.world.projection.keyOf(1_048_576); // { projection: "board.items", key: "i7" }
4581
+ * app.world.projection.keyOf(1_048_576) === app.world.projection.keyOf(1_048_576); // true
4474
4582
  * app.world.projection.keyOf(42); // undefined: not a view
4475
4583
  * ```
4476
4584
  */
4477
4585
  keyOf(entity: Entity): ProjectionKey | undefined;
4586
+ /**
4587
+ * A number that moves every time a view enters the live views of a mounted projection, leaves
4588
+ * them (an exit or a despawn), or is forgotten after its despawn, and when the projections are
4589
+ * cleared. An equal number means the same views live as at the last read, so a plugin that
4590
+ * works on the live views (`ui` hosting them in an element) skips its step while it holds.
4591
+ * Reading it allocates nothing.
4592
+ *
4593
+ * @returns The version; 0 for a world where no view ever came or went.
4594
+ * @example
4595
+ * ```ts
4596
+ * // ui, at the start of its hosting step: nothing to do while no view came or went.
4597
+ * const version = ctx.require(worldPlugin).projection.membershipVersion(); // 7, as on the last frame
4598
+ * if (version === state.hostedMembership) return;
4599
+ * ```
4600
+ */
4601
+ membershipVersion(): number;
4478
4602
  /**
4479
4603
  * How the view of an entity came, or how it is leaving: `"mount"` when a direct reconcile or an unmount moved it,
4480
4604
  * `"change"` when a live commit did. A despawned view still answers until the next sweep, so a hook on its removal
@@ -4719,6 +4843,81 @@ type QueryPlan = {
4719
4843
  readonly names: readonly string[];
4720
4844
  readonly written: readonly string[];
4721
4845
  };
4846
+ /**
4847
+ * The walk of one system's declared query, made once when the system registers and reset by the
4848
+ * runner before every run. It allocates nothing per run: the entity list and the store list are
4849
+ * kept and overwritten by index, and every row it yields is the same array, written in place.
4850
+ * A row is valid until the next row; a system that keeps one copies it.
4851
+ */
4852
+ declare class SystemWalk implements Iterator<readonly unknown[], undefined> {
4853
+ private readonly ctx;
4854
+ /** The step handed back for every row: always the one row array. */
4855
+ private readonly step;
4856
+ /** The one row: the entity, then one stored value per name, sized at registration. */
4857
+ private readonly row;
4858
+ /** The component names of the terms, in order. */
4859
+ private readonly names;
4860
+ /** The names of the `mut()` terms, marked for every yielded row. */
4861
+ private readonly written;
4862
+ /** The store of each name, read once per run at the first step; `undefined` for a name no entity carried yet. */
4863
+ private readonly stores;
4864
+ /** The matching entities of this run, from index 0 to `count`; kept and overwritten by index. */
4865
+ private readonly entities;
4866
+ /** How many entities this run collected. */
4867
+ private count;
4868
+ /** The next entity to visit. */
4869
+ private index;
4870
+ /** Whether this run took its entities yet. */
4871
+ private started;
4872
+ /** The first store's size at the first step: a dev build compares it; -1 when not watched. */
4873
+ private size;
4874
+ /**
4875
+ * Prepares the walk of one system.
4876
+ *
4877
+ * @param ctx - Domain context of the world plugin.
4878
+ * @param plan - The system's resolved query.
4879
+ */
4880
+ constructor(ctx: WorldCtx, plan: QueryPlan);
4881
+ /**
4882
+ * The walk is its own iterator, so the runner hands it to `run` as the rows. A second loop over
4883
+ * it in the same run finds it walked.
4884
+ *
4885
+ * @returns This walk.
4886
+ */
4887
+ [Symbol.iterator](): SystemWalk;
4888
+ /**
4889
+ * Starts a new run: nothing is read before the first step.
4890
+ *
4891
+ * @returns This walk, the rows of the run.
4892
+ */
4893
+ reset(): SystemWalk;
4894
+ /**
4895
+ * Keeps one key of the first store when the entity carries every other name.
4896
+ *
4897
+ * @param entity - The key.
4898
+ */
4899
+ keep(entity: Entity): void;
4900
+ /**
4901
+ * Hands out the next row: the next collected entity, its values written into the one row, and
4902
+ * marked for every `mut()` name.
4903
+ *
4904
+ * @returns The step of the row, or the end.
4905
+ */
4906
+ next(): IteratorResult<readonly unknown[], undefined>;
4907
+ /**
4908
+ * Reads the stores of this run and collects the entities that carry every name, in the first
4909
+ * store's order. Structural changes are queued until the phase ends, so the matches stay true
4910
+ * while the rows are consumed.
4911
+ */
4912
+ private start;
4913
+ /**
4914
+ * Writes the row of one entity in place: the entity, then the value of every name.
4915
+ *
4916
+ * @param entity - The entity.
4917
+ * @returns False when the entity no longer carries a name, and the row is not handed out.
4918
+ */
4919
+ private fill;
4920
+ }
4722
4921
  //#endregion
4723
4922
  //#region src/plugins/world/ecs/types.d.ts
4724
4923
  /**
@@ -5035,6 +5234,12 @@ type SystemDefinition<Terms extends readonly QueryTerm[]> = {
5035
5234
  readonly name: string;
5036
5235
  readonly phase: WorldPhase;
5037
5236
  readonly query: Terms;
5237
+ /**
5238
+ * Runs the system once a frame in its phase. `entities` walks the declared `query` once; every
5239
+ * row it hands out is the same array, rewritten for the next row, so a row is valid until the
5240
+ * next row. A system that keeps a row copies what it keeps: `const [entity, transform] = row`.
5241
+ * `ecs.query()` called inside `run` hands out a new array per row.
5242
+ */
5038
5243
  run(entities: Iterable<QueryTuple<Terms>>, ctx: SystemContext): void;
5039
5244
  };
5040
5245
  /**
@@ -5088,23 +5293,23 @@ type SystemFor<Player, Session> = <const Terms extends readonly QueryTerm[]>(def
5088
5293
  type AnySystem = {
5089
5294
  readonly name: string;
5090
5295
  readonly phase: WorldPhase;
5091
- readonly query: readonly QueryTerm[];
5296
+ readonly query: readonly QueryTerm[]; /** As `SystemDefinition.run`: one row array per walk, valid until the next row. */
5092
5297
  run(entities: Iterable<readonly unknown[]>, ctx: SystemContext): void;
5093
5298
  };
5094
5299
  /**
5095
- * One registered system, the frame it was registered in and its query resolved once. A system
5096
- * registered during a frame runs from the next frame on. Spec 28 (deferred) extends it with the
5097
- * system's scope.
5098
- *
5099
- * @example
5100
- * ```ts
5101
- * const entry: SystemEntry = { definition: bobSystem, frame: 12, plan: { names: ["Transform"], written: ["Transform"] } };
5102
- * ```
5300
+ * One registered system, the frame it was registered in, its query resolved once and the walk the
5301
+ * runner resets for every run. A system registered during a frame runs from the next frame on.
5302
+ * Spec 28 (deferred) extends it with the system's scope.
5103
5303
  */
5104
5304
  type SystemEntry = {
5105
5305
  readonly definition: AnySystem;
5106
5306
  readonly frame: number;
5107
5307
  readonly plan: QueryPlan;
5308
+ /**
5309
+ * The walk of the declared query, made at registration; `undefined` for an empty query, which
5310
+ * the runner never walks. An entry built without one gets it on its first run.
5311
+ */
5312
+ walk?: SystemWalk | undefined;
5108
5313
  };
5109
5314
  /**
5110
5315
  * A structural change queued while a phase runs. Applied in call order when the phase ends.
@@ -5143,17 +5348,23 @@ type Command = {
5143
5348
  */
5144
5349
  type StructuralHook = (entity: Entity, value: unknown) => void;
5145
5350
  /**
5146
- * The frozen model snapshot of one frame, kept so four phases read it once.
5147
- *
5148
- * @example
5149
- * ```ts
5150
- * const cached: FrameSnapshot = { frame: 12, snapshot: { player: {}, session: {}, rng: rngState } };
5151
- * ```
5351
+ * The frozen model snapshot of one frame, kept so four phases read it once. One object per world,
5352
+ * rewritten when a new frame reads its snapshot.
5152
5353
  */
5153
5354
  type FrameSnapshot = {
5154
5355
  frame: number;
5155
5356
  snapshot: Snapshot;
5156
5357
  };
5358
+ /**
5359
+ * The context the runner hands every system of one world: one object, made at the first phase
5360
+ * run, whose `time` and `snapshot` are rewritten for every phase.
5361
+ */
5362
+ type PhaseContext = {
5363
+ /** The ecs API the systems of this world receive. */readonly world: EcsApi; /** The resource reader, one function per world. */
5364
+ readonly res: <Value extends object>(resource: ResourceType<Value>) => Value; /** The frozen model snapshot of the running frame. */
5365
+ snapshot: Snapshot; /** The running frame's `Time`. */
5366
+ time: Readonly<Time>;
5367
+ };
5157
5368
  /**
5158
5369
  * The JSON kind of one component field, read from its default value: what the editor's component
5159
5370
  * palette shows next to the field name. A nested shape is not described; `defaults` carries it.
@@ -5300,6 +5511,24 @@ type MarkCount = {
5300
5511
  marked: number;
5301
5512
  changed: number;
5302
5513
  };
5514
+ /**
5515
+ * The entities marked changed for one component in the current frame, each once, in mark order.
5516
+ * Both collections are kept from frame to frame, so a frame's marks allocate nothing once they
5517
+ * have grown to its size.
5518
+ */
5519
+ type ChangeList = {
5520
+ /** The marked entities of this frame, in mark order: what `changed()` hands out. */readonly entities: Entity[]; /** Entity to the frame stamp of its last mark; a stamp of the current frame is a repeat. */
5521
+ readonly frames: Map<Entity, number>;
5522
+ };
5523
+ /**
5524
+ * A plan `ecs.query()` remembers: the plan, and the type objects and `mut()` places it was made
5525
+ * for, compared by identity at every call.
5526
+ */
5527
+ type KnownPlan = {
5528
+ readonly plan: QueryPlan;
5529
+ readonly types: readonly AnyComponentType[];
5530
+ readonly written: readonly boolean[];
5531
+ };
5303
5532
  /**
5304
5533
  * ecs module state.
5305
5534
  */
@@ -5309,8 +5538,10 @@ type EcsState = {
5309
5538
  owners: Map<Entity, Owner>; /** Key is `"kind:name"` of the owner. */
5310
5539
  byOwner: Map<string, Set<Entity>>;
5311
5540
  types: Map<string, AnyComponentType>;
5312
- stores: Map<string, Map<Entity, object | true>>;
5313
- resources: Map<string, object>;
5541
+ stores: Map<string, Map<Entity, object | true>>; /** The store of each type object that was read, written or asked through, once it has one. */
5542
+ typeStores: Map<AnyComponentType, Map<Entity, object | true>>;
5543
+ resources: Map<string, object>; /** The value of each resource type object that was read, so a read is one lookup. */
5544
+ resourceSlots: Map<ResourceType<object>, object>;
5314
5545
  /**
5315
5546
  * The type object stored under each engine resource name (`"Now"`, `"Pointer"`, …), so a game
5316
5547
  * resource of the same name is refused instead of sharing the engine's value.
@@ -5318,18 +5549,23 @@ type EcsState = {
5318
5549
  engineResources: Map<string, object>;
5319
5550
  systems: Record<WorldPhase, SystemEntry[]>;
5320
5551
  running: WorldPhase | undefined;
5321
- commands: Command[];
5322
- changed: Map<string, Set<Entity>>;
5552
+ commands: Command[]; /** The change list of each component marked since the world began, emptied at every `signals`. */
5553
+ changed: Map<string, ChangeList>; /** The stamp of the current frame's marks, moved on by every clear of the change lists. */
5554
+ changeFrame: number;
5323
5555
  added: Map<string, StructuralHook[]>;
5324
5556
  removed: Map<string, StructuralHook[]>;
5325
5557
  ownerLeft: Array<(owner: Owner) => void>;
5326
5558
  mode: WorldMode;
5327
5559
  offFrame: Array<() => void>;
5328
- frameSnapshot: FrameSnapshot | undefined; /** The frame history: dev builds only, from the first frame on; `undefined` before it. */
5560
+ frameSnapshot: FrameSnapshot | undefined; /** The context of every system run of this world; `undefined` before the first phase ran. */
5561
+ phaseContext: PhaseContext | undefined; /** The frame history: dev builds only, from the first frame on; `undefined` before it. */
5329
5562
  history: History | undefined; /** Time per system of an agent build (H2b); `undefined` without the flag and before start. */
5330
5563
  timing: SystemTiming | undefined; /** The row of each timed system in `timing`, by name: a system that comes back keeps its row. */
5331
5564
  timingSlots: Map<string, number>; /** A dev build reported a store whose keys changed while a phase walked it: reported once. */
5332
- walkReported: boolean;
5565
+ walkReported: boolean; /** The plans of `ecs.query()` by the type of their first term, made once per tuple of types. */
5566
+ queryPlans: Map<AnyComponentType, KnownPlan[]>; /** The entity lists of ended `ecs.query()` walks, taken by the next walks: no key copy per call. */
5567
+ walkLists: Entity[][]; /** How many entities were despawned since the world began: the sweep looks for dead elements when it moved. */
5568
+ despawns: number;
5333
5569
  };
5334
5570
  /**
5335
5571
  * ecs module API, `app.world.ecs`: the screen as data. Plain-object components in one store per
@@ -5510,8 +5746,10 @@ type EcsApi = {
5510
5746
  * Walks the store of the FIRST term in insertion order and keeps the entities that carry every
5511
5747
  * term. Deterministic. A `mut()` term marks every yielded entity changed for that component
5512
5748
  * (README, "Marking what changed"). Inside a system the walk reads the store live (changes are
5513
- * queued until the phase ends); outside one it walks the keys taken at the start. Every row is a
5514
- * new array: keep it if you like. Put the rarest component first: the walk visits its store.
5749
+ * queued until the phase ends); outside one it walks the keys taken at the start. Every row of
5750
+ * `ecs.query()` is a new array: keep it if you like. The rows a system's `run` gets for its
5751
+ * declared `query` are not: they share one array, valid until the next row. Put the rarest
5752
+ * component first: the walk visits its store.
5515
5753
  *
5516
5754
  * @param terms - Component types, tag types and `mut()` terms.
5517
5755
  * @returns The matching rows: the entity and one value per term.
@@ -5616,7 +5854,12 @@ type EcsApi = {
5616
5854
  onRemoved(tagType: TagType, fn: (entity: Entity) => void): () => void;
5617
5855
  /**
5618
5856
  * The coarse change set of the frame, cleared in `time` phase `signals` after every `sync`
5619
- * system ran.
5857
+ * system ran. It is the world's own list, in mark order, each entity once: walk it in the frame
5858
+ * and copy what you keep, because the clear empties it in place. A component nothing marked
5859
+ * answers one shared empty list. Asking allocates nothing. Inside a system nothing leaves the
5860
+ * list until the phase ends. Outside a phase a despawn, or a removal of the component, takes the
5861
+ * entity out at once and the entities after it move up, so a loop that despawns or removes
5862
+ * outside a phase walks a copy: `const copy = [...ecs.changed(C)]`.
5620
5863
  *
5621
5864
  * @param component - The component type to ask about.
5622
5865
  * @returns The entities marked changed for it this frame.
@@ -5689,6 +5932,24 @@ type EcsApi = {
5689
5932
  * ```
5690
5933
  */
5691
5934
  ownerOf(entity: Entity): Owner | undefined;
5935
+ /**
5936
+ * How many entities live now: every id `spawn` handed out that no despawn took back, the views
5937
+ * of the projections and the layers included. It reads a size the world keeps, so it allocates
5938
+ * nothing and a monitor may call it every second. A screen entered and left returns the count to
5939
+ * where it was; a count that grows with every visit is a leak.
5940
+ *
5941
+ * @returns The number of live entities.
5942
+ * @example
5943
+ * ```ts
5944
+ * // A test that spawns an entity sees it counted until the despawn.
5945
+ * const before = app.world.ecs.count();
5946
+ * const coin = app.world.ecs.spawn({ kind: "plugin", name: "test" }, [Transform({ x: 10 })]);
5947
+ * app.world.ecs.count(); // before + 1
5948
+ * app.world.ecs.despawn(coin);
5949
+ * app.world.ecs.count(); // before
5950
+ * ```
5951
+ */
5952
+ count(): number;
5692
5953
  /**
5693
5954
  * The whole world as plain JSON, sorted by entity index. A component value that is not JSON is
5694
5955
  * left out and its name is listed in `skipped`.
@@ -5781,6 +6042,8 @@ type EcsInternal = {
5781
6042
  //#region src/plugins/world/ecs/systems.d.ts
5782
6043
  /**
5783
6044
  * Time each world system took in the current frame of an agent build: names by registration index, ms in the same order.
6045
+ * `names` is a new array each time a system of a new name registers and the same array otherwise, so a reader that
6046
+ * kept it sees growth by identity.
5784
6047
  *
5785
6048
  * @example
5786
6049
  * ```ts
@@ -5789,7 +6052,7 @@ type EcsInternal = {
5789
6052
  */
5790
6053
  type SystemTiming = {
5791
6054
  readonly clock: () => number;
5792
- readonly names: string[];
6055
+ names: readonly string[];
5793
6056
  ms: Float64Array;
5794
6057
  };
5795
6058
  declare namespace types_d_exports$3 {
@@ -5858,7 +6121,8 @@ type Api$2 = {
5858
6121
  *
5859
6122
  * A live view, not a copy: the table is the world's own, the same object every call, and the next frame zeroes and
5860
6123
  * rewrites `ms` in place. The monitor reads it every frame of an agent build without allocating, which a copy would
5861
- * break. A caller that keeps numbers past the frame copies them (`[...systems.ms]`); a write into it changes what the
6124
+ * break. `names` is replaced by a new array when a system of a new name registers, and only then: a reader that kept
6125
+ * the array tells a new system by `names !== kept`. A caller that keeps numbers past the frame copies them (`[...systems.ms]`); a write into it changes what the
5862
6126
  * monitor logs.
5863
6127
  *
5864
6128
  * @returns The live timing table, or `undefined`.
@@ -5957,6 +6221,52 @@ type ProjectionModule = ProjectionApi & ProjectionInternal;
5957
6221
  */
5958
6222
  declare const DEMAND_REASONS: readonly ["always", "first", "resize", "restore", "layers", "debug", "wake", "capture", "request", "mark", "verify", "none", "overlay", "retake"];
5959
6223
  //#endregion
6224
+ //#region src/plugins/renderer/demand/marks.d.ts
6225
+ /**
6226
+ * The marks of one kind of the decision running now, in mark order, each entity once.
6227
+ */
6228
+ declare class MarkList implements Marks {
6229
+ /** Entities marked in this decision. */
6230
+ size: number;
6231
+ /** The marked entities: the first `size` of them; the rest is left from earlier decisions. */
6232
+ private readonly entities;
6233
+ /** The decision each entity was last marked in. */
6234
+ private readonly stamps;
6235
+ /** The number of this decision. */
6236
+ private stamp;
6237
+ /**
6238
+ * Tells whether an entity is marked in this decision.
6239
+ *
6240
+ * @param entity - The entity.
6241
+ * @returns True when it is.
6242
+ */
6243
+ has(entity: Entity): boolean;
6244
+ /**
6245
+ * The entity marked `index`-th in this decision.
6246
+ *
6247
+ * @param index - 0 to `size - 1`.
6248
+ * @returns The entity; 0 past the end.
6249
+ */
6250
+ at(index: number): Entity;
6251
+ /**
6252
+ * Marks an entity; a second mark of the same decision changes nothing.
6253
+ *
6254
+ * @param entity - The entity.
6255
+ */
6256
+ add(entity: Entity): void;
6257
+ /**
6258
+ * Empties the list: the next decision gets a new number, so every stamp is stale. Allocates nothing, except when the
6259
+ * stamps grew past `STAMPS_KEPT` or the numbers ran out: then they are dropped.
6260
+ */
6261
+ clear(): void;
6262
+ /**
6263
+ * Walks the marks of this decision in mark order. Allocates an iterator: for a test or a spread, never per frame.
6264
+ *
6265
+ * @yields {Entity} Each marked entity.
6266
+ */
6267
+ [Symbol.iterator](): Iterator<Entity>;
6268
+ }
6269
+ //#endregion
5960
6270
  //#region src/plugins/renderer/demand/types.d.ts
5961
6271
  /**
5962
6272
  * Why the render phase drew a frame, or `"none"` when it did not. Internal: `monitor` and the agent log read it.
@@ -5969,6 +6279,28 @@ type DemandReason = (typeof DEMAND_REASONS)[number];
5969
6279
  * out of the tree, no view). Internal: `sync.loopsHeld` reads it.
5970
6280
  */
5971
6281
  type Unseen = "shown" | "alpha-above" | "alpha-own" | "off-screen" | "other";
6282
+ /**
6283
+ * The change marks of one kind of the running decision, read: each entity once, in mark order. What a `Set` offered
6284
+ * its readers, kept in a list that empties without allocating (`MarkList`). Internal.
6285
+ */
6286
+ type Marks = {
6287
+ /** Entities marked in this decision. */readonly size: number;
6288
+ /**
6289
+ * Tells whether an entity is marked in this decision.
6290
+ *
6291
+ * @param entity - The entity.
6292
+ * @returns True when it is.
6293
+ */
6294
+ has(entity: Entity): boolean;
6295
+ /**
6296
+ * The entity marked `index`-th in this decision: a walk by index makes no iterator.
6297
+ *
6298
+ * @param index - 0 to `size - 1`.
6299
+ * @returns The entity; 0 past the end.
6300
+ */
6301
+ at(index: number): Entity; /** Walks the marks, allocating an iterator: for a test or a spread, never per frame. */
6302
+ [Symbol.iterator](): Iterator<Entity>;
6303
+ };
5972
6304
  /**
5973
6305
  * The last decision of the render phase, one object reused every frame.
5974
6306
  */
@@ -5981,12 +6313,12 @@ type DemandFrame = {
5981
6313
  * demand module state, `ctx.state.demand`.
5982
6314
  */
5983
6315
  type DemandState = {
5984
- /** Entities marked by a compared or structural write since the last decision. */marked: Set<Entity>; /** Marked entities whose view was not drawn at mark time; counted at the decision when drawn then. */
5985
- pending: Set<Entity>; /** Entities a display adapter reported after its `update` (not `false`): counted unless an ancestor is hidden. */
5986
- loose: Set<Entity>; /** Marked by a write that changes what the view draws (`mark`): the kept cache retakes its picture. */
5987
- visual: Set<Entity>; /** Marked by a write of the view's own transform or depth (`markMoved`): a kept picture only moves. */
5988
- moved: Set<Entity>; /** Marked through the public `invalidate`: a filter value or a self-drawn object about to change. */
5989
- published: Set<Entity>;
6316
+ /** Entities marked by a compared or structural write since the last decision. */marked: MarkList; /** Marked entities whose view was not drawn at mark time; counted at the decision when drawn then. */
6317
+ pending: MarkList; /** Entities a display adapter reported after its `update` (not `false`): counted unless an ancestor is hidden. */
6318
+ loose: MarkList; /** Marked by a write that changes what the view draws (`mark`): the kept cache retakes its picture. */
6319
+ visual: MarkList; /** Marked by a write of the view's own transform or depth (`markMoved`): a kept picture only moves. */
6320
+ moved: MarkList; /** Marked through the public `invalidate`: a filter value or a self-drawn object about to change. */
6321
+ published: MarkList;
5990
6322
  /**
5991
6323
  * Every whole-frame reason found since the last decision (O17, LEAD-FIXES-3). The first one added is
5992
6324
  * `frame().reason`, as spec 14 says; the set keeps the others visible to the overlay step and `kept.collect`.
@@ -6027,11 +6359,11 @@ type DemandInternal = {
6027
6359
  frame(): Readonly<DemandFrame>; /** The F6/F18a predicate: `unseenBy(entity) === "shown"`. */
6028
6360
  shown(entity: Entity): boolean; /** Where the walk of `shown` stopped for the entity's view, for `sync.loopsHeld` (spec 26 R1.7.3). */
6029
6361
  unseenBy(entity: Entity): Unseen; /** Marks of the current decision. */
6030
- marked(): ReadonlySet<Entity>; /** The adapter marks of the current decision, as `marked()` for the others. */
6031
- loose(): ReadonlySet<Entity>; /** The marks of the current decision made by `mark`: what a view draws changed. */
6032
- visual(): ReadonlySet<Entity>; /** The marks of the current decision made by `markMoved`: a view's own transform or depth changed. */
6033
- moved(): ReadonlySet<Entity>; /** The marks of the current decision made through the public `invalidate`. */
6034
- published(): ReadonlySet<Entity>; /** The pixel oracle switch, behind `DebugApi.verifyFrames`. */
6362
+ marked(): Marks; /** The adapter marks of the current decision, as `marked()` for the others. */
6363
+ loose(): Marks; /** The marks of the current decision made by `mark`: what a view draws changed. */
6364
+ visual(): Marks; /** The marks of the current decision made by `markMoved`: a view's own transform or depth changed. */
6365
+ moved(): Marks; /** The marks of the current decision made through the public `invalidate`. */
6366
+ published(): Marks; /** The pixel oracle switch, behind `DebugApi.verifyFrames`. */
6035
6367
  verifyFrames(on: boolean): void; /** Whether the pixel oracle is on. */
6036
6368
  verifying(): boolean; /** The body of the public `renderer.invalidate`: a mark of an entity that has a view. */
6037
6369
  invalidate(entity: Entity): void;
@@ -6373,7 +6705,12 @@ type KeptInternal = {
6373
6705
  stats(): KeptStats; /** The takes since the last call; the monitor's window close. */
6374
6706
  drainTakes(): number; /** The stage-3-only redos since the last call. */
6375
6707
  drainRefreshes(): number; /** Dev and agent builds only: every record and range drawn live for one extract, and back. */
6376
- bypass(on: boolean): void; /** The debug switch: false draws every view live from the next drawn frame. */
6708
+ bypass(on: boolean): void;
6709
+ /**
6710
+ * Dev and agent builds only: runs `draw` with every kept record drawn live and every range's entries lifted (its
6711
+ * sprite hidden), then puts every picture back: the live render of the pixel leg and of the overdraw map.
6712
+ */
6713
+ live<Result>(draw: () => Result): Result; /** The debug switch: false draws every view live from the next drawn frame. */
6377
6714
  enabled(on: boolean): void; /** Whether the switch is on (and `keptMb` above 0). */
6378
6715
  isOn(): boolean; /** Every record back to live, every texture destroyed: device loss, rebuild, stop. */
6379
6716
  reset(): void;
@@ -6406,7 +6743,12 @@ type KeptRecord = {
6406
6743
  readonly matrix: PixiMatrix; /** Drawn frames without a stage-1 change (live), stage-1 retakes in a row (kept), and whether calm is needed. */
6407
6744
  still: number;
6408
6745
  churn: number;
6409
- calm: boolean; /** Drawn-frame number of the last frame its quad was drawn; the LRU key of P6. */
6746
+ calm: boolean;
6747
+ /**
6748
+ * Drawn frames in a row its subtree did not change (no stage-1 mark), whether it may be kept or not: the monitor's
6749
+ * `filter-still` rule reads it.
6750
+ */
6751
+ quiet: number; /** Drawn-frame number of the last frame its quad was drawn; the LRU key of P6. */
6410
6752
  lastDrawn: number; /** Depth of the top under the stage, for the inner-first order (P5.2). */
6411
6753
  depth: number; /** Bytes of s1, s2, s3 (estimate, P6.1). */
6412
6754
  bytes: number; /** A settle retake is owed: the view came to rest off its picture's grid (P2.5). */
@@ -7203,17 +7545,19 @@ type FiltersApi = {
7203
7545
  warm(filter: PixiFilter): Promise<boolean>;
7204
7546
  };
7205
7547
  /**
7206
- * The debug switches of the renderer: the nine-slice outline, the pixel check of skipped frames and the kept pictures.
7548
+ * The debug switches of the renderer: the nine-slice outline, the pixel check of skipped frames, the kept pictures and
7549
+ * the overdraw heat map.
7207
7550
  *
7208
7551
  * @example
7209
7552
  * ```ts
7210
- * const switches: DebugSwitches = { nineSlice: true, verifyFrames: false, kept: true };
7553
+ * const switches: DebugSwitches = { nineSlice: true, verifyFrames: false, kept: true, overdraw: false };
7211
7554
  * ```
7212
7555
  */
7213
7556
  type DebugSwitches = {
7214
7557
  nineSlice: boolean;
7215
7558
  verifyFrames: boolean;
7216
7559
  kept: boolean;
7560
+ overdraw: boolean;
7217
7561
  };
7218
7562
  /**
7219
7563
  * The debug drawing of the renderer, `app.renderer.sync.debug`. The switch outlines every
@@ -7224,7 +7568,7 @@ type DebugSwitches = {
7224
7568
  * ```ts
7225
7569
  * // Check where every panel of the open popup is cut: cyan lines, red where corners overlap.
7226
7570
  * app.renderer.sync.debug.nineSlice(true);
7227
- * app.renderer.sync.debug.state(); // { nineSlice: true, verifyFrames: false, kept: true }
7571
+ * app.renderer.sync.debug.state(); // { nineSlice: true, verifyFrames: false, kept: true, overdraw: false }
7228
7572
  * ```
7229
7573
  */
7230
7574
  type DebugApi = {
@@ -7257,7 +7601,7 @@ type DebugApi = {
7257
7601
  * ```ts
7258
7602
  * // The visual runner checks every stepped frame of a test in the browser.
7259
7603
  * app.renderer.sync.debug.verifyFrames(true);
7260
- * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: true, kept: true }
7604
+ * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: true, kept: true, overdraw: false }
7261
7605
  * ```
7262
7606
  */
7263
7607
  verifyFrames(on: boolean): void;
@@ -7271,10 +7615,29 @@ type DebugApi = {
7271
7615
  * ```ts
7272
7616
  * // The editor's tools page compares the memory screen with and without kept pictures.
7273
7617
  * app.renderer.sync.debug.kept(false);
7274
- * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: false, kept: false }
7618
+ * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: false, kept: false, overdraw: false }
7275
7619
  * ```
7276
7620
  */
7277
7621
  kept(on: boolean): void;
7622
+ /**
7623
+ * Draws every frame as the overdraw heat map (true) or as the game (false): a pixel's brightness counts the layers
7624
+ * drawn there, 1/16 white for one, white for sixteen and more. While it is on every frame is drawn (also with
7625
+ * `onDemand`), kept pictures are drawn live and filters are off, so the map shows what a frame would draw without
7626
+ * them; the frames of the performance log are marked (`debug` event, header `debug.overdraw`), so a recording is not
7627
+ * read as real cost. Only the stage pass changes: between frames every view keeps its own alpha, blend mode and
7628
+ * filters, and off draws the game again from the next frame. A texel a texture leaves transparent counts nothing.
7629
+ * Dev and debug builds only; elsewhere the switch stays off.
7630
+ *
7631
+ * @param on - True to draw the heat map.
7632
+ * @example
7633
+ * ```ts
7634
+ * // The editor's Mission control shows where the board stacks its layers: three full-screen backdrops show 3/16 white.
7635
+ * app.renderer.sync.debug.overdraw(true);
7636
+ * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: false, kept: true, overdraw: true }
7637
+ * app.renderer.sync.debug.overdraw(false); // the next frame draws the board as before
7638
+ * ```
7639
+ */
7640
+ overdraw(on: boolean): void;
7278
7641
  /**
7279
7642
  * What every overlay does this frame, bottom first: its phase, its mode, its level, how many entries its range has
7280
7643
  * and whether a picture holds them. The level and the landing are the engine's state of the overlay, read here. For
@@ -7296,7 +7659,7 @@ type DebugApi = {
7296
7659
  * @example
7297
7660
  * ```ts
7298
7661
  * // An e2e test makes sure no outline is drawn before it compares a screenshot.
7299
- * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: false, kept: true }
7662
+ * app.renderer.sync.debug.state(); // { nineSlice: false, verifyFrames: false, kept: true, overdraw: false }
7300
7663
  * ```
7301
7664
  */
7302
7665
  state(): DebugSwitches;
@@ -7419,7 +7782,7 @@ type SyncApi = {
7419
7782
  * ```ts
7420
7783
  * // Check where every panel of the open popup is cut: cyan lines, red where corners overlap.
7421
7784
  * app.renderer.sync.debug.nineSlice(true);
7422
- * app.renderer.sync.debug.state(); // { nineSlice: true, verifyFrames: false, kept: true }
7785
+ * app.renderer.sync.debug.state(); // { nineSlice: true, verifyFrames: false, kept: true, overdraw: false }
7423
7786
  * ```
7424
7787
  */
7425
7788
  debug: DebugApi;
@@ -7565,6 +7928,27 @@ type SyncInternal = {
7565
7928
  * @param fn - Called with the probe draws made since the queue last started.
7566
7929
  */
7567
7930
  onWarm(fn: WarmListener): void;
7931
+ /**
7932
+ * Whether the overdraw heat map draws: the frame then asks to be drawn, and the stage pass goes through
7933
+ * `drawOverdraw`.
7934
+ *
7935
+ * @returns True while `debug.overdraw` is on.
7936
+ */
7937
+ overdrawing(): boolean;
7938
+ /**
7939
+ * Draws the stage pass as the overdraw heat map and puts every node back after it (`overdraw.ts`).
7940
+ *
7941
+ * @param render - The stage pass, `host.render`.
7942
+ * @returns What `render` answered.
7943
+ */
7944
+ drawOverdraw(render: () => boolean): boolean;
7945
+ /**
7946
+ * Subscribes to a move of the overdraw switch (the monitor of an agent build writes the `debug` event line).
7947
+ * Emptied by `stopSync`.
7948
+ *
7949
+ * @param fn - Called with the switch, when it moved.
7950
+ */
7951
+ onOverdraw(fn: (on: boolean) => void): void;
7568
7952
  /**
7569
7953
  * Gives the view of an entity its wrapper before the kept cache takes its first picture (spec 23 D4), so the
7570
7954
  * picture's root is a plain container; marks nothing, since the wrapper draws the same pixels. The wrapper stays
@@ -7810,10 +8194,16 @@ type SyncState = {
7810
8194
  * parent's view (spec 16 U2.4).
7811
8195
  */
7812
8196
  orphans: Set<Entity>; /** Entities the tree check of a dev or agent build already reported, so each is reported once (spec 16 U2.5). */
7813
- mismatched: Set<Entity>; /** The nine-slice switch, from `config.debug` and `debug.nineSlice`; the pixel check lives in `demand`. */
8197
+ mismatched: Set<Entity>;
8198
+ /**
8199
+ * The nine-slice and overdraw switches, from `config.debug` and `debug.nineSlice` / `debug.overdraw`; the pixel check
8200
+ * lives in `demand`, the kept switch in `kept`.
8201
+ */
7814
8202
  debug: {
7815
8203
  nineSlice: boolean;
7816
- }; /** The nine-slice switch moved: the next pass writes every nine-slice again. */
8204
+ overdraw: boolean;
8205
+ }; /** The restore table of the overdraw stage pass, its matrix and the listeners of the switch. */
8206
+ overdraw: OverdrawState; /** The nine-slice switch moved: the next pass writes every nine-slice again. */
7817
8207
  outlinesStale: boolean; /** The filters `effects` set per entity, kept while the entity lives, view or not. */
7818
8208
  filters: Map<Entity, readonly FilterSlot[]>; /** The filter area `effects` set per entity, kept like its filters (B4). */
7819
8209
  areas: Map<Entity, AreaEntry>;
@@ -7828,7 +8218,12 @@ type SyncState = {
7828
8218
  * The kind of each Pixi filter a slot named, for the performance log of an agent build; a
7829
8219
  * blur's two passes are filters of their own and are in it too. Empty in a production build.
7830
8220
  */
7831
- filterKinds: WeakMap<object, string>; /** Hidden masks of engine objects that are no view (an overlay's dim, a kept quad). */
8221
+ filterKinds: WeakMap<object, string>;
8222
+ /**
8223
+ * The entity each Pixi filter a slot hangs on (its stages and a blur's pass filters too), for the passes of the
8224
+ * performance log; the slot set last wins. Empty in a production build.
8225
+ */
8226
+ filterOwners: WeakMap<object, Entity>; /** Hidden masks of engine objects that are no view (an overlay's dim, a kept quad). */
7832
8227
  objectHidden: WeakMap<PixiContainer, number>; /** The view whose top (`wrapper ?? object`) a node is, for the hidden masks (O15; skeleton choice, confirmed in LEAD-FIXES-3). */
7833
8228
  viewOfTop: WeakMap<PixiContainer, View>; /** The overlays, their ranges, dims and HUD parts (spec 22). */
7834
8229
  overlay: OverlayState; /** `Graphics` paths drawn since the monitor last took them: shapes, clip masks, outlines (spec 26 R2.3). */
@@ -7838,8 +8233,42 @@ type SyncState = {
7838
8233
  * an entity with several endless loops is walked once a frame. Cleared by every pass, which may change the tree.
7839
8234
  */
7840
8235
  held: Map<Entity, Unseen>;
8236
+ /**
8237
+ * Moves on with every change that may change the render passes of a frame: a filter set or forgotten, a node's
8238
+ * `renderable` flipped, a view hung, wrapped or dropped (a pass that did work), a kept picture taken or let go, a
8239
+ * write a plugin announced with `invalidate` (a filter switched on or off).
8240
+ */
8241
+ passesVersion: number; /** The filtered views' passes of `renderPassesOf`, counted at `passesVersion` `at` under `root`. */
8242
+ passes: PassCount; /** The name of the scene's HUD layer (`above: "overlays"`), kept with the layers; `undefined` without one. */
8243
+ hudLayer: string | undefined;
7841
8244
  cleanups: Array<() => void>;
7842
8245
  };
8246
+ /**
8247
+ * The overdraw heat map's state: the restore table of one stage pass (a node, its alpha, its blend mode, its filters
8248
+ * or `undefined` for none), the root's own filters, the matrix made once, and who hears a move of the switch.
8249
+ */
8250
+ type OverdrawState = {
8251
+ nodes: PixiContainer[];
8252
+ alphas: number[];
8253
+ blends: Array<PixiContainer["blendMode"]>;
8254
+ filters: Array<readonly PixiFilter[] | undefined>;
8255
+ rootFilters: readonly PixiFilter[] | undefined;
8256
+ matrix: PixiFilter | undefined;
8257
+ listeners: Array<(on: boolean) => void>;
8258
+ };
8259
+ /**
8260
+ * The filtered views' part of the render passes, counted once and read every frame while nothing it reads changed: the
8261
+ * version and the root it was counted at, and every filter of the filter table with its `enabled` then (a plugin may
8262
+ * switch a filter by its field alone). Not public.
8263
+ */
8264
+ type PassCount = {
8265
+ at: number;
8266
+ root: PixiContainer | undefined;
8267
+ filtered: number; /** Filters recorded: the first `count` of `filters` and `enabled`. */
8268
+ count: number;
8269
+ filters: Array<PixiFilter | undefined>;
8270
+ enabled: boolean[];
8271
+ };
7843
8272
  //#endregion
7844
8273
  //#region src/plugins/renderer/monitor/log-types.d.ts
7845
8274
  /**
@@ -7887,7 +8316,9 @@ type FrameColumns = {
7887
8316
  fltApplies: Uint16Array;
7888
8317
  fltMpx: Float32Array; /** Kept pictures drawn in the frame (spec 23); NaN before attach. */
7889
8318
  kept: Float32Array; /** Kept pictures and range pictures taken in the frame; refreshes go to the per-second count only. */
7890
- retakes: Float32Array;
8319
+ retakes: Float32Array; /** Render targets Pixi created in the frame (WebGPU, counted on the texture system); NaN where not counted. */
8320
+ targetsCreated: Float32Array; /** Render targets Pixi destroyed in the frame (a pool trim, a resize); NaN where not counted. */
8321
+ targetsDestroyed: Float32Array;
7891
8322
  };
7892
8323
  /**
7893
8324
  * What `read` did: one object owned by the ring, rewritten by every call (copy the numbers you keep).
@@ -7919,6 +8350,11 @@ type FrameReadout = Partial<FrameColumns> & {
7919
8350
  * ```ts
7920
8351
  * const ring = app.renderer.monitor.frames();
7921
8352
  * ring.size; // 1024 after nine seconds at 120 fps; 0 in a production build
8353
+ * // The filter of the newest pass, from the pass ring: `kind2` is 1-based, 0 is "no filter".
8354
+ * const kind2 = ring.passes.kind2[ring.passes.newest] ?? 0; // 2
8355
+ * kind2 === 0 ? null : ring.kindNames[kind2 - 1]; // "fog.blur"
8356
+ * // The world systems the `sysIdx` column indexes; a new system makes a new array.
8357
+ * ring.systemNames[ring.columns.sysIdx[ring.newest * 5] ?? 0]; // "board.looks"
7922
8358
  * ```
7923
8359
  */
7924
8360
  type FrameRing = {
@@ -7939,9 +8375,20 @@ type FrameRing = {
7939
8375
  width: Uint16Array;
7940
8376
  height: Uint16Array;
7941
8377
  gpuMs: Float32Array; /** Pre-sized; references, never copies. */
7942
- label: (string | null)[];
8378
+ label: (string | null)[]; /** The entity whose filter drew the pass; 0 for none (the canvas, a texture pass, a filter no view hangs). */
8379
+ owner: Uint32Array; /** The filter kind of a filter pass, 1-based into `kindNames` (`kindNames[kind2 - 1]`); 0 for none. */
8380
+ kind2: Uint8Array; /** The draw count of the frame when the pass began: a pass drew up to the next one's (WebGPU; 0 on WebGL). */
8381
+ drawsAt: Uint16Array; /** The target's texture format (`"rgba8unorm"`), by reference; `null` when unknown. */
8382
+ format: (string | null)[]; /** The target's resolution; NaN when unknown. */
8383
+ resolution: Float32Array;
7943
8384
  }; /** Path id → flow path. */
7944
8385
  readonly paths: readonly string[];
8386
+ /**
8387
+ * The interned filter kinds, `"other"` first: `fltKind` and the passes' `kind2` index it 1-based
8388
+ * (`kindNames[kind - 1]`), 0 is none. It only grows, at most 32 names.
8389
+ */
8390
+ readonly kindNames: readonly string[]; /** The world's system names by registration index (`sysIdx`); a registration makes a new array. */
8391
+ readonly systemNames: readonly string[];
7945
8392
  /**
7946
8393
  * Copies the frames committed after the cursor `since` (a `written` value read earlier), oldest first, into the
7947
8394
  * columns `into` provides (`phaseMs` takes 6 values a frame), at most `into.length` of the newest of them. Allocates
@@ -7974,10 +8421,49 @@ type PerfLine<Kind extends string> = {
7974
8421
  t: number;
7975
8422
  build: string | null;
7976
8423
  };
8424
+ /**
8425
+ * Which groups of the monitor's per-frame work run in an agent build, each on unless switched off (spec 11 R14, the
8426
+ * names of the editor's contract, telemetry proposal §2.7): `gpu` the GPU timer; `passes` the pass ring with its stencil
8427
+ * count and the draws, filter applies, uploads, instruction rebuilds and render targets the counting classes count;
8428
+ * `shaders` the pipeline compiles and shader modules; `problems` the problem rules' accumulators and their judgment;
8429
+ * `memory` the heap read of every frame and its `gc?` hint; `systems` the time of every world system and the five
8430
+ * slowest of a frame; `nodes` the change marks of the frame and its draw reason; `longFrames` the long frames the
8431
+ * browser reports. A section switched off skips its work, and its columns and sample fields read as not measured.
8432
+ *
8433
+ * @example
8434
+ * ```ts
8435
+ * // The editor measures the ward without the per-system timing, the rest as it was.
8436
+ * const sections: MonitorSections = {
8437
+ * gpu: true, passes: true, shaders: true, problems: true, memory: true, systems: false, nodes: true, longFrames: true
8438
+ * };
8439
+ * ```
8440
+ */
8441
+ type MonitorSections = {
8442
+ gpu: boolean;
8443
+ passes: boolean;
8444
+ shaders: boolean;
8445
+ problems: boolean;
8446
+ memory: boolean;
8447
+ systems: boolean;
8448
+ nodes: boolean;
8449
+ longFrames: boolean;
8450
+ };
8451
+ /**
8452
+ * The sections of the monitor in their order: the order of a `sections` event's lists and of `sample.sectionsMs`
8453
+ * (which adds `base`, the rest of the commit). New sections are only ever appended.
8454
+ *
8455
+ * @example
8456
+ * ```ts
8457
+ * // The editor draws one switch per section of the device's monitor, in the engine's order, and greys the ones off.
8458
+ * app.renderer.monitor.sections({ systems: false });
8459
+ * MONITOR_SECTIONS.filter(section => !app.renderer.monitor.sections()[section]); // ["systems"]
8460
+ * ```
8461
+ */
8462
+ declare const MONITOR_SECTIONS: readonly ["gpu", "passes", "shaders", "problems", "memory", "systems", "nodes", "longFrames"];
7977
8463
  /**
7978
8464
  * What an agent build could measure on this page; the header lists it.
7979
8465
  */
7980
- type PerfMeasured = "gpu" | "passes" | "stencilPasses" | "draws" | "filters" | "uploads" | "compiles" | "marks" | "rebuilds" | "longFrames" | "heap" | "systems" | "kept";
8466
+ type PerfMeasured = "gpu" | "passes" | "stencilPasses" | "draws" | "filters" | "uploads" | "compiles" | "marks" | "rebuilds" | "longFrames" | "heap" | "systems" | "kept" | "targets";
7981
8467
  /**
7982
8468
  * What the screen is: in the header and in every `display` event.
7983
8469
  */
@@ -8044,6 +8530,27 @@ type PerfHeaderLine = PerfLine<"header"> & {
8044
8530
  frames: number;
8045
8531
  };
8046
8532
  measured: readonly PerfMeasured[];
8533
+ /**
8534
+ * The bundle identifier of the app, `"com.mokulabs.timber.debug"` in a native debug build (`__MOKU_GAME_APP__`), so a
8535
+ * device stream pairs with its project; `null` on a page without one. The engine always writes it; optional for a
8536
+ * reader of an older log.
8537
+ */
8538
+ app?: {
8539
+ id: string | null;
8540
+ };
8541
+ /**
8542
+ * The debug drawing at the moment of the header: `overdraw` true while the overdraw heat map draws, so the frames that
8543
+ * follow are not read as real cost (a `debug` event marks a switch later). Optional for a reader of an older log.
8544
+ */
8545
+ debug?: {
8546
+ overdraw: boolean;
8547
+ };
8548
+ /**
8549
+ * The sections of the monitor at the moment of the header, as `monitor.sections()` answers them (a `sections` event
8550
+ * marks a switch later): a section `measured` lists but `false` here is switched off, not missing. The engine always
8551
+ * writes it; optional for a reader of an older log.
8552
+ */
8553
+ sections?: Readonly<MonitorSections>;
8047
8554
  };
8048
8555
  /**
8049
8556
  * One Long Animation Frame (or long task) of the page.
@@ -8102,6 +8609,28 @@ type MeanMax = {
8102
8609
  mean: number;
8103
8610
  max: number;
8104
8611
  };
8612
+ /**
8613
+ * The live object counts of a sample, read once a second: a count that grows each time a screen is entered and left
8614
+ * is a leak. `entities` are the world's live entities, `views` the display objects `sync` holds, `timers` the clock's
8615
+ * scheduled moments not yet due, `frameListeners` the `time.onFrame` callbacks, `clockListeners` the `clock.onElapsed`
8616
+ * listeners; every count a plugin registered through `monitor.counts.register` sits beside them by its name
8617
+ * (`inputListeners`).
8618
+ *
8619
+ * @example
8620
+ * ```ts
8621
+ * const counts: PerfCounts = {
8622
+ * entities: 412, views: 180, timers: 1, frameListeners: 9, clockListeners: 2, inputListeners: 6
8623
+ * };
8624
+ * ```
8625
+ */
8626
+ type PerfCounts = {
8627
+ readonly entities: number;
8628
+ readonly views: number;
8629
+ readonly timers: number;
8630
+ readonly frameListeners: number;
8631
+ readonly clockListeners: number;
8632
+ readonly [registered: string]: number;
8633
+ };
8105
8634
  /**
8106
8635
  * One second of frames.
8107
8636
  */
@@ -8184,7 +8713,23 @@ type PerfSampleLine = PerfLine<"sample"> & {
8184
8713
  pooled: number;
8185
8714
  resolution: number;
8186
8715
  monitorMs: number;
8187
- spikes: number;
8716
+ spikes: number; /** The live object counts of the moment (U3); optional for a reader of an older log. */
8717
+ counts?: PerfCounts;
8718
+ /**
8719
+ * Render targets Pixi created and destroyed in the second (WebGPU); `null` where they are not counted. Not the
8720
+ * texture memory of `textures.targets`. Optional for a reader of an older log.
8721
+ */
8722
+ targets?: {
8723
+ created: number;
8724
+ destroyed: number;
8725
+ } | null;
8726
+ /**
8727
+ * What each section of the monitor cost in the second: page-clock ms its part of the frame commits took, summed over
8728
+ * the second, 0 for a section switched off; `base` is the rest of the commits. Pixi-side counting is not timed. A
8729
+ * WebView's clock ticks in 0.1 ms, so a part shorter than that adds 0 or 0.1 at a time: the sum over a second is
8730
+ * what to read. The engine always writes it; optional for a reader of an older log.
8731
+ */
8732
+ sectionsMs?: { readonly [Section in keyof MonitorSections | "base"]: number };
8188
8733
  };
8189
8734
  /**
8190
8735
  * Why a frame is a spike: a rule (`gap`, `js`, `gpu`) or an annotation of a spike. `compile` is a compile in the frame or,
@@ -8279,7 +8824,8 @@ type FrameRecord = {
8279
8824
  */
8280
8825
  type PassKind = "canvas" | "texture" | "filter";
8281
8826
  /**
8282
- * One render pass of a spike frame.
8827
+ * One render pass of a spike frame. The fields after `gpuMs` came with the 0.15 telemetry; a reader of an older log
8828
+ * finds them absent.
8283
8829
  */
8284
8830
  type PassRecord = {
8285
8831
  index: number;
@@ -8288,7 +8834,14 @@ type PassRecord = {
8288
8834
  stencil: boolean | null;
8289
8835
  w: number;
8290
8836
  h: number;
8291
- gpuMs: number | null;
8837
+ gpuMs: number | null; /** The entity whose filter drew the pass; `null` for the canvas, a texture pass and a filter no view hangs. */
8838
+ owner?: Entity | null; /** The label of the owner's view when the line was built; `null` without an owner or once the view left. */
8839
+ view?: string | null; /** The kind id of the filter that drew it (`"fog.blur"`), `"other"` for a filter without one; `null` otherwise. */
8840
+ filter?: string | null; /** The draws issued in the pass (WebGPU); `null` where draws are not counted. */
8841
+ draws?: number | null; /** The target's texture format; `null` when unknown. */
8842
+ format?: string | null; /** The target's resolution; `null` when unknown. */
8843
+ resolution?: number | null; /** The pass took or refreshed a kept picture (its label starts with `kept:`). */
8844
+ kept?: boolean;
8292
8845
  };
8293
8846
  /**
8294
8847
  * A frame over the budget, with its detail.
@@ -8320,9 +8873,10 @@ type PerfLongFrameLine = PerfLine<"longFrame"> & LongFrameEntry & {
8320
8873
  /**
8321
8874
  * The names of `event` lines (`"devtools"` is the editor's overlay event, in 2b because the editor's page agent emits
8322
8875
  * it on the 2b debug build; `"pool-trim"` is the pool trim of spec 25, detail `PoolTrimDetail`; `"warm"` is the end of
8323
- * a pipeline warm-up run of spec 27, detail `WarmDetail`).
8876
+ * a pipeline warm-up run of spec 27, detail `WarmDetail`; `"sections"` is a switch of the monitor's sections, detail
8877
+ * `{ on, off }`: the names of the sections on and off, in the order of `MONITOR_SECTIONS`).
8324
8878
  */
8325
- type PerfEventName = "scene" | "overlay" | "bundle" | "compile" | "upload" | "resolution" | "device-lost" | "device-restored" | "pause" | "resume" | "switch" | "display" | "command" | "recorder" | "rate" | "devtools" | "pool-trim" | "warm";
8879
+ type PerfEventName = "scene" | "overlay" | "bundle" | "compile" | "upload" | "resolution" | "device-lost" | "device-restored" | "pause" | "resume" | "switch" | "display" | "command" | "recorder" | "rate" | "devtools" | "pool-trim" | "warm" | "shader" | "problem" | "debug" | "sections";
8326
8880
  /**
8327
8881
  * What an `overlay` event line says happened to an overlay (spec 22 O10.2): it opened, landed (its enter motion is
8328
8882
  * over), started to leave, is gone, or its range went live or was kept again (the live fallback).
@@ -8375,6 +8929,113 @@ type PoolTrimDetail = {
8375
8929
  type WarmDetail = {
8376
8930
  draws: number;
8377
8931
  };
8932
+ /**
8933
+ * What a `shader` event line reports, in its `what`: a render pipeline (WebGPU) or a program (WebGL) created, or a
8934
+ * WebGPU shader module compiled. New values are only ever appended.
8935
+ *
8936
+ * @example
8937
+ * ```ts
8938
+ * // The editor counts the pipelines of a recording without a switch over strings it does not know.
8939
+ * const pipelines = lines.filter(line => line.kind === "event" && line.name === "shader" && line.detail.what === SHADER_WHATS[0]).length; // 14
8940
+ * ```
8941
+ */
8942
+ declare const SHADER_WHATS: readonly ["compile", "module"];
8943
+ /**
8944
+ * One value of `SHADER_WHATS`.
8945
+ *
8946
+ * @example
8947
+ * ```ts
8948
+ * const what: ShaderWhat = "module";
8949
+ * ```
8950
+ */
8951
+ type ShaderWhat = (typeof SHADER_WHATS)[number];
8952
+ /**
8953
+ * The detail of a `shader` event line: one pipeline or program created, or one shader module compiled, in the frame
8954
+ * `frame`, written at the flush of its second. `boot` is true up to the first drawn frame (the warm-up and the first
8955
+ * screen); `during` names where it happened: the index of the pass of the frame, and the filter, its entity and the
8956
+ * label of that entity's view when a filter was applying. A compile of a plain batch has no filter: `owner: null`.
8957
+ *
8958
+ * @example
8959
+ * ```ts
8960
+ * // The fog blur of the memory screen compiled its pipeline while it applied on the board's fog layer.
8961
+ * const detail: ShaderEventDetail = {
8962
+ * what: "compile", label: "PIXI Pipeline (blur-pass-filter)", ms: 4.2, frame: 1824, boot: false,
8963
+ * during: { pass: 2, filter: "fog.blur", owner: 1_048_601, view: "Sprite#1048601 board.fog:fog" }
8964
+ * };
8965
+ * ```
8966
+ */
8967
+ type ShaderEventDetail = {
8968
+ what: ShaderWhat;
8969
+ label: string | null;
8970
+ ms: number;
8971
+ frame: number;
8972
+ boot: boolean;
8973
+ during: {
8974
+ pass: number | null;
8975
+ filter: string | null;
8976
+ owner: Entity | null;
8977
+ view: string | null;
8978
+ };
8979
+ };
8980
+ /**
8981
+ * The rules of the `problem` event lines, judged once a second: `filter-still` (a filtered view stood still and was
8982
+ * filtered again in every drawn frame), `target-churn` (render targets made and dropped all the time, or right after a
8983
+ * pool trim, or while idle), `retakes` (a kept view taken again and again). New rules are only ever appended.
8984
+ *
8985
+ * @example
8986
+ * ```ts
8987
+ * // The editor reads the rule of the first problem a recording opened: a fog blur filtered again on a still board.
8988
+ * const problems = lines.filter((line): line is PerfEventLine => line.kind === "event" && line.name === "problem");
8989
+ * const opened = problems.find(line => line.detail.cleared !== true);
8990
+ * PROBLEM_RULES.find(rule => rule === opened?.detail.rule); // "filter-still"
8991
+ * ```
8992
+ */
8993
+ declare const PROBLEM_RULES: readonly ["filter-still", "target-churn", "retakes"];
8994
+ /**
8995
+ * One value of `PROBLEM_RULES`.
8996
+ *
8997
+ * @example
8998
+ * ```ts
8999
+ * const rule: ProblemRule = "target-churn";
9000
+ * ```
9001
+ */
9002
+ type ProblemRule = (typeof PROBLEM_RULES)[number];
9003
+ /**
9004
+ * The detail of a `problem` event line: a rule crossed its threshold. Written once per crossing; the line with
9005
+ * `cleared: true` follows when the condition stops. `value` is in the rule's unit (still frames, targets made and
9006
+ * dropped per second, takes in two seconds), `since` the frame the run began, `frames` the frames it covers. `reason`
9007
+ * says why a still filtered view is not kept (`"budget"`: its take was refused for the kept budget, `"not-eligible"`:
9008
+ * the kept policy may not keep it) and which clause of `target-churn` fired (`"rate"`, `"idle"`, `"after-trim"`).
9009
+ *
9010
+ * @example
9011
+ * ```ts
9012
+ * // The fog of the board is blurred again in every frame of a still screen: the budget refused its picture.
9013
+ * const opened: ProblemEventDetail = {
9014
+ * rule: "filter-still", severity: "warn", owner: 1_048_601, view: "Sprite#1048601 board.fog:fog",
9015
+ * filter: "fog.blur", value: 75, threshold: 60, since: 1700, frames: 75, reason: "budget"
9016
+ * };
9017
+ * const closed: ProblemEventDetail = {
9018
+ * rule: "filter-still", cleared: true, owner: 1_048_601, view: "Sprite#1048601 board.fog:fog"
9019
+ * };
9020
+ * ```
9021
+ */
9022
+ type ProblemEventDetail = {
9023
+ rule: ProblemRule;
9024
+ severity: "info" | "warn";
9025
+ owner: Entity | null;
9026
+ view: string | null;
9027
+ filter: string | null;
9028
+ value: number;
9029
+ threshold: number;
9030
+ since: number;
9031
+ frames: number;
9032
+ reason?: "budget" | "not-eligible" | "rate" | "idle" | "after-trim";
9033
+ } | {
9034
+ rule: ProblemRule;
9035
+ cleared: true;
9036
+ owner: Entity | null;
9037
+ view: string | null;
9038
+ };
8378
9039
  /**
8379
9040
  * Something that happened, at the frame it happened in.
8380
9041
  */
@@ -8417,6 +9078,30 @@ type PerfCollectorLine = PerfLine<"gfx"> & {
8417
9078
  p99: number;
8418
9079
  hist: Readonly<Record<string, number>>;
8419
9080
  };
9081
+ /**
9082
+ * A line the editor's collector writes into the computer's copy of a device log for lines the device lost before it
9083
+ * sent them; the engine never produces it.
9084
+ */
9085
+ type PerfGapLine = PerfLine<"gap"> & {
9086
+ /** Computer epoch ms at receipt. */rx: number;
9087
+ device: string | null; /** Lines lost. */
9088
+ lines: number;
9089
+ };
9090
+ /**
9091
+ * A line of wired device telemetry (GPU clock and load, CPU and memory per process, heap samples) the editor's
9092
+ * collector writes into the computer's copy of a device log while it records; the engine never produces it. The
9093
+ * readings are the editor's `DeviceTelemetry` fields as it defines them: the engine passes them as JSON.
9094
+ */
9095
+ type PerfTelemetryLine = PerfLine<"telemetry"> & {
9096
+ /** Computer epoch ms at receipt. */rx: number;
9097
+ device: string;
9098
+ serial: string | null;
9099
+ wired: boolean;
9100
+ gpu: Json;
9101
+ cpu: Json;
9102
+ memory: Json;
9103
+ heap: Json;
9104
+ };
8420
9105
  /**
8421
9106
  * One line of the performance log.
8422
9107
  *
@@ -8425,7 +9110,7 @@ type PerfCollectorLine = PerfLine<"gfx"> & {
8425
9110
  * const off = app.renderer.monitor.stream((line: PerfLogLine) => lines.push(line));
8426
9111
  * ```
8427
9112
  */
8428
- type PerfLogLine = PerfHeaderLine | PerfStartupLine | PerfSampleLine | PerfSpikeLine | PerfLongFrameLine | PerfEventLine | PerfMarkLine | PerfThermalLine | PerfCollectorLine;
9113
+ type PerfLogLine = PerfHeaderLine | PerfStartupLine | PerfSampleLine | PerfSpikeLine | PerfLongFrameLine | PerfEventLine | PerfMarkLine | PerfThermalLine | PerfCollectorLine | PerfGapLine | PerfTelemetryLine;
8429
9114
  //#endregion
8430
9115
  //#region src/plugins/renderer/host/resolution.d.ts
8431
9116
  /**
@@ -8930,6 +9615,11 @@ type RenderStats = {
8930
9615
  * rebuilds at its start and its end. Dev and debug builds only.
8931
9616
  */
8932
9617
  rebuilds?: number;
9618
+ /**
9619
+ * The live object counts of the moment, the same the performance log samples once a second: entities, views, the
9620
+ * clock's timers and listeners, the frame callbacks and every count a plugin registered. Agent builds only.
9621
+ */
9622
+ counts?: PerfCounts;
8933
9623
  };
8934
9624
  /**
8935
9625
  * The draw-call counter of a dev build: the draws of the frame being drawn and of the last one.
@@ -8952,23 +9642,40 @@ type CountingClasses = {
8952
9642
  glTexture: PixiModule["GlTextureSystem"];
8953
9643
  };
8954
9644
  /**
8955
- * One pipeline or program compile a wrapper saw: its frame, its label (by reference) and the CPU
8956
- * time of the creation call. Not public.
9645
+ * One pipeline or program compile, or one shader module, a wrapper saw: its frame, its label (by reference), the CPU
9646
+ * time of the creation call, and where it happened: the pass of the frame, the filter kind and its owner. Not public.
8957
9647
  */
8958
9648
  type CompileRecord = {
8959
9649
  frame: number;
8960
9650
  label: string | null;
8961
- ms: number;
9651
+ ms: number; /** A WebGPU shader module, not a pipeline: not a compile of the frame's count. */
9652
+ module: boolean; /** The index of the pass of the frame it happened in; -1 before the first pass or outside a frame. */
9653
+ pass: number; /** The interned kind of the filter applying then, 1-based; 0 without a filter. */
9654
+ kind: number; /** The entity whose filter applied then; 0 without one. */
9655
+ owner: number;
8962
9656
  };
8963
9657
  /**
8964
- * The last 64 compiles, in pre-made records written in turn, and the shader modules counted for
8965
- * the header. Not public.
9658
+ * The last 64 compiles and shader modules, in pre-made records written in turn, the shader modules counted for the
9659
+ * header, and how many records were ever written and sent as `shader` events. Not public.
8966
9660
  */
8967
9661
  type CompileRing = {
8968
9662
  records: CompileRecord[];
8969
9663
  next: number;
8970
9664
  size: number;
8971
- shaderModules: number;
9665
+ shaderModules: number; /** Records ever written. */
9666
+ written: number; /** Records sent as `shader` events: the ones from `sent` to `written` wait for a flush with a listener. */
9667
+ sent: number;
9668
+ };
9669
+ /**
9670
+ * The owners of the filter applies of a frame, in a fixed table: the entity, its applies and pixels of the frame
9671
+ * (zeroed in `begin()`) and the kind of its last apply. A slot stays while its owner applies. The entities are held in
9672
+ * a `Float64Array`, exact for every entity, read with `cellF64` like the applies and pixels beside them. Not public.
9673
+ */
9674
+ type OwnerTable = {
9675
+ ids: Float64Array;
9676
+ applies: Float64Array;
9677
+ pixels: Float64Array;
9678
+ kinds: Uint8Array;
8972
9679
  };
8973
9680
  /**
8974
9681
  * A render target as the pass rules read it: the public fields of Pixi's `RenderTarget`. Not
@@ -8981,6 +9688,10 @@ type PassTarget = {
8981
9688
  readonly label?: string | undefined;
8982
9689
  readonly colorTexture?: {
8983
9690
  readonly label?: string | undefined;
9691
+ readonly source?: {
9692
+ readonly format?: string;
9693
+ readonly resolution?: number;
9694
+ } | undefined;
8984
9695
  } | undefined;
8985
9696
  };
8986
9697
  /**
@@ -9015,11 +9726,16 @@ type MonitorSinks = {
9015
9726
  kindNames: string[]; /** Applies of each kind in this frame, zeroed in `begin()`. */
9016
9727
  kindApplies: Float64Array; /** Pixels of each kind in this frame, zeroed in `begin()`. */
9017
9728
  kindPixels: Float64Array; /** The kind of each Pixi filter, `SyncState.filterKinds`, handed over at attach. */
9018
- filterKinds: WeakMap<object, string> | undefined;
9729
+ filterKinds: WeakMap<object, string> | undefined; /** The entity each Pixi filter hangs on, `SyncState.filterOwners`, handed over at attach. */
9730
+ filterOwners: WeakMap<object, number> | undefined; /** The entity of the apply running now (0 = none); names the owner of a filter pass. */
9731
+ filterOwner: number; /** The owners of this frame's filter applies (agent builds). */
9732
+ owners: OwnerTable;
9019
9733
  compiles: CompileRing; /** The WebGPU pass sink, set at attach. */
9020
9734
  passes: PassSink | undefined; /** A pass counter runs (F8): the counting encoder exists, or the WebGL runner items are subscribed. */
9021
9735
  measuring: boolean; /** Pixi's instruction rebuilds since the monitor last took them, in a frame or between frames (spec 26 R2.3). */
9022
- rebuildsWindow: number;
9736
+ rebuildsWindow: number; /** The `SECTION` bits of the sections that run in the frame now: what the commit and the counting classes test. */
9737
+ sections: number; /** The `SECTION` bits a switch asked for: they apply at the start of the next frame, so a frame is measured whole. */
9738
+ sectionsNext: number;
9023
9739
  };
9024
9740
  /**
9025
9741
  * The ring's render passes, written in place: `capacity * 4` of them. Not public.
@@ -9037,6 +9753,11 @@ type RingPasses = {
9037
9753
  height: Uint16Array;
9038
9754
  gpuMs: Float32Array;
9039
9755
  label: (string | null)[];
9756
+ owner: Uint32Array;
9757
+ kind2: Uint8Array;
9758
+ drawsAt: Uint16Array;
9759
+ format: (string | null)[];
9760
+ resolution: Float32Array;
9040
9761
  };
9041
9762
  /**
9042
9763
  * The last 64 changes of a frame that had a subject: a scene switched to, a bundle loaded. Not
@@ -9060,7 +9781,7 @@ type PerfRing = {
9060
9781
  written: number;
9061
9782
  columns: FrameColumns;
9062
9783
  passes: RingPasses;
9063
- paths: string[]; /** Interned filter kind names, the sinks' list: `fltKind − 1` indexes it. */
9784
+ paths: string[]; /** Interned filter kind names, the sinks' list: `fltKind − 1` and `passes.kind2 − 1` index it. */
9064
9785
  kindNames: readonly string[]; /** The world's system names by registration index: `sysIdx` indexes it. */
9065
9786
  systemNames: readonly string[]; /** The subjects of the changes of recent frames. */
9066
9787
  events: EventRing; /** The one answer `read` rewrites. */
@@ -9128,6 +9849,50 @@ type TickEstimate = {
9128
9849
  next: number; /** `[0]`: the period of the display rate the median snaps to, ms; NaN while it snaps to none. */
9129
9850
  periodMs: Float64Array;
9130
9851
  };
9852
+ /**
9853
+ * The counts plugins registered with `monitor.counts.register`, in a table of `COUNT_SLOTS` read once a second. Not
9854
+ * public.
9855
+ */
9856
+ type CountTable = {
9857
+ names: string[];
9858
+ reads: Array<() => number>; /** Readers that threw once and were warned about. */
9859
+ failed: Set<string>;
9860
+ };
9861
+ /**
9862
+ * The thresholds of the problem rules, from `renderer.monitor`, defaults filled in. Not public.
9863
+ */
9864
+ type ProblemThresholds = {
9865
+ stillFrames: number;
9866
+ stillMpx: number;
9867
+ churnPerSecond: number;
9868
+ churnSeconds: number;
9869
+ churnAfterTrimMs: number;
9870
+ };
9871
+ /**
9872
+ * The accumulators of the problem rules: per owner of a filter (the slots of `MonitorSinks.owners`) the drawn frames
9873
+ * in a row it applied in, the frame that run began, its applies and pixels of the second; the render targets of the
9874
+ * second and its churn seconds in a row; the takes of each kept record two flushes back; and the problems open now.
9875
+ * Not public.
9876
+ */
9877
+ type ProblemState = {
9878
+ thresholds: ProblemThresholds;
9879
+ run: Float64Array;
9880
+ runSince: Float64Array;
9881
+ secFrames: Float64Array;
9882
+ secPixels: Float64Array; /** 1 while the `filter-still` problem of the owner in that slot is open. */
9883
+ stillOpen: Uint8Array; /** Render targets made and dropped this second. */
9884
+ secTargets: number; /** A create in an idle frame, this second. */
9885
+ idleCreate: boolean; /** A create within `churnAfterTrimMs` after a pool trim, this second. */
9886
+ trimCreate: boolean; /** The frame of the first such create of the second. */
9887
+ createFrame: number; /** Page clock of the last pool trim; NaN before one. */
9888
+ trimAt: number; /** Seconds in a row with `churnPerSecond` or more targets made and dropped, and the frame the run began. */
9889
+ churnRun: number;
9890
+ churnSince: number;
9891
+ churnOpen: boolean; /** The stage-1 takes (`s1Version`) of each kept record at the last two flushes, newest first. */
9892
+ takes: [Map<number, number>, Map<number, number>]; /** The frame of each of those two flushes. */
9893
+ takesFrame: [number, number]; /** The owners with an open `retakes` problem. */
9894
+ retakesOpen: Set<number>;
9895
+ };
9131
9896
  /**
9132
9897
  * What the monitor of an agent build keeps. `undefined` in a build without the flag. Not public.
9133
9898
  */
@@ -9135,8 +9900,14 @@ type PerfState = {
9135
9900
  /** `config.monitor.budgetMs`; a frame's budget is `budgetOf` it. */budget: number | "rate"; /** `config.monitor.frames`: the capacity the ring gets at the first attach. */
9136
9901
  frames: number;
9137
9902
  ring: PerfRing; /** `capacity` long, for percentiles at a flush. */
9138
- scratch: Float32Array; /** Doubles kept unboxed: `[0]` the budget of the frame being committed, `[1]` the end of a flushed window. */
9139
- staged: Float64Array; /** The display's frame period from the ticks: the budget's display part while `time` does not know the display. */
9903
+ scratch: Float32Array;
9904
+ /**
9905
+ * Doubles kept unboxed: `[0]` the budget of the frame being committed, `[1]` the end of a flushed window, `[2]` the
9906
+ * page clock of the last lap of the commit, where the next section's cost starts.
9907
+ */
9908
+ staged: Float64Array; /** The sections `monitor.sections()` answers: the config's at first, then the last switch; frozen. */
9909
+ sections: Readonly<MonitorSections>; /** Page-clock ms of the commits of the current second, by section in `COST` order, `base` last. */
9910
+ sectionsMs: Float64Array; /** The display's frame period from the ticks: the budget's display part while `time` does not know the display. */
9140
9911
  ticks: TickEstimate; /** The `time.rate()` snapshot the last frame saw: a new one may carry another `displayHz` (a `display` event). */
9141
9912
  rate: Readonly<FrameRate> | undefined;
9142
9913
  drawn: boolean; /** The `CHANGED` bits of the frame running now. */
@@ -9164,7 +9935,10 @@ type PerfState = {
9164
9935
  failed: WeakSet<(line: PerfLogLine) => void>; /** Whether the header waits for the native shell. */
9165
9936
  shell: "unknown" | "waiting" | "settled";
9166
9937
  startup: PerfStartupLine | undefined; /** Page-clock end of the first drawn frame; NaN before. */
9167
- firstDrawnAt: number;
9938
+ firstDrawnAt: number; /** The number of the first drawn frame; NaN before. A compile up to it is a boot compile. */
9939
+ firstDrawnFrame: number; /** The counts plugins registered. */
9940
+ counts: CountTable; /** The problem rules' accumulators. */
9941
+ problems: ProblemState;
9168
9942
  timer: FrameTimer | undefined;
9169
9943
  longFrames: LongFrameRing; /** Per-application removers: runners, wrappers, the timer. */
9170
9944
  detach: Array<() => void>; /** The first attach registered the time listener and the observer. */
@@ -9175,8 +9949,9 @@ type PerfState = {
9175
9949
  * wait for a drawn frame.
9176
9950
  */
9177
9951
  type MonitorState = {
9178
- /** Clock time at the start of the frame being drawn; `undefined` between frames. */frameStart: number | undefined; /** Clock time at the start of the last frame, the far end of the next interval. */
9179
- lastStart: number | undefined; /** Clock milliseconds between frame starts, summed over the open window. */
9952
+ /** Clock time at the start of the frame being drawn; NaN between frames. A double from the start: never boxed. */frameStart: number; /** Clock time at the start of the last frame, the far end of the next interval; NaN before the first. */
9953
+ lastStart: number; /** `[0]`: the clock reading `begin` or `end` hands the window, staged: a double handed to a call is boxed. */
9954
+ staged: Float64Array; /** Clock milliseconds between frame starts, summed over the open window. */
9180
9955
  windowMs: number; /** Intervals between frame starts in the open window. */
9181
9956
  intervals: number; /** Clock milliseconds spent inside frames in the open window. */
9182
9957
  workMs: number; /** Frames finished in the open window. */
@@ -9382,6 +10157,70 @@ type PerfMonitorApi = {
9382
10157
  * ```
9383
10158
  */
9384
10159
  setResolution(resolution: number | undefined): number;
10160
+ /**
10161
+ * The sections of the monitor of a debug build, each on unless switched off: what runs every frame, and what can be
10162
+ * switched off to measure the game without it. Without an argument it reads them. With one it switches the sections
10163
+ * it names, from the next frame on, and writes a `sections` event; a write needs control (`--debug`, or a dev page):
10164
+ * a read-only page (`--debug=read`) keeps the sections and throws. A section off skips its work: its ring columns and its sample fields read as not measured, and its
10165
+ * `sample.sectionsMs` is 0. Agent builds only: elsewhere every section answers on and nothing switches.
10166
+ *
10167
+ * @param next - The sections to switch: `true` on, `false` off; a section left out keeps its switch.
10168
+ * @returns The sections from the next frame on, one frozen object until the next switch.
10169
+ * @throws {Error} With `reason: "read-only"` when a page without control is asked to change a section.
10170
+ * @example
10171
+ * ```ts
10172
+ * // The editor's Mission control: what does the ward cost without the per-system timing?
10173
+ * app.renderer.monitor.sections().systems; // true
10174
+ * app.renderer.monitor.sections({ systems: false }); // { gpu: true, passes: true, shaders: true, problems: true, memory: true, systems: false, nodes: true, longFrames: true }
10175
+ * // a stream() listener gets { kind: "event", name: "sections", detail: { on: ["gpu", "passes", "shaders", "problems", "memory", "nodes", "longFrames"], off: ["systems"] }, … }
10176
+ * ```
10177
+ */
10178
+ sections(next?: Partial<MonitorSections>): Readonly<MonitorSections>;
10179
+ /**
10180
+ * The live object counts of a debug build: a plugin the renderer cannot depend on (it depends on the renderer)
10181
+ * registers a count here, and the monitor reads it once a second into `sample.counts` and `stats().counts`.
10182
+ *
10183
+ * @example
10184
+ * ```ts
10185
+ * // A game's own pool of particles joins the leak counts of a debug build.
10186
+ * app.renderer.monitor.counts.register("sparks", () => sparkPool.live);
10187
+ * app.renderer.stats().counts?.sparks; // 24
10188
+ * ```
10189
+ */
10190
+ counts: MonitorCounts;
10191
+ };
10192
+ /**
10193
+ * The count registry of `renderer.monitor.counts`: at most 16 counts, read once a second. Agent builds only.
10194
+ *
10195
+ * @example
10196
+ * ```ts
10197
+ * // A game's pool of sparks joins the leak counts of the sample.
10198
+ * app.renderer.monitor.counts.register("sparks", () => sparkPool.size);
10199
+ * ```
10200
+ */
10201
+ type MonitorCounts = {
10202
+ /**
10203
+ * Registers a count under a name: `read` is called once a second, at the flush of the sample, and its number goes
10204
+ * into `sample.counts[name]` and `stats().counts[name]`. It must answer a number and allocate nothing. A name already
10205
+ * registered gets the new reader. The renderer's `onStop` drops every registration. Agent builds only: without the
10206
+ * flag nothing is kept and the returned remover does nothing.
10207
+ *
10208
+ * @param name - The count's name in the sample, beside `entities`, `views`, `timers`, `frameListeners` and
10209
+ * `clockListeners`.
10210
+ * @param read - Answers the count now.
10211
+ * @returns The remover; a second call does nothing.
10212
+ * @throws {Error} When 16 counts are registered already: `[game] Monitor count table is full (16).`
10213
+ * @example
10214
+ * ```ts
10215
+ * // A game's pool of sparks joins the leak counts: the reader answers the pool's size, and a pool that grows with
10216
+ * // every visit of the shop shows as a count that grows in every sample.
10217
+ * const off = app.renderer.monitor.counts.register("myPool", () => pool.size);
10218
+ * app.renderer.stats().counts?.myPool; // 24 while the pool holds 24 sparks
10219
+ * off();
10220
+ * app.renderer.stats().counts?.myPool; // undefined
10221
+ * ```
10222
+ */
10223
+ register(name: string, read: () => number): () => void;
9385
10224
  };
9386
10225
  /**
9387
10226
  * monitor methods the frame and the hooks drive. Not public.
@@ -9559,7 +10398,7 @@ type PictureRequest = {
9559
10398
  reject: (error: Error) => void;
9560
10399
  };
9561
10400
  declare namespace types_d_exports$2 {
9562
- export { Api$1 as Api, AspectRange, BitmapFontData, CaptureOptions, Captured, Config$2 as Config, CountingClasses, CoverFrame, CreateTextureOptions$1 as CreateTextureOptions, DebugApi, DebugSwitches, DemandFrame, DemandModule, DemandReason, DemandState, Deps$2 as Deps, DisplayAdapter, DisplayEntry, DisplaysApi, DrawCounter, Events$2 as Events, FilterCache, FilterSlot, FilterSlotInputs, FiltersApi, FontsApi, FrameColumns, FrameReadResult, FrameReadout, FrameRecord, FrameRing, HitBox, HostApi, HostInternal, HostModule, HostState, HudPartValue, KeptInternal, KeptModule, KeptRange, KeptState, KeptStats, KernelSlice$2 as KernelSlice, LayerEntry, LegendEntry, LongFrameEntry, Modules, MonitorApi, MonitorConfig, MonitorDeps, MonitorInternal, MonitorModule, MonitorState, NineBorders$1 as NineBorders, Orientation, OverlayEventDetail, OverlayLevelValue, OverlayMode, OverlayPhase, OverlayReport, OverlayValue, OverlayWhat, PassKind, PassRecord, PerfCause, PerfChange, PerfChangeEntry, PerfCollectorLine, PerfDisplay, PerfEventLine, PerfEventName, PerfHeaderLine, PerfLogLine, PerfLongFrameLine, PerfMarkLine, PerfMeasured, PerfMonitorApi, PerfSampleLine, PerfSpikeLine, PerfStartupLine, PerfThermalLine, PictureRect, PixiApplication, PixiBitmapFont, PixiBounds, PixiContainer, PixiFilter, PixiFilterManager, PixiGraphics, PixiMatrix, PixiModule, PixiNineSliceSprite, PixiRectangle, PixiRenderTexture, PixiSprite, PixiTexture, Point, PoolTrim, PoolTrimDetail, Rect, RenderStats, RendererCtx, RendererKind, SafeArea, SliceFrame, State$2 as State, SyncApi, SyncCounts, SyncInternal, SyncModule, SyncState, TeardownScope, TextureProvider, TextureUsage, TexturesApi, Under, View, ViewKind, ViewportApi, ViewportInternal, ViewportModule, ViewportSize, ViewportState, WarmDetail };
10401
+ export { Api$1 as Api, AspectRange, BitmapFontData, CaptureOptions, Captured, Config$2 as Config, CountingClasses, CoverFrame, CreateTextureOptions$1 as CreateTextureOptions, DebugApi, DebugSwitches, DemandFrame, DemandModule, DemandReason, DemandState, Deps$2 as Deps, DisplayAdapter, DisplayEntry, DisplaysApi, DrawCounter, Events$2 as Events, FilterCache, FilterSlot, FilterSlotInputs, FiltersApi, FontsApi, FrameColumns, FrameReadResult, FrameReadout, FrameRecord, FrameRing, HitBox, HostApi, HostInternal, HostModule, HostState, HudPartValue, KeptInternal, KeptModule, KeptRange, KeptState, KeptStats, KernelSlice$2 as KernelSlice, LayerEntry, LegendEntry, LongFrameEntry, Modules, MonitorApi, MonitorConfig, MonitorCounts, MonitorDeps, MonitorInternal, MonitorModule, MonitorSections, MonitorState, NineBorders$1 as NineBorders, Orientation, OverlayEventDetail, OverlayLevelValue, OverlayMode, OverlayPhase, OverlayReport, OverlayValue, OverlayWhat, PassKind, PassRecord, PerfCause, PerfChange, PerfChangeEntry, PerfCollectorLine, PerfCounts, PerfDisplay, PerfEventLine, PerfEventName, PerfGapLine, PerfHeaderLine, PerfLogLine, PerfLongFrameLine, PerfMarkLine, PerfMeasured, PerfMonitorApi, PerfSampleLine, PerfSpikeLine, PerfStartupLine, PerfTelemetryLine, PerfThermalLine, PictureRect, PixiApplication, PixiBitmapFont, PixiBounds, PixiContainer, PixiFilter, PixiFilterManager, PixiGraphics, PixiMatrix, PixiModule, PixiNineSliceSprite, PixiRectangle, PixiRenderTexture, PixiSprite, PixiTexture, Point, PoolTrim, PoolTrimDetail, ProblemEventDetail, ProblemRule, Rect, RenderStats, RendererCtx, RendererKind, SafeArea, ShaderEventDetail, ShaderWhat, SliceFrame, State$2 as State, SyncApi, SyncCounts, SyncInternal, SyncModule, SyncState, TeardownScope, TextureProvider, TextureUsage, TexturesApi, Under, View, ViewKind, ViewportApi, ViewportInternal, ViewportModule, ViewportSize, ViewportState, WarmDetail };
9563
10402
  }
9564
10403
  /**
9565
10404
  * The part of Pixi the engine uses: the classes `sync` builds views from (a `Rectangle` frames the
@@ -9776,11 +10615,16 @@ type Config$2 = {
9776
10615
  warm: number;
9777
10616
  };
9778
10617
  /**
9779
- * The performance monitor of an agent build (`__MOKU_GAME_AGENT__`: dev and `--debug` builds).
10618
+ * The performance monitor of an agent build (`__MOKU_GAME_AGENT__`: dev and `--debug` builds): the spike budget, the
10619
+ * ring's frames and the thresholds of the `problem` rules. A threshold left out keeps its default.
9780
10620
  *
9781
10621
  * @example
9782
10622
  * ```ts
9783
10623
  * createApp({ pluginConfigs: { renderer: { mount: "#game", monitor: { budgetMs: 8.3, frames: 2048 } } } });
10624
+ * // A game with a large blurred backdrop flags it sooner: 30 still frames over 0.1 Mpx.
10625
+ * createApp({ pluginConfigs: { renderer: { mount: "#game", monitor: { budgetMs: "rate", frames: 1024, stillFrames: 30, stillMpx: 0.1 } } } });
10626
+ * // A debug build that starts without the per-system timing: app.renderer.monitor.sections().systems is false.
10627
+ * createApp({ pluginConfigs: { renderer: { mount: "#game", monitor: { budgetMs: "rate", frames: 1024, sections: { systems: false } } } } });
9784
10628
  * ```
9785
10629
  */
9786
10630
  type MonitorConfig = {
@@ -9791,7 +10635,17 @@ type MonitorConfig = {
9791
10635
  * no config change; a number is a fixed budget in ms.
9792
10636
  */
9793
10637
  budgetMs: number | "rate"; /** Frames the ring keeps in an agent build, 64 to 16384; render passes keep 4x as many. */
9794
- frames: number;
10638
+ frames: number; /** `filter-still`: drawn frames a filtered view stands still, filtered in every one. Default 60. */
10639
+ stillFrames?: number; /** `filter-still`: megapixels its filter covers a frame, at least. Default 0.25. */
10640
+ stillMpx?: number; /** `target-churn`: render targets made and dropped a second that count as churn. Default 4. */
10641
+ churnPerSecond?: number; /** `target-churn`: seconds of churn in a row before the problem. Default 3. */
10642
+ churnSeconds?: number; /** `target-churn`: a render target made this many ms after a pool trim is churn at once. Default 1000. */
10643
+ churnAfterTrimMs?: number;
10644
+ /**
10645
+ * The sections of the monitor a debug build starts with (spec 11 R14): a section left out is on. `monitor.sections`
10646
+ * switches them later.
10647
+ */
10648
+ sections?: Partial<MonitorSections>;
9795
10649
  };
9796
10650
  /**
9797
10651
  * renderer plugin state: one branch per module.
@@ -11218,4 +12072,4 @@ type BundleCompanion = {
11218
12072
  unload(bundle: string): void;
11219
12073
  };
11220
12074
  //#endregion
11221
- export { PerfChangeEntry as $, NodeFx as $n, FrameDiff as $t, PixiSprite as A, defineFlow as An, Api$7 as Ar, Shape as At, FrameReadout as B, DefineNode as Bn, Require as Br, Config$3 as Bt, State$1 as C, EmitterIdOf as Cn, RngState as Cr, HudPart as Ct, MonitorConfig as D, State$4 as Dn, PauseReason as Dr, Overlay as Dt, Config$2 as E, Position as En, Events$6 as Er, NineSliceValue as Et, types_d_exports$2 as F, FeatureDescription as Fn, ThermalReading as Fr, Transform as Ft, OverlayWhat as G, RouteStep as Gn, AnyComponentType as Gt, FrameRing as H, FlowState as Hn, State$3 as Ht, Captured as I, FeaturePlugin as In, Time as Ir, TransformValue as It, PERF_LOG_VERSION as J, Descriptor as Jn, ComponentSchema as Jt, PERF_CAUSES as K, SceneIdOf as Kn, AnyComponentValue as Kt, RenderStats as L, FeatureTree as Ln, types_d_exports$8 as Lr, sprite as Lt, PoolTrim as M, slot as Mn, FrameRate as Mr, Sprite as Mt, State$2 as N, to as Nn, RateChange as Nr, SpriteOptions as Nt, PixiFilter as O, TextStylesOf as On, State$7 as Or, OverlayValue as Ot, Under as P, type as Pn, State$8 as Pr, SpriteValue as Pt, PerfChange as Q, Hint as Qn, EntitySnapshot as Qt, FrameColumns as R, AnyFlow as Rn, Config$9 as Rr, DEMAND_REASONS as Rt, ScenesApi as S, Config$4 as Sn, RngApi as Sr, Display as St, Api$1 as T, HudKeysOf as Tn, Config$7 as Tr, NineSlice$1 as Tt, LongFrameEntry as U, JournalEntry as Un, types_d_exports$3 as Ut, FrameRecord as V, FlowGraph as Vn, Explained as Vt, OverlayEventDetail as W, NodeTable as Wn, AnyComponent as Wt, PassRecord as X, EffectOptions as Xn, ComponentValue as Xt, PassKind as Y, Effect as Yn, ComponentType as Yt, PerfCause as Z, GuideOptions as Zn, Entity as Zt, DefineScene as _, TrackOptions as _n, SaveDoc as _r, FilterSlotInputs as _t, Config as a, SystemFor as an, State$5 as ar, PerfLogLine as at, SceneDefinition as b, Api$3 as bn, SnapshotOf as br, ViewportSize as bt, LoadBundles as c, DescriptionNode as cn, Config$6 as cr, PerfMeasured as ct, Manifest as d, MotionCause as dn, Root as dr, PerfStartupLine as dt, Mut as en, Answer as er, PerfCollectorLine as et, State as f, MotionHandle as fn, State$6 as fr, PerfThermalLine as ft, AnySceneProjection as g, ReducedMotion as gn, ProviderCall as gr, FilterSlot as gt, types_d_exports as h, ProjectionSpec as hn, PlayerStateProvider as hr, DebugSwitches as ht, BundleSpec as i, SystemDefinition as in, FakeClock as ir, PerfHeaderLine as it, PixiTexture as j, exit as jn, Config$8 as jr, ShapeValue as jt, PixiModule as k, types_d_exports$4 as kn, types_d_exports$7 as kr, Parent as kt, LoadReason as l, Ease as ln, Events$5 as lr, PerfSampleLine as lt, Usage as m, ProjectionMotion as mn, Patch as mr, WarmDetail as mt, AssetsIo as n, QueryTerm as nn, ClockSource as nr, PerfEventLine as nt, DefineBundles as o, TagType as on, types_d_exports$5 as or, PerfLongFrameLine as ot, Tier as p, NumericFields as pn, types_d_exports$6 as pr, PoolTrimDetail as pt, PERF_GAP_BUCKETS as q, TypeTag as qn, ComponentHandle as qt, BundleMap as r, ResourceType as rn, Config$5 as rr, PerfEventName as rt, Events as s, ChangeHook as sn, Api$5 as sr, PerfMarkLine as st, Api as t, Narrowed as tn, Api$4 as tr, PerfDisplay as tt, LoadResult as u, Motion as un, Json as ur, PerfSpikeLine as ut, Events$1 as v, TrackSegment as vn, SaveUnreadableError as vr, Orientation as vt, types_d_exports$1 as w, GameTypes as wn, Api$6 as wr, HudPartValue as wt, SceneSpec as x, BundlesOf as xn, StoreApi as xr, FilterCache as xt, LayerMap as y, ViewHandle as yn, Snapshot as yr, Point as yt, FrameReadResult as z, Bookmark as zn, Events$8 as zr, Api$2 as zt };
12075
+ export { PassKind as $, FlowGraph as $n, Explained as $t, PixiSprite as A, TrackSegment as An, SaveUnreadableError as Ar, Orientation as At, FrameReadResult as B, TextStylesOf as Bn, State$7 as Br, OverlayValue as Bt, State$1 as C, MotionCause as Cn, Root as Cr, SHADER_WHATS as Ct, MonitorConfig as D, ProjectionSpec as Dn, PlayerStateProvider as Dr, DebugSwitches as Dt, Config$2 as E, ProjectionMotion as En, Patch as Er, WarmDetail as Et, types_d_exports$2 as F, EmitterIdOf as Fn, RngState as Fr, HudPart as Ft, MONITOR_SECTIONS as G, to as Gn, RateChange as Gr, SpriteOptions as Gt, FrameRecord as H, defineFlow as Hn, Api$7 as Hr, Shape as Ht, Captured as I, GameTypes as In, Api$6 as Ir, HudPartValue as It, OverlayWhat as J, FeaturePlugin as Jn, Time as Jr, TransformValue as Jt, MonitorSections as K, type as Kn, State$8 as Kr, SpriteValue as Kt, MonitorCounts as L, HudKeysOf as Ln, Config$7 as Lr, NineSlice$1 as Lt, PoolTrim as M, Api$3 as Mn, SnapshotOf as Mr, ViewportSize as Mt, State$2 as N, BundlesOf as Nn, StoreApi as Nr, FilterCache as Nt, PixiFilter as O, ReducedMotion as On, ProviderCall as Or, FilterSlot as Ot, Under as P, Config$4 as Pn, RngApi as Pr, Display as Pt, PROBLEM_RULES as Q, DefineNode as Qn, Require as Qr, Config$3 as Qt, RenderStats as R, Position as Rn, Events$6 as Rr, NineSliceValue as Rt, ScenesApi as S, Motion as Sn, Json as Sr, ProblemRule as St, Api$1 as T, NumericFields as Tn, types_d_exports$6 as Tr, ShaderWhat as Tt, FrameRing as U, exit as Un, Config$8 as Ur, ShapeValue as Ut, FrameReadout as V, types_d_exports$4 as Vn, types_d_exports$7 as Vr, Parent as Vt, LongFrameEntry as W, slot as Wn, FrameRate as Wr, Sprite as Wt, PERF_GAP_BUCKETS as X, AnyFlow as Xn, Config$9 as Xr, DEMAND_REASONS as Xt, PERF_CAUSES as Y, FeatureTree as Yn, types_d_exports$8 as Yr, sprite as Yt, PERF_LOG_VERSION as Z, Bookmark as Zn, Events$8 as Zr, Api$2 as Zt, DefineScene as _, SystemFor as _n, State$5 as _r, PerfStartupLine as _t, Config as a, ComponentHandle as an, TypeTag as ar, PerfCounts as at, SceneDefinition as b, DescriptionNode as bn, Config$6 as br, PoolTrimDetail as bt, LoadBundles as c, ComponentValue as cn, EffectOptions as cr, PerfEventName as ct, Manifest as d, FrameDiff as dn, NodeFx as dr, PerfLogLine as dt, State$3 as en, FlowState as er, PassRecord as et, State as f, Mut as fn, Answer as fr, PerfLongFrameLine as ft, AnySceneProjection as g, SystemDefinition as gn, FakeClock as gr, PerfSpikeLine as gt, types_d_exports as h, ResourceType as hn, Config$5 as hr, PerfSampleLine as ht, BundleSpec as i, AnyComponentValue as in, SceneIdOf as ir, PerfCollectorLine as it, PixiTexture as j, ViewHandle as jn, Snapshot as jr, Point as jt, PixiModule as k, TrackOptions as kn, SaveDoc as kr, FilterSlotInputs as kt, LoadReason as l, Entity as ln, GuideOptions as lr, PerfGapLine as lt, Usage as m, QueryTerm as mn, ClockSource as mr, PerfMeasured as mt, AssetsIo as n, AnyComponent as nn, NodeTable as nr, PerfChange as nt, DefineBundles as o, ComponentSchema as on, Descriptor as or, PerfDisplay as ot, Tier as p, Narrowed as pn, Api$4 as pr, PerfMarkLine as pt, OverlayEventDetail as q, FeatureDescription as qn, ThermalReading as qr, Transform as qt, BundleMap as r, AnyComponentType as rn, RouteStep as rr, PerfChangeEntry as rt, Events as s, ComponentType as sn, Effect as sr, PerfEventLine as st, Api as t, types_d_exports$3 as tn, JournalEntry as tr, PerfCause as tt, LoadResult as u, EntitySnapshot as un, Hint as ur, PerfHeaderLine as ut, Events$1 as v, TagType as vn, types_d_exports$5 as vr, PerfTelemetryLine as vt, types_d_exports$1 as w, MotionHandle as wn, State$6 as wr, ShaderEventDetail as wt, SceneSpec as x, Ease as xn, Events$5 as xr, ProblemEventDetail as xt, LayerMap as y, ChangeHook as yn, Api$5 as yr, PerfThermalLine as yt, FrameColumns as z, State$4 as zn, PauseReason as zr, Overlay as zt };