@moku-labs/game 0.15.19 → 0.15.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/page.d.mts +1 -1
- package/dist/app/page.mjs +1 -1
- package/dist/app/system.d.mts +3 -3
- package/dist/app.d.mts +3 -3
- package/dist/app.mjs +1 -1
- package/dist/assets.d.mts +1 -1
- package/dist/{capture-D7XvoHdH.mjs → capture-PTJXDpxK.mjs} +117 -16
- package/dist/{commands-G9maNqTK.d.mts → commands-CV6Deuaj.d.mts} +5 -5
- package/dist/control.d.mts +4 -4
- package/dist/control.mjs +1 -1
- package/dist/{define-BAuGN0UM.d.mts → define-42KUe-al.d.mts} +2 -2
- package/dist/{headless-BA6ffzqD.mjs → headless-Bxxl_mQX.mjs} +1 -1
- package/dist/{headless-uUUH54Ij.d.mts → headless-DIbfzWkr.d.mts} +1 -1
- package/dist/{index-Cku9aRz_.d.mts → index-CHI7dTc9.d.mts} +26 -4
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +3 -3
- package/dist/inspect.d.mts +6 -6
- package/dist/inspect.mjs +1 -1
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{run-BqYmcNo7.mjs → run-DRcc2SKF.mjs} +2 -2
- package/dist/{runtime-CuMDZhii.d.mts → runtime-BcMjHiib.d.mts} +1 -1
- package/dist/{sources-BYKXYPhd.mjs → sources-Dpc4NXvG.mjs} +1 -1
- package/dist/{src-CXfBuKpl.mjs → src-hVe3f8GV.mjs} +1380 -302
- package/dist/testing.d.mts +4 -4
- package/dist/testing.mjs +2 -2
- package/dist/{types-rr56N0Ab.d.mts → types-9xAh1wGm.d.mts} +8 -5
- package/dist/{types-bwefoJES.d.mts → types-CHmCFReJ.d.mts} +219 -24
- package/dist/{types-4T4QTU48.d.mts → types-DmGs6x0c.d.mts} +24 -6
- package/dist/{types-DQtBMqRE.d.mts → types-DwREs5vb.d.mts} +4 -4
- package/dist/{types-DMsvVZZT.d.mts → types-L4nVF9Ny.d.mts} +2 -2
- package/dist/{visual-B-c5OIFB.d.mts → visual-CjXAp0DW.d.mts} +6 -6
- package/dist/{visual-DNM5oFYx.mjs → visual-kEC9nb-o.mjs} +20 -2
- package/dist/visual.d.mts +1 -1
- package/dist/visual.mjs +2 -2
- package/llms.txt +2 -2
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import { r as systemSource, t as memory } from "./memory-CWlqhS7l.mjs";
|
|
3
3
|
import { a as finishDrafts, i as enableDraftPatches, n as deepFreeze$1, o as openDrafts, r as dropDrafts } from "./drafts-BawpSaVp.mjs";
|
|
4
|
-
import { d as
|
|
5
|
-
import { $ as component, A as localPoseOf, B as OverlayLevel, C as builtInStyles, D as textFor, E as readStyle, F as rootPoseOf, G as arcDefaults, H as Shape, I as Display, J as Exiting, K as componentsFor, L as HudPart, M as poseUnder, N as rootPointOf, O as applyPose, P as rootPoseInto, Q as Tree, R as NineSlice, U as Sprite, V as Parent, W as Transform, X as Now, Y as Layer, Z as Order, _ as uiFor, a as visualOf$1, b as Countdown, c as Covered, d as LocalWrite, f as Scroll, h as UiCounters, i as samePose, it as tag, j as parentOf$1, k as isLeaving, l as DebugTrigger, m as UI_OWNER, n as drawnRect, nt as system, o as visualRectOf, p as ScrollState, r as fitScaleOf, rt as systemsFor, s as Box, t as CONTENT$1, tt as resource, u as Escapable, x as Text, z as Overlay } from "./visual-
|
|
4
|
+
import { _ as pushEntry, c as collectFlows, d as findNode, f as framePath, g as compact, h as slotNames, i as servePaused, l as describeGraph, m as isRestCheckpoint, n as captureFrame, o as epochOf, p as graphHash, r as serveCaptures, s as pageNow$1, t as cancelCaptures } from "./capture-PTJXDpxK.mjs";
|
|
5
|
+
import { $ as component, A as localPoseOf, B as OverlayLevel, C as builtInStyles, D as textFor, E as readStyle, F as rootPoseOf, G as arcDefaults, H as Shape, I as Display, J as Exiting, K as componentsFor, L as HudPart, M as poseUnder, N as rootPointOf, O as applyPose, P as rootPoseInto, Q as Tree, R as NineSlice, U as Sprite, V as Parent, W as Transform, X as Now, Y as Layer, Z as Order, _ as uiFor, a as visualOf$1, b as Countdown, c as Covered, d as LocalWrite, f as Scroll, h as UiCounters, i as samePose, it as tag, j as parentOf$1, k as isLeaving, l as DebugTrigger, m as UI_OWNER, n as drawnRect, nt as system, o as visualRectOf, p as ScrollState, r as fitScaleOf, rt as systemsFor, s as Box, t as CONTENT$1, tt as resource, u as Escapable, x as Text, z as Overlay } from "./visual-kEC9nb-o.mjs";
|
|
6
6
|
import { a as isDev } from "./dev-BNndV-hU.mjs";
|
|
7
7
|
import { n as isComponentDefinition } from "./component-DGg5DqKK.mjs";
|
|
8
8
|
import { n as flatten } from "./flatten-nUX9PpGo.mjs";
|
|
@@ -252,7 +252,7 @@ function createMeasureState() {
|
|
|
252
252
|
*
|
|
253
253
|
* @returns The page clock, ms.
|
|
254
254
|
*/
|
|
255
|
-
function pageNow
|
|
255
|
+
function pageNow() {
|
|
256
256
|
return globalThis.performance.now();
|
|
257
257
|
}
|
|
258
258
|
/**
|
|
@@ -277,7 +277,7 @@ function timePhase(ctx, record, callbacks, phase, start) {
|
|
|
277
277
|
error
|
|
278
278
|
});
|
|
279
279
|
}
|
|
280
|
-
const end = pageNow
|
|
280
|
+
const end = pageNow();
|
|
281
281
|
record.phaseMs[phase] = end - start;
|
|
282
282
|
return end;
|
|
283
283
|
}
|
|
@@ -294,7 +294,7 @@ function timePhase(ctx, record, callbacks, phase, start) {
|
|
|
294
294
|
function runMeasuredFrame(ctx, measure, at, stepped) {
|
|
295
295
|
const { record } = measure;
|
|
296
296
|
const phases = capturePhases(ctx);
|
|
297
|
-
let mark = pageNow
|
|
297
|
+
let mark = pageNow();
|
|
298
298
|
record.startMs = stepped ? 0 : mark - at;
|
|
299
299
|
mark = timePhase(ctx, record, phases[0], 0, mark);
|
|
300
300
|
mark = timePhase(ctx, record, phases[1], 1, mark);
|
|
@@ -324,7 +324,7 @@ function runMeasuredFrame(ctx, measure, at, stepped) {
|
|
|
324
324
|
} catch (error) {
|
|
325
325
|
ctx.log.error("time: measure listener failed", { error });
|
|
326
326
|
}
|
|
327
|
-
measure.listenersMs = pageNow
|
|
327
|
+
measure.listenersMs = pageNow() - mark;
|
|
328
328
|
measure.lastAt = at;
|
|
329
329
|
measure.skipped = 0;
|
|
330
330
|
}
|
|
@@ -595,6 +595,11 @@ const CAP_SLACK_SHARE = .12;
|
|
|
595
595
|
const DISPLAY_SHARE = .25;
|
|
596
596
|
/** …and at least this share of the window's drawn intervals: the p90 rule written as a count. */
|
|
597
597
|
const DROP_SHARE = .1;
|
|
598
|
+
/**
|
|
599
|
+
* The drop window stays late this long before the rate drops, ms (F64): longer than the window, so one burst of late
|
|
600
|
+
* frames (a mount, an upload, a GC) never drops the rate by itself; only a sustained slow run does.
|
|
601
|
+
*/
|
|
602
|
+
const DROP_HOLD_MS = 2e3;
|
|
598
603
|
/** The least time at a rate before a return, ms. */
|
|
599
604
|
const DWELL_BASE_MS = 5e3;
|
|
600
605
|
/** The dwell never grows past this, ms. */
|
|
@@ -901,6 +906,7 @@ function createGovernor(floor) {
|
|
|
901
906
|
returnDrawn: 0,
|
|
902
907
|
returnMissed: 0,
|
|
903
908
|
returnHeavy: 0,
|
|
909
|
+
lateSince: NaN,
|
|
904
910
|
switchedAt: Number.NEGATIVE_INFINITY,
|
|
905
911
|
lastReturnAt: Number.NEGATIVE_INFINITY,
|
|
906
912
|
climbed: false,
|
|
@@ -977,7 +983,7 @@ function createRateState(config) {
|
|
|
977
983
|
* @returns The page clock, ms.
|
|
978
984
|
*/
|
|
979
985
|
function nowOf() {
|
|
980
|
-
return typeof globalThis.performance === "undefined" ? 0 : pageNow
|
|
986
|
+
return typeof globalThis.performance === "undefined" ? 0 : pageNow();
|
|
981
987
|
}
|
|
982
988
|
/**
|
|
983
989
|
* Empties both windows: evidence of one rate never judges another, and a resume is a new situation.
|
|
@@ -993,6 +999,7 @@ function emptyWindows(g) {
|
|
|
993
999
|
g.returnDrawn = 0;
|
|
994
1000
|
g.returnMissed = 0;
|
|
995
1001
|
g.returnHeavy = 0;
|
|
1002
|
+
g.lateSince = NaN;
|
|
996
1003
|
}
|
|
997
1004
|
/**
|
|
998
1005
|
* Takes the oldest sample of the drop window out of its counts.
|
|
@@ -1326,15 +1333,36 @@ function isSample(ctx, g, intervalMs) {
|
|
|
1326
1333
|
return g.lastDrawn && !g.lastIdle && !g.lastProbed && intervalMs <= 500 && g.lastFps === rate.fps && rate.mode === "auto" && rate.display.hz !== null;
|
|
1327
1334
|
}
|
|
1328
1335
|
/**
|
|
1329
|
-
* Tells whether the drop window is late
|
|
1336
|
+
* Tells whether the drop window is late: late intervals reach `max(8, 10 %)` of the second's.
|
|
1330
1337
|
*
|
|
1331
1338
|
* @param g - The governor.
|
|
1332
|
-
* @returns True when the
|
|
1339
|
+
* @returns True when the window is late.
|
|
1333
1340
|
*/
|
|
1334
|
-
function
|
|
1341
|
+
function isLate(g) {
|
|
1335
1342
|
return g.dropMissed >= Math.max(8, Math.ceil(DROP_SHARE * g.dropDrawn));
|
|
1336
1343
|
}
|
|
1337
1344
|
/**
|
|
1345
|
+
* Tells whether the rate should step down (F64): the drop window has been late for 2 s in a row, so one burst of late
|
|
1346
|
+
* frames never drops it. Within 3 s of a step of the rule itself the first late window is enough: a return on
|
|
1347
|
+
* probation that fails, and the next step of a descent already under way. Notes when the window turned late.
|
|
1348
|
+
*
|
|
1349
|
+
* @param ctx - Domain context of the time plugin.
|
|
1350
|
+
* @param g - The governor.
|
|
1351
|
+
* @param at - The page time of the sample.
|
|
1352
|
+
* @returns True when the rate should drop one step.
|
|
1353
|
+
*/
|
|
1354
|
+
function shouldDrop(ctx, g, at) {
|
|
1355
|
+
if (!isLate(g)) {
|
|
1356
|
+
g.lateSince = NaN;
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
if (Number.isNaN(g.lateSince)) g.lateSince = at;
|
|
1360
|
+
const { reason } = ctx.state.rate;
|
|
1361
|
+
const afterRuleStep = (reason === "missed" || reason === "headroom") && at - g.switchedAt < 3e3;
|
|
1362
|
+
const heldLate = at - g.lateSince >= DROP_HOLD_MS;
|
|
1363
|
+
return afterRuleStep || heldLate;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1338
1366
|
* Tells whether the late frames of the drop window are GPU-bound: fewer than half of them kept the main thread longer
|
|
1339
1367
|
* than their frame.
|
|
1340
1368
|
*
|
|
@@ -1357,8 +1385,8 @@ function canReturn(g, at, scaleStep) {
|
|
|
1357
1385
|
return thermalRank(g.thermal) < HEAT_LIMIT_RANK && at - g.switchedAt >= (scaleStep ? 2 * g.dwellMs : g.dwellMs) && g.returnDrawn >= 90 && g.returnMissed <= 1 && (scaleStep || g.returnHeavy <= Math.floor(.1 * g.returnDrawn));
|
|
1358
1386
|
}
|
|
1359
1387
|
/**
|
|
1360
|
-
* Decides on the windows after a sample. Down one step when late intervals reach `max(8, 10 %)` of the second's
|
|
1361
|
-
* render scale when the window is GPU-bound and a lower scale is allowed, the rate otherwise. Up one step in the
|
|
1388
|
+
* Decides on the windows after a sample. Down one step when late intervals reach `max(8, 10 %)` of the second's
|
|
1389
|
+
* for 2 s in a row (at once within 3 s of a step of the rule): the render scale when the window is GPU-bound and a lower scale is allowed, the rate otherwise. Up one step in the
|
|
1362
1390
|
* reverse of the way down, with 90 steady drawn frames in three seconds, at most one late and no heat: the rate first,
|
|
1363
1391
|
* at the scale it dropped at, after the dwell and with a tenth heavy at most, then the scale after twice the dwell. A
|
|
1364
1392
|
* probe of the higher rate so never starts at full scale, and its failure changes no resolution.
|
|
@@ -1369,13 +1397,14 @@ function canReturn(g, at, scaleStep) {
|
|
|
1369
1397
|
*/
|
|
1370
1398
|
function decide(ctx, g, at) {
|
|
1371
1399
|
const { fps, scale } = ctx.state.rate;
|
|
1372
|
-
if (shouldDrop(g)) {
|
|
1400
|
+
if (shouldDrop(ctx, g, at)) {
|
|
1373
1401
|
const smaller = gpuBound(g) ? stepBelow(g.scales, scale) : void 0;
|
|
1374
1402
|
const lower = stepBelow(g.ladder, fps);
|
|
1375
1403
|
if (smaller !== void 0) switchRate(ctx, fps, smaller, "auto", "missed", at);
|
|
1376
1404
|
else if (lower !== void 0) switchRate(ctx, lower, scale, "auto", "missed", at);
|
|
1377
1405
|
return;
|
|
1378
1406
|
}
|
|
1407
|
+
if (!Number.isNaN(g.lateSince)) return;
|
|
1379
1408
|
const higher = stepAbove(g.ladder, fps);
|
|
1380
1409
|
if (higher !== void 0 && canReturn(g, at, false)) {
|
|
1381
1410
|
switchRate(ctx, higher, scale, "auto", "headroom", at);
|
|
@@ -1650,12 +1679,12 @@ function runFrameWork(ctx, at) {
|
|
|
1650
1679
|
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__) {
|
|
1651
1680
|
const { measure } = ctx.state;
|
|
1652
1681
|
if (measure !== void 0 && measure.listeners.length > 0) {
|
|
1653
|
-
runMeasuredFrame(ctx, measure, at ?? pageNow
|
|
1682
|
+
runMeasuredFrame(ctx, measure, at ?? pageNow(), at === void 0);
|
|
1654
1683
|
return measure.record.workMs;
|
|
1655
1684
|
}
|
|
1656
1685
|
}
|
|
1657
1686
|
runPhases(ctx);
|
|
1658
|
-
return at !== void 0 && ctx.state.rate.governor !== void 0 ? pageNow
|
|
1687
|
+
return at !== void 0 && ctx.state.rate.governor !== void 0 ? pageNow() - at : 0;
|
|
1659
1688
|
}
|
|
1660
1689
|
/**
|
|
1661
1690
|
* Runs exactly one frame: advances `Time` by the scaled delta, then calls the six phases.
|
|
@@ -1849,7 +1878,7 @@ function createTimeApi(ctx) {
|
|
|
1849
1878
|
}
|
|
1850
1879
|
return noMeasure;
|
|
1851
1880
|
},
|
|
1852
|
-
agentClock: () => typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ ? pageNow
|
|
1881
|
+
agentClock: () => typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ ? pageNow : void 0,
|
|
1853
1882
|
chargeAgent: (ms) => {
|
|
1854
1883
|
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__) {
|
|
1855
1884
|
const { measure } = ctx.state;
|
|
@@ -3050,7 +3079,7 @@ async function effectValue(state, log, descriptor, signal) {
|
|
|
3050
3079
|
* @param work - The promise of the running effect.
|
|
3051
3080
|
* @returns A promise that settles in the next `signals` phase after `work` settled.
|
|
3052
3081
|
*/
|
|
3053
|
-
function enqueue$
|
|
3082
|
+
function enqueue$2(state, work) {
|
|
3054
3083
|
return new Promise((resolve, reject) => {
|
|
3055
3084
|
const pending = {
|
|
3056
3085
|
ready: false,
|
|
@@ -3191,7 +3220,7 @@ function createFxApi(ctx, deps) {
|
|
|
3191
3220
|
const shown = descriptor.kind === "guide" ? beginGuide(state, signal) : signal;
|
|
3192
3221
|
const work = effectValue(state, ctx.log, descriptor, shown);
|
|
3193
3222
|
if (state.mode === "fast" || !ctx.deps.time.isRunning()) return work;
|
|
3194
|
-
return enqueue$
|
|
3223
|
+
return enqueue$2(state, work);
|
|
3195
3224
|
},
|
|
3196
3225
|
buffer: (item) => {
|
|
3197
3226
|
if (state.mode === "fast") return;
|
|
@@ -5507,7 +5536,7 @@ function startDeadline(timeoutMs) {
|
|
|
5507
5536
|
* @param running - The promise of `run()`.
|
|
5508
5537
|
* @returns `true` when the loop settled, `false` when the deadline won and the loop still runs.
|
|
5509
5538
|
*/
|
|
5510
|
-
async function settle$
|
|
5539
|
+
async function settle$3(ctx, running) {
|
|
5511
5540
|
const quiet = running.then(noop, noop);
|
|
5512
5541
|
const deadline = startDeadline(ctx.config.settleTimeoutMs);
|
|
5513
5542
|
try {
|
|
@@ -5536,7 +5565,7 @@ async function stopRunner(ctx) {
|
|
|
5536
5565
|
stop.abort("stop");
|
|
5537
5566
|
state.abort = stop;
|
|
5538
5567
|
}
|
|
5539
|
-
const settled = running === void 0 || await settle$
|
|
5568
|
+
const settled = running === void 0 || await settle$3(ctx, running);
|
|
5540
5569
|
state.running = void 0;
|
|
5541
5570
|
ctx.state.gate.held = void 0;
|
|
5542
5571
|
if (settled) state.abort = void 0;
|
|
@@ -14939,6 +14968,11 @@ function createHostApi(ctx, deps) {
|
|
|
14939
14968
|
});
|
|
14940
14969
|
return true;
|
|
14941
14970
|
},
|
|
14971
|
+
initSource: (source) => {
|
|
14972
|
+
const app = state.app;
|
|
14973
|
+
if (!state.ready || app === void 0) return;
|
|
14974
|
+
app.renderer.texture.initSource(source);
|
|
14975
|
+
},
|
|
14942
14976
|
clearsCanvas: () => state.ready && state.app?.renderer.background.clearBeforeRender === true,
|
|
14943
14977
|
setResolution: (value, reason) => askResolution(state, value, reason),
|
|
14944
14978
|
overrideResolution: (value) => {
|
|
@@ -15058,7 +15092,10 @@ const KEY = {
|
|
|
15058
15092
|
qc: 25,
|
|
15059
15093
|
qd: 26,
|
|
15060
15094
|
qtx: 27,
|
|
15061
|
-
qty: 28
|
|
15095
|
+
qty: 28,
|
|
15096
|
+
/** The canvas size at the take, CSS px: a picture cut to it is taken again when it changes (F53). */
|
|
15097
|
+
sw: 29,
|
|
15098
|
+
sh: 30
|
|
15062
15099
|
};
|
|
15063
15100
|
/** A whole number of texels within this much (P2.5). */
|
|
15064
15101
|
const ON_GRID = 1 / 64;
|
|
@@ -15123,6 +15160,27 @@ function gridSpan(start, end, resolution) {
|
|
|
15123
15160
|
};
|
|
15124
15161
|
}
|
|
15125
15162
|
/**
|
|
15163
|
+
* A span on the canvas cut to the canvas and the padding around it (F53). Pixi's live filter pass cuts its target to
|
|
15164
|
+
* the viewport and pads it after, so it draws what lies within the padding of the canvas too: a glow just off the
|
|
15165
|
+
* screen still reaches in. A span wholly outside comes back empty: its end is not after its start.
|
|
15166
|
+
*
|
|
15167
|
+
* @param start - The span's start on the canvas, CSS px.
|
|
15168
|
+
* @param end - Its end.
|
|
15169
|
+
* @param size - The canvas size on that axis, CSS px.
|
|
15170
|
+
* @param padding - The padding of the filters, CSS px.
|
|
15171
|
+
* @returns The part of the span kept.
|
|
15172
|
+
* @example
|
|
15173
|
+
* ```ts
|
|
15174
|
+
* cutSpan(-40, 120, 100, 10); // { start: -10, end: 110 }
|
|
15175
|
+
* ```
|
|
15176
|
+
*/
|
|
15177
|
+
function cutSpan(start, end, size, padding) {
|
|
15178
|
+
return {
|
|
15179
|
+
start: Math.max(start, -padding),
|
|
15180
|
+
end: Math.min(end, size + padding)
|
|
15181
|
+
};
|
|
15182
|
+
}
|
|
15183
|
+
/**
|
|
15126
15184
|
* The padding of a stack on the grid of its resolution: what stage 3 adds on each side of the subtree picture, so the
|
|
15127
15185
|
* picture's bounds stay on that grid inside the output texture.
|
|
15128
15186
|
*
|
|
@@ -15925,6 +15983,123 @@ function flushRanges(kctx) {
|
|
|
15925
15983
|
}
|
|
15926
15984
|
}
|
|
15927
15985
|
//#endregion
|
|
15986
|
+
//#region src/plugins/renderer/kept/drawn-bounds.ts
|
|
15987
|
+
/** Every flag of a node's display status: visible, renderable, not culled (Pixi's `localDisplayStatus`). */
|
|
15988
|
+
const SHOWN = 7;
|
|
15989
|
+
/** The effects of a node that holds none. */
|
|
15990
|
+
const NO_EFFECTS = [];
|
|
15991
|
+
/** The matrix of each depth of the walk, from the top's space to the node's. Grown once, then reused. */
|
|
15992
|
+
const MATRICES = [];
|
|
15993
|
+
/** The bounds a masked node's subtree is gathered in, one per depth. Grown once, then reused. */
|
|
15994
|
+
const MASKED = [];
|
|
15995
|
+
/** The bounds the last walk answered, made once. */
|
|
15996
|
+
const RESULT = [];
|
|
15997
|
+
/**
|
|
15998
|
+
* The matrix of a depth, made the first time the walk reaches it.
|
|
15999
|
+
*
|
|
16000
|
+
* @param pixi - The module that draws.
|
|
16001
|
+
* @param depth - The depth under the top.
|
|
16002
|
+
* @returns The matrix.
|
|
16003
|
+
*/
|
|
16004
|
+
function matrixAt(pixi, depth) {
|
|
16005
|
+
const known = MATRICES[depth];
|
|
16006
|
+
if (known !== void 0) return known;
|
|
16007
|
+
const made = new pixi.Matrix();
|
|
16008
|
+
MATRICES[depth] = made;
|
|
16009
|
+
return made;
|
|
16010
|
+
}
|
|
16011
|
+
/**
|
|
16012
|
+
* The bounds a masked node of a depth gathers its subtree in, made the first time the walk needs it.
|
|
16013
|
+
*
|
|
16014
|
+
* @param pixi - The module that draws.
|
|
16015
|
+
* @param depth - The depth under the top.
|
|
16016
|
+
* @returns The bounds, cleared.
|
|
16017
|
+
*/
|
|
16018
|
+
function maskedAt(pixi, depth) {
|
|
16019
|
+
const known = MASKED[depth];
|
|
16020
|
+
if (known !== void 0) return known.clear();
|
|
16021
|
+
const made = new pixi.Bounds();
|
|
16022
|
+
MASKED[depth] = made;
|
|
16023
|
+
return made;
|
|
16024
|
+
}
|
|
16025
|
+
/**
|
|
16026
|
+
* Tells whether Pixi skips a node and its subtree: hidden, not renderable, culled, or not measured (a mask).
|
|
16027
|
+
*
|
|
16028
|
+
* @param node - A node under the top.
|
|
16029
|
+
* @returns True when it adds nothing.
|
|
16030
|
+
*/
|
|
16031
|
+
function skipped(node) {
|
|
16032
|
+
return node.localDisplayStatus !== SHOWN || !node.measurable;
|
|
16033
|
+
}
|
|
16034
|
+
/**
|
|
16035
|
+
* Adds what a node draws itself and what its children draw to the bounds, under the node's matrix.
|
|
16036
|
+
*
|
|
16037
|
+
* @param pixi - The module that draws.
|
|
16038
|
+
* @param node - The node, shown.
|
|
16039
|
+
* @param into - The bounds so far.
|
|
16040
|
+
* @param depth - Its depth under the top.
|
|
16041
|
+
* @param top - The top.
|
|
16042
|
+
*/
|
|
16043
|
+
function addContent(pixi, node, into, depth, top) {
|
|
16044
|
+
const matrix = matrixAt(pixi, depth);
|
|
16045
|
+
if (node.boundsArea) {
|
|
16046
|
+
into.addRect(node.boundsArea, matrix);
|
|
16047
|
+
return;
|
|
16048
|
+
}
|
|
16049
|
+
if (node.renderPipeId !== void 0) {
|
|
16050
|
+
const own = node.bounds;
|
|
16051
|
+
into.addFrame(own.minX, own.minY, own.maxX, own.maxY, matrix);
|
|
16052
|
+
}
|
|
16053
|
+
const children = node.children;
|
|
16054
|
+
for (let index = 0; index < children.length; index += 1) {
|
|
16055
|
+
const child = children[index];
|
|
16056
|
+
if (child !== void 0) addDrawn$1(pixi, child, into, depth + 1, top);
|
|
16057
|
+
}
|
|
16058
|
+
}
|
|
16059
|
+
/**
|
|
16060
|
+
* Adds what one node and its subtree draw to the bounds, in the top's space. The top itself is never skipped: it is
|
|
16061
|
+
* the root of the picture's render, drawn whatever its own flags say. A masked node gathers its subtree apart, then
|
|
16062
|
+
* the mask cuts it.
|
|
16063
|
+
*
|
|
16064
|
+
* @param pixi - The module that draws.
|
|
16065
|
+
* @param node - The node.
|
|
16066
|
+
* @param into - The bounds so far.
|
|
16067
|
+
* @param depth - Its depth under the top, 0 for the top.
|
|
16068
|
+
* @param top - The top: the space of the bounds, and the root a mask is measured from.
|
|
16069
|
+
*/
|
|
16070
|
+
function addDrawn$1(pixi, node, into, depth, top) {
|
|
16071
|
+
if (depth > 0 && skipped(node)) return;
|
|
16072
|
+
const matrix = matrixAt(pixi, depth);
|
|
16073
|
+
if (depth === 0) matrix.identity();
|
|
16074
|
+
else {
|
|
16075
|
+
node.updateLocalTransform();
|
|
16076
|
+
matrix.appendFrom(node.localTransform, matrixAt(pixi, depth - 1));
|
|
16077
|
+
}
|
|
16078
|
+
const effects = node.effects ?? NO_EFFECTS;
|
|
16079
|
+
if (effects.length === 0) {
|
|
16080
|
+
addContent(pixi, node, into, depth, top);
|
|
16081
|
+
return;
|
|
16082
|
+
}
|
|
16083
|
+
const masked = maskedAt(pixi, depth);
|
|
16084
|
+
addContent(pixi, node, masked, depth, top);
|
|
16085
|
+
for (const effect of effects) effect.addLocalBounds?.(masked, top);
|
|
16086
|
+
if (masked.isValid) into.addFrame(masked.minX, masked.minY, masked.maxX, masked.maxY);
|
|
16087
|
+
}
|
|
16088
|
+
/**
|
|
16089
|
+
* The bounds of what a subtree draws, in its top's own space: no node Pixi skips, masks applied.
|
|
16090
|
+
*
|
|
16091
|
+
* @param pixi - The module that draws.
|
|
16092
|
+
* @param top - The top of the subtree.
|
|
16093
|
+
* @returns The bounds, one object the next walk writes again; empty (`isValid` false) when nothing is drawn.
|
|
16094
|
+
*/
|
|
16095
|
+
function drawnBounds(pixi, top) {
|
|
16096
|
+
const into = RESULT[0] ?? new pixi.Bounds();
|
|
16097
|
+
RESULT[0] = into;
|
|
16098
|
+
into.clear();
|
|
16099
|
+
addDrawn$1(pixi, top, into, 0, top);
|
|
16100
|
+
return into;
|
|
16101
|
+
}
|
|
16102
|
+
//#endregion
|
|
15928
16103
|
//#region src/plugins/renderer/kept/state.ts
|
|
15929
16104
|
/**
|
|
15930
16105
|
* @file renderer/kept — the state of the kept-picture cache, and its stop from state alone.
|
|
@@ -16081,6 +16256,7 @@ const NOW = {
|
|
|
16081
16256
|
enabled: 0,
|
|
16082
16257
|
rs: 1,
|
|
16083
16258
|
P: 0,
|
|
16259
|
+
alpha: NaN,
|
|
16084
16260
|
r: 1,
|
|
16085
16261
|
a: 1,
|
|
16086
16262
|
b: 0,
|
|
@@ -16109,27 +16285,42 @@ function resolutionOf$1(filter, resolution) {
|
|
|
16109
16285
|
return own === "inherit" ? resolution : own;
|
|
16110
16286
|
}
|
|
16111
16287
|
/**
|
|
16112
|
-
* Reads what the enabled filters of a stack draw with: how many, the smallest resolution,
|
|
16113
|
-
* pads by (whole pixels)
|
|
16288
|
+
* Reads what the enabled filters of a stack draw with: how many, the smallest resolution, the padding sum Pixi
|
|
16289
|
+
* pads by (whole pixels), and the alpha they multiply by while every one is an `AlphaFilter`.
|
|
16114
16290
|
*
|
|
16115
16291
|
* @param slots - The record's slots.
|
|
16116
16292
|
* @param resolution - The renderer resolution.
|
|
16293
|
+
* @param alphaFilter - Pixi's `AlphaFilter` class.
|
|
16117
16294
|
* @param into - Where the numbers go.
|
|
16118
16295
|
*/
|
|
16119
|
-
function readStack(slots, resolution, into) {
|
|
16296
|
+
function readStack(slots, resolution, alphaFilter, into) {
|
|
16120
16297
|
let enabled = 0;
|
|
16121
16298
|
let rs = Number.POSITIVE_INFINITY;
|
|
16122
16299
|
let padding = 0;
|
|
16300
|
+
let alpha = 1;
|
|
16123
16301
|
for (let index = 0; index < slots.length; index += 1) {
|
|
16124
16302
|
const filter = slots[index]?.filter;
|
|
16125
16303
|
if (filter === void 0 || !filter.enabled) continue;
|
|
16126
16304
|
enabled += 1;
|
|
16127
16305
|
rs = Math.min(rs, resolutionOf$1(filter, resolution));
|
|
16128
16306
|
padding += filter.padding;
|
|
16307
|
+
alpha *= filter instanceof alphaFilter ? filter.alpha : NaN;
|
|
16129
16308
|
}
|
|
16130
16309
|
into.enabled = enabled;
|
|
16131
16310
|
into.rs = enabled === 0 ? resolution : rs;
|
|
16132
16311
|
into.P = Math.trunc(padding);
|
|
16312
|
+
into.alpha = alpha;
|
|
16313
|
+
}
|
|
16314
|
+
/**
|
|
16315
|
+
* Tells whether a stack draws as its subtree picture at an alpha (F54): every enabled filter an `AlphaFilter`, no
|
|
16316
|
+
* padding, at the renderer resolution. The quad then draws the picture itself at that alpha, as Pixi's alpha pass
|
|
16317
|
+
* draws its input: no stage 3, no pass when the alpha changes.
|
|
16318
|
+
*
|
|
16319
|
+
* @param now - This frame's numbers.
|
|
16320
|
+
* @returns True when it does.
|
|
16321
|
+
*/
|
|
16322
|
+
function drawsDirect(now) {
|
|
16323
|
+
return !Number.isNaN(now.alpha) && now.P === 0 && now.rs === now.r;
|
|
16133
16324
|
}
|
|
16134
16325
|
/**
|
|
16135
16326
|
* Tells whether a blend mode draws over what is under it as a picture does.
|
|
@@ -16283,7 +16474,7 @@ function holdsDisplay(kctx, top) {
|
|
|
16283
16474
|
*/
|
|
16284
16475
|
function createRecord$1(kctx, pixi, entity, view, slots, category) {
|
|
16285
16476
|
const top = view.wrapper ?? view.object;
|
|
16286
|
-
const key = (/* @__PURE__ */ new Float64Array(
|
|
16477
|
+
const key = (/* @__PURE__ */ new Float64Array(31)).fill(NaN);
|
|
16287
16478
|
const record = {
|
|
16288
16479
|
entity,
|
|
16289
16480
|
top,
|
|
@@ -16316,6 +16507,8 @@ function createRecord$1(kctx, pixi, entity, view, slots, category) {
|
|
|
16316
16507
|
bytes: 0,
|
|
16317
16508
|
settle: false,
|
|
16318
16509
|
refused: 0,
|
|
16510
|
+
cut: false,
|
|
16511
|
+
direct: false,
|
|
16319
16512
|
displays: void 0
|
|
16320
16513
|
};
|
|
16321
16514
|
const { state } = kctx;
|
|
@@ -16441,7 +16634,8 @@ function linearDiffers(key, now, base, rsAt) {
|
|
|
16441
16634
|
*/
|
|
16442
16635
|
function neededBytes(record, now, box) {
|
|
16443
16636
|
const { w1, h1, Ps } = box;
|
|
16444
|
-
let bytes = textureBytes(w1, h1, now.rs)
|
|
16637
|
+
let bytes = textureBytes(w1, h1, now.rs);
|
|
16638
|
+
if (!drawsDirect(now)) bytes += textureBytes(w1 + 2 * Ps, h1 + 2 * Ps, now.r);
|
|
16445
16639
|
const first = firstInputs(record.slots);
|
|
16446
16640
|
if (first !== void 0) for (let index = 0; index < first.inputs.count; index += 1) {
|
|
16447
16641
|
const scale = first.inputs.scaleOf(index) ?? 0;
|
|
@@ -16508,14 +16702,22 @@ function writeTakeKeys(record, now, box) {
|
|
|
16508
16702
|
function takePicture(kctx, pixi, matrices, record, view, now) {
|
|
16509
16703
|
const { top } = record;
|
|
16510
16704
|
if (!kctx.ctx.config.onDemand && holdsDisplay(kctx, top)) return false;
|
|
16511
|
-
const
|
|
16705
|
+
const drawn = drawnBounds(pixi, top);
|
|
16706
|
+
if (!drawn.isValid) return false;
|
|
16707
|
+
const local = clippedToArea(drawn, top.filterArea);
|
|
16512
16708
|
if (isEmptyBounds(local)) return false;
|
|
16513
16709
|
const box = linearBounds(now.a, now.b, now.c, now.d, local);
|
|
16514
|
-
const
|
|
16515
|
-
const
|
|
16710
|
+
const screen = kctx.host.screen();
|
|
16711
|
+
const width = screen?.width ?? Number.POSITIVE_INFINITY;
|
|
16712
|
+
const height = screen?.height ?? Number.POSITIVE_INFINITY;
|
|
16713
|
+
const Ps = paddingOnGrid(now.P, now.rs);
|
|
16714
|
+
const across = cutSpan(now.tx + box.x0, now.tx + box.x1, width, Ps);
|
|
16715
|
+
const down = cutSpan(now.ty + box.y0, now.ty + box.y1, height, Ps);
|
|
16716
|
+
if (isEmptySpan(across) || isEmptySpan(down)) return false;
|
|
16717
|
+
const x = gridSpan(across.start, across.end, now.rs);
|
|
16718
|
+
const y = gridSpan(down.start, down.end, now.rs);
|
|
16516
16719
|
const w1 = x.texels / now.rs;
|
|
16517
16720
|
const h1 = y.texels / now.rs;
|
|
16518
|
-
const Ps = paddingOnGrid(now.P, now.rs);
|
|
16519
16721
|
if (!fitsTexture(w1, h1, now.rs) || !fitsTexture(w1 + 2 * Ps, h1 + 2 * Ps, now.r)) return false;
|
|
16520
16722
|
const needed = neededBytes(record, now, {
|
|
16521
16723
|
w1,
|
|
@@ -16528,6 +16730,11 @@ function takePicture(kctx, pixi, matrices, record, view, now) {
|
|
|
16528
16730
|
return false;
|
|
16529
16731
|
}
|
|
16530
16732
|
record.refused = 0;
|
|
16733
|
+
const cutAcross = across.start > now.tx + box.x0 || across.end < now.tx + box.x1;
|
|
16734
|
+
const cutDown = down.start > now.ty + box.y0 || down.end < now.ty + box.y1;
|
|
16735
|
+
record.cut = cutAcross || cutDown;
|
|
16736
|
+
record.key[KEY.sw] = width;
|
|
16737
|
+
record.key[KEY.sh] = height;
|
|
16531
16738
|
writeTakeKeys(record, now, {
|
|
16532
16739
|
x,
|
|
16533
16740
|
y,
|
|
@@ -16540,6 +16747,21 @@ function takePicture(kctx, pixi, matrices, record, view, now) {
|
|
|
16540
16747
|
return ok;
|
|
16541
16748
|
}
|
|
16542
16749
|
/**
|
|
16750
|
+
* Tells whether a span holds nothing: its end is not after its start. Pure.
|
|
16751
|
+
*
|
|
16752
|
+
* @param span - The span.
|
|
16753
|
+
* @param span.start - Its start.
|
|
16754
|
+
* @param span.end - Its end.
|
|
16755
|
+
* @returns True when it is empty.
|
|
16756
|
+
* @example
|
|
16757
|
+
* ```ts
|
|
16758
|
+
* isEmptySpan({ start: -10, end: -10 }); // true
|
|
16759
|
+
* ```
|
|
16760
|
+
*/
|
|
16761
|
+
function isEmptySpan(span) {
|
|
16762
|
+
return span.end <= span.start;
|
|
16763
|
+
}
|
|
16764
|
+
/**
|
|
16543
16765
|
* The local bounds of a top cut to its filter area, both in the top's own units: its filters draw nothing outside
|
|
16544
16766
|
* the area. With no area the bounds are returned as they are. Pure.
|
|
16545
16767
|
*
|
|
@@ -16591,6 +16813,13 @@ function renderStages(kctx, pixi, matrices, record, view, now) {
|
|
|
16591
16813
|
if (live) handFilters(kctx, pixi, record, view);
|
|
16592
16814
|
const first = firstInputs(record.slots);
|
|
16593
16815
|
let ok = renderStage1(kctx, pixi, matrices, record);
|
|
16816
|
+
if (drawsDirect(now)) {
|
|
16817
|
+
goDirect(kctx, record);
|
|
16818
|
+
account(kctx, record);
|
|
16819
|
+
if (!ok && live) putFiltersBack(kctx, record, view);
|
|
16820
|
+
return ok;
|
|
16821
|
+
}
|
|
16822
|
+
record.direct = false;
|
|
16594
16823
|
if (ok && first !== void 0) renderStage2(kctx, pixi, matrices, record, {
|
|
16595
16824
|
inputs: first.inputs,
|
|
16596
16825
|
resolution: now.r,
|
|
@@ -16599,13 +16828,36 @@ function renderStages(kctx, pixi, matrices, record, view, now) {
|
|
|
16599
16828
|
else if (record.s2Of !== void 0) releaseInputs(kctx, record);
|
|
16600
16829
|
ok = ok && renderStage3(kctx, pixi, matrices, record, now);
|
|
16601
16830
|
account(kctx, record);
|
|
16602
|
-
if (!ok && live)
|
|
16603
|
-
if (record.standIn !== void 0) record.standIn.filters = null;
|
|
16604
|
-
kctx.sync.applyFilters(view, filtersOf$1(kctx.ctx.state.sync, record.entity), noMark$1, record.top);
|
|
16605
|
-
}
|
|
16831
|
+
if (!ok && live) putFiltersBack(kctx, record, view);
|
|
16606
16832
|
return ok;
|
|
16607
16833
|
}
|
|
16608
16834
|
/**
|
|
16835
|
+
* Puts the filters of a live record whose take failed back on its top: it draws live again.
|
|
16836
|
+
*
|
|
16837
|
+
* @param kctx - The kept context.
|
|
16838
|
+
* @param record - The record.
|
|
16839
|
+
* @param view - Its view.
|
|
16840
|
+
*/
|
|
16841
|
+
function putFiltersBack(kctx, record, view) {
|
|
16842
|
+
if (record.standIn !== void 0) record.standIn.filters = null;
|
|
16843
|
+
kctx.sync.applyFilters(view, filtersOf$1(kctx.ctx.state.sync, record.entity), noMark$1, record.top);
|
|
16844
|
+
}
|
|
16845
|
+
/**
|
|
16846
|
+
* Lets a record draw its subtree picture itself (F54): the pre-pass results and the filter output go, and the quad
|
|
16847
|
+
* shows the picture at the stack's alpha from now on.
|
|
16848
|
+
*
|
|
16849
|
+
* @param kctx - The kept context.
|
|
16850
|
+
* @param record - The record.
|
|
16851
|
+
*/
|
|
16852
|
+
function goDirect(kctx, record) {
|
|
16853
|
+
if (record.s2Of !== void 0) releaseInputs(kctx, record);
|
|
16854
|
+
if (record.s3 !== void 0) {
|
|
16855
|
+
dropTexture(kctx, record.s3);
|
|
16856
|
+
record.s3 = void 0;
|
|
16857
|
+
}
|
|
16858
|
+
record.direct = true;
|
|
16859
|
+
}
|
|
16860
|
+
/**
|
|
16609
16861
|
* Ends the loan of stage 2 when the first filter with inputs left the stack: the results are destroyed.
|
|
16610
16862
|
*
|
|
16611
16863
|
* @param kctx - The kept context.
|
|
@@ -16641,7 +16893,8 @@ function account(kctx, record) {
|
|
|
16641
16893
|
function placeQuad(sync, pixi, matrices, record, view) {
|
|
16642
16894
|
const { top, key } = record;
|
|
16643
16895
|
const parent = top.parent;
|
|
16644
|
-
const output = record.s3 ?? pixi.Texture.EMPTY;
|
|
16896
|
+
const output = (record.direct ? record.s1 : record.s3) ?? pixi.Texture.EMPTY;
|
|
16897
|
+
const alpha = record.direct ? NOW.alpha : 1;
|
|
16645
16898
|
if (record.quad === void 0 || record.quad.destroyed) {
|
|
16646
16899
|
record.quad = new pixi.Sprite(output);
|
|
16647
16900
|
record.quad.label = `kept:${labelOf$3(top)}`;
|
|
@@ -16655,8 +16908,9 @@ function placeQuad(sync, pixi, matrices, record, view) {
|
|
|
16655
16908
|
}
|
|
16656
16909
|
if (quad.zIndex !== top.zIndex) quad.zIndex = top.zIndex;
|
|
16657
16910
|
if (quad.blendMode !== top.blendMode) quad.blendMode = top.blendMode;
|
|
16911
|
+
if (quad.alpha !== alpha) quad.alpha = alpha;
|
|
16658
16912
|
const inverse = parent.getGlobalTransform(matrices.parent, false).invert();
|
|
16659
|
-
const Ps = key[KEY.Ps] ?? 0;
|
|
16913
|
+
const Ps = record.direct ? 0 : key[KEY.Ps] ?? 0;
|
|
16660
16914
|
const x = (key[KEY.X0] ?? 0) - Ps + NOW.tx - (key[KEY.tx] ?? 0);
|
|
16661
16915
|
const y = (key[KEY.Y0] ?? 0) - Ps + NOW.ty - (key[KEY.ty] ?? 0);
|
|
16662
16916
|
const { local } = matrices;
|
|
@@ -16800,6 +17054,17 @@ function noteMove(kctx, record) {
|
|
|
16800
17054
|
if (record.settle) kctx.demand.request();
|
|
16801
17055
|
}
|
|
16802
17056
|
/**
|
|
17057
|
+
* Tells whether the canvas has another size than at the take.
|
|
17058
|
+
*
|
|
17059
|
+
* @param kctx - The kept context.
|
|
17060
|
+
* @param key - The record's key.
|
|
17061
|
+
* @returns True when it does.
|
|
17062
|
+
*/
|
|
17063
|
+
function canvasChanged(kctx, key) {
|
|
17064
|
+
const screen = kctx.host.screen();
|
|
17065
|
+
return screen !== void 0 && (screen.width !== key[KEY.sw] || screen.height !== key[KEY.sh]);
|
|
17066
|
+
}
|
|
17067
|
+
/**
|
|
16803
17068
|
* A kept record's frame (P4.3): a stage-1 change retakes (three in a row go live and calm); otherwise a new chain
|
|
16804
17069
|
* version redoes stages 2 and 3, a filter value, the resolution, the padding or (placed) a move stage 3; at rest after
|
|
16805
17070
|
* an off-grid move one settle retake is owed.
|
|
@@ -16816,8 +17081,12 @@ function stepKept$1(kctx, pixi, matrices, record, view) {
|
|
|
16816
17081
|
churn(kctx, pixi, matrices, record, view);
|
|
16817
17082
|
return;
|
|
16818
17083
|
}
|
|
16819
|
-
record.churn = 0;
|
|
16820
17084
|
const moved = NOW.tx !== key[KEY.lastTx] || NOW.ty !== key[KEY.lastTy];
|
|
17085
|
+
if (record.cut && (moved || canvasChanged(kctx, key))) {
|
|
17086
|
+
churn(kctx, pixi, matrices, record, view);
|
|
17087
|
+
return;
|
|
17088
|
+
}
|
|
17089
|
+
record.churn = 0;
|
|
16821
17090
|
if (!moved && record.settle) {
|
|
16822
17091
|
retakeOrGoLive(kctx, pixi, matrices, record, view);
|
|
16823
17092
|
return;
|
|
@@ -16835,6 +17104,12 @@ function stepKept$1(kctx, pixi, matrices, record, view) {
|
|
|
16835
17104
|
*/
|
|
16836
17105
|
function refresh$1(kctx, pixi, matrices, record) {
|
|
16837
17106
|
const { key } = record;
|
|
17107
|
+
if (drawsDirect(NOW)) {
|
|
17108
|
+
if (record.direct) return;
|
|
17109
|
+
goDirect(kctx, record);
|
|
17110
|
+
account(kctx, record);
|
|
17111
|
+
return;
|
|
17112
|
+
}
|
|
16838
17113
|
const inputs = firstInputs(record.slots)?.inputs;
|
|
16839
17114
|
const stageTwoOwed = inputs !== void 0 && (record.dirtyS2 || inputs !== record.s2Of || inputs.version() !== record.s2InputsVersion || record.s1Version !== record.s2PictureVersion);
|
|
16840
17115
|
const inputsLeft = inputs === void 0 && record.s2Of !== void 0;
|
|
@@ -16846,6 +17121,7 @@ function refresh$1(kctx, pixi, matrices, record) {
|
|
|
16846
17121
|
padding: NOW.P
|
|
16847
17122
|
});
|
|
16848
17123
|
if (!stageThreeOwed) return;
|
|
17124
|
+
record.direct = false;
|
|
16849
17125
|
renderStage3(kctx, pixi, matrices, record, NOW);
|
|
16850
17126
|
account(kctx, record);
|
|
16851
17127
|
if (!stageTwoOwed) kctx.state.refreshesOpen += 1;
|
|
@@ -16915,7 +17191,7 @@ function stepCalm(kctx, record) {
|
|
|
16915
17191
|
function stepRecord(kctx, pixi, matrices, record, view) {
|
|
16916
17192
|
noteDrawing(kctx, record, BEFORE);
|
|
16917
17193
|
NOW.r = kctx.host.resolution();
|
|
16918
|
-
readStack(record.slots, NOW.r, NOW);
|
|
17194
|
+
readStack(record.slots, NOW.r, pixi.AlphaFilter, NOW);
|
|
16919
17195
|
if (eligible(kctx, record, view, NOW)) stepEligible(kctx, pixi, matrices, record, view);
|
|
16920
17196
|
else stepIneligible(kctx, record, view);
|
|
16921
17197
|
if (drawingChanged(kctx, record, BEFORE)) dirtyAbove(kctx, record);
|
|
@@ -17581,31 +17857,6 @@ function stopDemand(state) {
|
|
|
17581
17857
|
state.verify.reference = void 0;
|
|
17582
17858
|
}
|
|
17583
17859
|
//#endregion
|
|
17584
|
-
//#region src/plugins/renderer/monitor/clock.ts
|
|
17585
|
-
/**
|
|
17586
|
-
* @file renderer/monitor — the one renderer file that reads the page clock; lint L3 ignores it (eslint `ignores`). It
|
|
17587
|
-
* measures the page in agent builds: the agent phase, a line's `t`, an event's time between frames. Never a game's
|
|
17588
|
-
* state, whose time is `clock`.
|
|
17589
|
-
*/
|
|
17590
|
-
/**
|
|
17591
|
-
* Reads `globalThis.performance.now()` at call time, so a test stubs it.
|
|
17592
|
-
*
|
|
17593
|
-
* @returns Page-clock milliseconds.
|
|
17594
|
-
*/
|
|
17595
|
-
function pageNow() {
|
|
17596
|
-
return globalThis.performance.now();
|
|
17597
|
-
}
|
|
17598
|
-
/**
|
|
17599
|
-
* Turns page-clock milliseconds into page epoch milliseconds, `performance.timeOrigin + ms`, rounded to 0.01 ms: the
|
|
17600
|
-
* `t` of every log line.
|
|
17601
|
-
*
|
|
17602
|
-
* @param pageMs - Page-clock milliseconds.
|
|
17603
|
-
* @returns Epoch milliseconds.
|
|
17604
|
-
*/
|
|
17605
|
-
function epochOf(pageMs) {
|
|
17606
|
-
return Math.round((globalThis.performance.timeOrigin + pageMs) * 100) / 100;
|
|
17607
|
-
}
|
|
17608
|
-
//#endregion
|
|
17609
17860
|
//#region src/plugins/renderer/reasons.ts
|
|
17610
17861
|
/**
|
|
17611
17862
|
* @file renderer plugin — the reasons a frame is drawn, in ring order: the monitor stores a reason by its index. A
|
|
@@ -19265,9 +19516,9 @@ function wrapDevice(pc, device) {
|
|
|
19265
19516
|
* @returns The pipeline.
|
|
19266
19517
|
*/
|
|
19267
19518
|
value: function countedPipeline(descriptor) {
|
|
19268
|
-
const started = pageNow();
|
|
19519
|
+
const started = pageNow$1();
|
|
19269
19520
|
const made = createRenderPipeline.call(this, descriptor);
|
|
19270
|
-
recordCompile(sinks, pc.perf.frame, descriptor.label ?? null, pageNow() - started);
|
|
19521
|
+
recordCompile(sinks, pc.perf.frame, descriptor.label ?? null, pageNow$1() - started);
|
|
19271
19522
|
return made;
|
|
19272
19523
|
}
|
|
19273
19524
|
});
|
|
@@ -19282,9 +19533,9 @@ function wrapDevice(pc, device) {
|
|
|
19282
19533
|
* @returns The module.
|
|
19283
19534
|
*/
|
|
19284
19535
|
value: function countedModule(descriptor) {
|
|
19285
|
-
const started = pageNow();
|
|
19536
|
+
const started = pageNow$1();
|
|
19286
19537
|
const made = createShaderModule.call(this, descriptor);
|
|
19287
|
-
recordModule(sinks, pc.perf.frame, descriptor.label ?? null, pageNow() - started);
|
|
19538
|
+
recordModule(sinks, pc.perf.frame, descriptor.label ?? null, pageNow$1() - started);
|
|
19288
19539
|
return made;
|
|
19289
19540
|
}
|
|
19290
19541
|
});
|
|
@@ -19312,9 +19563,9 @@ function wrapGl(pc, gl) {
|
|
|
19312
19563
|
* @param program - The program.
|
|
19313
19564
|
*/
|
|
19314
19565
|
value: function countedLink(program) {
|
|
19315
|
-
const started = pageNow();
|
|
19566
|
+
const started = pageNow$1();
|
|
19316
19567
|
linkProgram.call(this, program);
|
|
19317
|
-
recordCompile(sinks, pc.perf.frame, null, pageNow() - started);
|
|
19568
|
+
recordCompile(sinks, pc.perf.frame, null, pageNow$1() - started);
|
|
19318
19569
|
}
|
|
19319
19570
|
});
|
|
19320
19571
|
return () => {
|
|
@@ -19980,7 +20231,7 @@ function headerLine(pc, thermal) {
|
|
|
19980
20231
|
return {
|
|
19981
20232
|
v: 1,
|
|
19982
20233
|
kind: "header",
|
|
19983
|
-
t: epochOf(pageNow()),
|
|
20234
|
+
t: epochOf(pageNow$1()),
|
|
19984
20235
|
build: buildId(),
|
|
19985
20236
|
timeOrigin: globalThis.performance.timeOrigin,
|
|
19986
20237
|
engine: typeof __MOKU_GAME_ENGINE__ === "string" ? __MOKU_GAME_ENGINE__ : null,
|
|
@@ -20500,7 +20751,7 @@ function eventLine(name, detail, frame) {
|
|
|
20500
20751
|
return {
|
|
20501
20752
|
v: 1,
|
|
20502
20753
|
kind: "event",
|
|
20503
|
-
t: epochOf(pageNow()),
|
|
20754
|
+
t: epochOf(pageNow$1()),
|
|
20504
20755
|
build: buildId(),
|
|
20505
20756
|
frame,
|
|
20506
20757
|
name,
|
|
@@ -20518,7 +20769,7 @@ function markLine(label, auto) {
|
|
|
20518
20769
|
return {
|
|
20519
20770
|
v: 1,
|
|
20520
20771
|
kind: "mark",
|
|
20521
|
-
t: epochOf(pageNow()),
|
|
20772
|
+
t: epochOf(pageNow$1()),
|
|
20522
20773
|
build: buildId(),
|
|
20523
20774
|
label,
|
|
20524
20775
|
auto
|
|
@@ -20535,7 +20786,7 @@ function thermalLine(reading) {
|
|
|
20535
20786
|
return {
|
|
20536
20787
|
v: 1,
|
|
20537
20788
|
kind: "thermal",
|
|
20538
|
-
t: epochOf(pageNow()),
|
|
20789
|
+
t: epochOf(pageNow$1()),
|
|
20539
20790
|
build: buildId(),
|
|
20540
20791
|
status: reading.status,
|
|
20541
20792
|
headroom: reading.headroom,
|
|
@@ -20677,7 +20928,7 @@ function buildStartup(pc) {
|
|
|
20677
20928
|
return {
|
|
20678
20929
|
v: 1,
|
|
20679
20930
|
kind: "startup",
|
|
20680
|
-
t: epochOf(pageNow()),
|
|
20931
|
+
t: epochOf(pageNow$1()),
|
|
20681
20932
|
build: buildId(),
|
|
20682
20933
|
htmlMs: responseEnd === void 0 ? null : roundTo(responseEnd, 2),
|
|
20683
20934
|
script,
|
|
@@ -21104,7 +21355,7 @@ function writeKept(pc, slot) {
|
|
|
21104
21355
|
* @param section - The `COST` index the time goes to.
|
|
21105
21356
|
*/
|
|
21106
21357
|
function lap(perf, section) {
|
|
21107
|
-
const now = pageNow();
|
|
21358
|
+
const now = pageNow$1();
|
|
21108
21359
|
perf.sectionsMs[section] = cellF64(perf.sectionsMs, section) + now - cellF64(perf.staged, 2);
|
|
21109
21360
|
perf.staged[2] = now;
|
|
21110
21361
|
}
|
|
@@ -21496,7 +21747,7 @@ function commitFrame(pc, measure) {
|
|
|
21496
21747
|
const sections = sinks.sections;
|
|
21497
21748
|
const slot = ring.written % ring.capacity;
|
|
21498
21749
|
const previous = ring.size === 0 ? -1 : ring.newest;
|
|
21499
|
-
perf.staged[2] = pageNow();
|
|
21750
|
+
perf.staged[2] = pageNow$1();
|
|
21500
21751
|
stageBudget(perf, measure);
|
|
21501
21752
|
writeMeasure(pc, slot, measure);
|
|
21502
21753
|
writeKept(pc, slot);
|
|
@@ -22116,7 +22367,7 @@ function notePauses(pc, reasons, paused, resumed) {
|
|
|
22116
22367
|
const detail = { reasons: [...reasons] };
|
|
22117
22368
|
if (paused) {
|
|
22118
22369
|
writeEvent(pc, "pause", detail);
|
|
22119
|
-
pc.perf.staged[1] = pageNow();
|
|
22370
|
+
pc.perf.staged[1] = pageNow$1();
|
|
22120
22371
|
flushPerf(pc);
|
|
22121
22372
|
} else if (resumed) {
|
|
22122
22373
|
writeEvent(pc, "resume", detail);
|
|
@@ -22138,7 +22389,7 @@ function noteChange(perf, change) {
|
|
|
22138
22389
|
* @param perf - The perf state.
|
|
22139
22390
|
*/
|
|
22140
22391
|
function beginAgent(perf) {
|
|
22141
|
-
if (Number.isNaN(perf.agentStart)) perf.agentStart = pageNow();
|
|
22392
|
+
if (Number.isNaN(perf.agentStart)) perf.agentStart = pageNow$1();
|
|
22142
22393
|
}
|
|
22143
22394
|
/**
|
|
22144
22395
|
* Closes the open agent span: its time goes into the agent phase of the frame interval it falls in, and to
|
|
@@ -22149,7 +22400,7 @@ function beginAgent(perf) {
|
|
|
22149
22400
|
function endAgent(pc) {
|
|
22150
22401
|
const { perf } = pc;
|
|
22151
22402
|
if (Number.isNaN(perf.agentStart)) return;
|
|
22152
|
-
const span = pageNow() - perf.agentStart;
|
|
22403
|
+
const span = pageNow$1() - perf.agentStart;
|
|
22153
22404
|
perf.agentStart = NaN;
|
|
22154
22405
|
perf.agentMs += span;
|
|
22155
22406
|
pc.ctx.deps.time.chargeAgent(span);
|
|
@@ -22192,7 +22443,7 @@ function attachFirst(pc) {
|
|
|
22192
22443
|
writeEvent(pc, "device-restored", { kind: host.kind() });
|
|
22193
22444
|
});
|
|
22194
22445
|
host.onTrim((reason, freedMb) => {
|
|
22195
|
-
perf.problems.trimAt = pageNow();
|
|
22446
|
+
perf.problems.trimAt = pageNow$1();
|
|
22196
22447
|
writeEvent(pc, "pool-trim", {
|
|
22197
22448
|
reason,
|
|
22198
22449
|
freedMb
|
|
@@ -22534,6 +22785,17 @@ function createMonitorApi(ctx, deps) {
|
|
|
22534
22785
|
}
|
|
22535
22786
|
if (pc === void 0) sinks.inFrame = false;
|
|
22536
22787
|
};
|
|
22788
|
+
/**
|
|
22789
|
+
* The agent span: `beginAgent` and `endAgent`, also the span the captures are taken in.
|
|
22790
|
+
*/
|
|
22791
|
+
const agentSpan = {
|
|
22792
|
+
begin: () => {
|
|
22793
|
+
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) beginAgent(pc.perf);
|
|
22794
|
+
},
|
|
22795
|
+
end: () => {
|
|
22796
|
+
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) endAgent(pc);
|
|
22797
|
+
}
|
|
22798
|
+
};
|
|
22537
22799
|
return {
|
|
22538
22800
|
stats: () => {
|
|
22539
22801
|
const usage = deps.host.textures();
|
|
@@ -22587,12 +22849,8 @@ function createMonitorApi(ctx, deps) {
|
|
|
22587
22849
|
event: (name, detail = NO_DETAIL) => {
|
|
22588
22850
|
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) writeEvent(pc, name, detail);
|
|
22589
22851
|
},
|
|
22590
|
-
beginAgent:
|
|
22591
|
-
|
|
22592
|
-
},
|
|
22593
|
-
endAgent: () => {
|
|
22594
|
-
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) endAgent(pc);
|
|
22595
|
-
},
|
|
22852
|
+
beginAgent: agentSpan.begin,
|
|
22853
|
+
endAgent: agentSpan.end,
|
|
22596
22854
|
sections: (next) => {
|
|
22597
22855
|
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) {
|
|
22598
22856
|
if (next === void 0) return pc.perf.sections;
|
|
@@ -22626,7 +22884,7 @@ function createMonitorApi(ctx, deps) {
|
|
|
22626
22884
|
state.lastDrawn = drawn;
|
|
22627
22885
|
notePasses(drawn);
|
|
22628
22886
|
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) pc.perf.drawn = drawn;
|
|
22629
|
-
if (drawn) serveCaptures(mctx);
|
|
22887
|
+
if (drawn) serveCaptures(mctx, agentSpan);
|
|
22630
22888
|
},
|
|
22631
22889
|
waiting: () => state.captures.length > 0,
|
|
22632
22890
|
changed: (change) => {
|
|
@@ -22647,7 +22905,7 @@ function createMonitorApi(ctx, deps) {
|
|
|
22647
22905
|
},
|
|
22648
22906
|
flush: () => {
|
|
22649
22907
|
if (typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__ && pc !== void 0) {
|
|
22650
|
-
pc.perf.staged[1] = pageNow();
|
|
22908
|
+
pc.perf.staged[1] = pageNow$1();
|
|
22651
22909
|
flushPerf(pc);
|
|
22652
22910
|
}
|
|
22653
22911
|
},
|
|
@@ -23728,7 +23986,7 @@ function variantsOf(sctx) {
|
|
|
23728
23986
|
* @param subject - The subject.
|
|
23729
23987
|
* @param variants - Its variants.
|
|
23730
23988
|
*/
|
|
23731
|
-
function enqueue(warm, subject, variants) {
|
|
23989
|
+
function enqueue$1(warm, subject, variants) {
|
|
23732
23990
|
for (const variant of variants) warm.queue.push({
|
|
23733
23991
|
subject,
|
|
23734
23992
|
variant
|
|
@@ -23770,7 +24028,7 @@ function abandon(warm) {
|
|
|
23770
24028
|
*
|
|
23771
24029
|
* @param sctx - Domain context of the sync module.
|
|
23772
24030
|
*/
|
|
23773
|
-
function startWarm$
|
|
24031
|
+
function startWarm$2(sctx) {
|
|
23774
24032
|
const state = sctx.ctx.state.sync;
|
|
23775
24033
|
const { warm } = state;
|
|
23776
24034
|
dropObjects(warm);
|
|
@@ -23781,12 +24039,12 @@ function startWarm$1(sctx) {
|
|
|
23781
24039
|
warm.failed = false;
|
|
23782
24040
|
if (!canWarm(sctx)) return;
|
|
23783
24041
|
const variants = variantsOf(sctx);
|
|
23784
|
-
for (const subject of CONTENT) enqueue(warm, subject, variants);
|
|
24042
|
+
for (const subject of CONTENT) enqueue$1(warm, subject, variants);
|
|
23785
24043
|
const [font] = state.fonts.keys();
|
|
23786
24044
|
if (font !== void 0) {
|
|
23787
24045
|
warm.textFont = font;
|
|
23788
24046
|
warm.textQueued = true;
|
|
23789
|
-
enqueue(warm, "text", variants);
|
|
24047
|
+
enqueue$1(warm, "text", variants);
|
|
23790
24048
|
}
|
|
23791
24049
|
for (const filter of warm.filters) {
|
|
23792
24050
|
const entry = warm.waiting.get(filter);
|
|
@@ -23795,7 +24053,7 @@ function startWarm$1(sctx) {
|
|
|
23795
24053
|
resolve: []
|
|
23796
24054
|
});
|
|
23797
24055
|
else entry.left = variants.length;
|
|
23798
|
-
enqueue(warm, filter, variants);
|
|
24056
|
+
enqueue$1(warm, filter, variants);
|
|
23799
24057
|
}
|
|
23800
24058
|
sctx.deps.demand.request();
|
|
23801
24059
|
}
|
|
@@ -23843,7 +24101,7 @@ function warmFilter(sctx, filter) {
|
|
|
23843
24101
|
entry.left = variants.length;
|
|
23844
24102
|
warm.filters.add(filter);
|
|
23845
24103
|
warm.waiting.set(filter, entry);
|
|
23846
|
-
enqueue(warm, filter, variants);
|
|
24104
|
+
enqueue$1(warm, filter, variants);
|
|
23847
24105
|
sctx.deps.demand.request();
|
|
23848
24106
|
}
|
|
23849
24107
|
return new Promise((resolve) => {
|
|
@@ -24153,6 +24411,419 @@ function clearFonts(state) {
|
|
|
24153
24411
|
state.fontCache = void 0;
|
|
24154
24412
|
}
|
|
24155
24413
|
//#endregion
|
|
24414
|
+
//#region src/plugins/renderer/sync/outline.ts
|
|
24415
|
+
/** Above every child of the wrapper, whatever `Order` a child carries. */
|
|
24416
|
+
const OUTLINE_Z = Number.MAX_SAFE_INTEGER;
|
|
24417
|
+
const CYAN = 65535;
|
|
24418
|
+
const RED = 16711680;
|
|
24419
|
+
/**
|
|
24420
|
+
* Writes the slice widths a texture asks for, in the units it is drawn in: its default borders divided
|
|
24421
|
+
* by the resolution of its source. A texture without borders gets 0 on every side, never Pixi's 10. Written in place,
|
|
24422
|
+
* so a nine-slice write allocates nothing.
|
|
24423
|
+
*
|
|
24424
|
+
* @param texture - The texture a nine-slice draws, or `undefined` while nothing can draw.
|
|
24425
|
+
* @param out - The widths to write.
|
|
24426
|
+
* @returns `out`.
|
|
24427
|
+
*/
|
|
24428
|
+
function bordersInto(texture, out) {
|
|
24429
|
+
const borders = texture?.defaultBorders;
|
|
24430
|
+
if (texture === void 0 || borders === void 0) {
|
|
24431
|
+
out.left = 0;
|
|
24432
|
+
out.top = 0;
|
|
24433
|
+
out.right = 0;
|
|
24434
|
+
out.bottom = 0;
|
|
24435
|
+
return out;
|
|
24436
|
+
}
|
|
24437
|
+
const resolution = texture.source.resolution > 0 ? texture.source.resolution : 1;
|
|
24438
|
+
out.left = borders.left / resolution;
|
|
24439
|
+
out.top = borders.top / resolution;
|
|
24440
|
+
out.right = borders.right / resolution;
|
|
24441
|
+
out.bottom = borders.bottom / resolution;
|
|
24442
|
+
return out;
|
|
24443
|
+
}
|
|
24444
|
+
/**
|
|
24445
|
+
* The factor Pixi shrinks the corners of a nine-slice by when two of them would overlap.
|
|
24446
|
+
*
|
|
24447
|
+
* @param size - The drawn size.
|
|
24448
|
+
* @param size.width - Drawn width.
|
|
24449
|
+
* @param size.height - Drawn height.
|
|
24450
|
+
* @param borders - The slice widths.
|
|
24451
|
+
* @returns 1 when the corners fit, less when they overlap.
|
|
24452
|
+
* @example
|
|
24453
|
+
* ```ts
|
|
24454
|
+
* sliceScale({ width: 100, height: 200 }, { left: 72, top: 76, right: 72, bottom: 76 }); // 0.6944
|
|
24455
|
+
* ```
|
|
24456
|
+
*/
|
|
24457
|
+
function sliceScale(size, borders) {
|
|
24458
|
+
const across = borders.left + borders.right;
|
|
24459
|
+
const down = borders.top + borders.bottom;
|
|
24460
|
+
const scaleX = across > 0 && size.width < across ? size.width / across : 1;
|
|
24461
|
+
const scaleY = down > 0 && size.height < down ? size.height / down : 1;
|
|
24462
|
+
return Math.min(scaleX, scaleY);
|
|
24463
|
+
}
|
|
24464
|
+
/**
|
|
24465
|
+
* Strokes the bounds of a nine-slice and the four lines Pixi cuts it at, into the wrapper of its
|
|
24466
|
+
* view: cyan, red when the corners overlap or the texture is missing. The graphics is kept on the
|
|
24467
|
+
* view and redrawn in place.
|
|
24468
|
+
*
|
|
24469
|
+
* @param pixi - The Pixi module the renderer loaded.
|
|
24470
|
+
* @param wrapper - The wrapper of the view, which carries its pose.
|
|
24471
|
+
* @param view - The view of the nine-slice.
|
|
24472
|
+
* @param shape - Size, borders and whether the texture is missing.
|
|
24473
|
+
*/
|
|
24474
|
+
function drawOutline(pixi, wrapper, view, shape) {
|
|
24475
|
+
const outline = view.outline ?? new pixi.Graphics();
|
|
24476
|
+
const { width, height, borders } = shape;
|
|
24477
|
+
const scale = sliceScale(shape, borders);
|
|
24478
|
+
const left = borders.left * scale;
|
|
24479
|
+
const right = width - borders.right * scale;
|
|
24480
|
+
const top = borders.top * scale;
|
|
24481
|
+
const bottom = height - borders.bottom * scale;
|
|
24482
|
+
outline.label = `outline#${shape.entity}`;
|
|
24483
|
+
outline.zIndex = OUTLINE_Z;
|
|
24484
|
+
outline.clear();
|
|
24485
|
+
outline.rect(0, 0, width, height).moveTo(left, 0).lineTo(left, height).moveTo(right, 0).lineTo(right, height).moveTo(0, top).lineTo(width, top).moveTo(0, bottom).lineTo(width, bottom);
|
|
24486
|
+
outline.stroke({
|
|
24487
|
+
color: shape.missing || scale < 1 ? RED : CYAN,
|
|
24488
|
+
width: 1,
|
|
24489
|
+
pixelLine: true
|
|
24490
|
+
});
|
|
24491
|
+
if (outline.parent !== wrapper) wrapper.addChild(outline);
|
|
24492
|
+
view.outline = outline;
|
|
24493
|
+
}
|
|
24494
|
+
//#endregion
|
|
24495
|
+
//#region src/plugins/renderer/sync/hollow.ts
|
|
24496
|
+
/** The vertices across and down: the four cut lines of a nine-slice on each axis. */
|
|
24497
|
+
const SIDE = 4;
|
|
24498
|
+
/** The cells of the 3 × 3 slicing on each axis. */
|
|
24499
|
+
const CELLS = 3;
|
|
24500
|
+
/** The cell left out, row by row: the centre, (1, 1). */
|
|
24501
|
+
const CENTRE = 4;
|
|
24502
|
+
/** Indices of one quad: two triangles. */
|
|
24503
|
+
const QUAD = 6;
|
|
24504
|
+
/**
|
|
24505
|
+
* The index buffer of the rim: the two triangles of every cell of the 3 × 3 slicing but the centre, in Pixi's
|
|
24506
|
+
* `PlaneGeometry` winding. Static: written once per geometry.
|
|
24507
|
+
*
|
|
24508
|
+
* @returns 48 indices into the 16 vertices.
|
|
24509
|
+
* @example
|
|
24510
|
+
* ```ts
|
|
24511
|
+
* rimIndices().length; // 48
|
|
24512
|
+
* ```
|
|
24513
|
+
*/
|
|
24514
|
+
function rimIndices() {
|
|
24515
|
+
const indices = /* @__PURE__ */ new Uint32Array(48);
|
|
24516
|
+
let at = 0;
|
|
24517
|
+
for (let cell = 0; cell < 9; cell += 1) {
|
|
24518
|
+
if (cell === CENTRE) continue;
|
|
24519
|
+
const topLeft = Math.floor(cell / CELLS) * SIDE + cell % CELLS;
|
|
24520
|
+
const bottomLeft = topLeft + SIDE;
|
|
24521
|
+
indices.set([
|
|
24522
|
+
topLeft,
|
|
24523
|
+
topLeft + 1,
|
|
24524
|
+
bottomLeft,
|
|
24525
|
+
topLeft + 1,
|
|
24526
|
+
bottomLeft + 1,
|
|
24527
|
+
bottomLeft
|
|
24528
|
+
], at);
|
|
24529
|
+
at += QUAD;
|
|
24530
|
+
}
|
|
24531
|
+
return indices;
|
|
24532
|
+
}
|
|
24533
|
+
/**
|
|
24534
|
+
* Writes one row of the grid: its four vertices, one per cut line across, at the same `y`.
|
|
24535
|
+
*
|
|
24536
|
+
* @param target - The 32 numbers of the grid: x and y of each vertex, row by row.
|
|
24537
|
+
* @param row - The row, 0 to 3.
|
|
24538
|
+
* @param y - The row's line down.
|
|
24539
|
+
* @param across - The cut lines across.
|
|
24540
|
+
*/
|
|
24541
|
+
function writeRow(target, row, y, across) {
|
|
24542
|
+
const at = row * SIDE * 2;
|
|
24543
|
+
target[at] = across.start;
|
|
24544
|
+
target[at + 1] = y;
|
|
24545
|
+
target[at + 2] = across.near;
|
|
24546
|
+
target[at + 3] = y;
|
|
24547
|
+
target[at + 4] = across.far;
|
|
24548
|
+
target[at + 5] = y;
|
|
24549
|
+
target[at + 6] = across.end;
|
|
24550
|
+
target[at + 7] = y;
|
|
24551
|
+
}
|
|
24552
|
+
/**
|
|
24553
|
+
* Writes the 4 × 4 grid of the cut lines in place: Pixi's vertex order, row by row.
|
|
24554
|
+
*
|
|
24555
|
+
* @param target - The 32 numbers of the grid.
|
|
24556
|
+
* @param across - The cut lines across.
|
|
24557
|
+
* @param down - The cut lines down.
|
|
24558
|
+
*/
|
|
24559
|
+
function writeGrid(target, across, down) {
|
|
24560
|
+
writeRow(target, 0, down.start, across);
|
|
24561
|
+
writeRow(target, 1, down.near, across);
|
|
24562
|
+
writeRow(target, 2, down.far, across);
|
|
24563
|
+
writeRow(target, 3, down.end, across);
|
|
24564
|
+
}
|
|
24565
|
+
/**
|
|
24566
|
+
* Makes the class of the rim-only nine-slice over one loaded Pixi module.
|
|
24567
|
+
*
|
|
24568
|
+
* @param pixi - The Pixi module the renderer loaded.
|
|
24569
|
+
* @returns The class.
|
|
24570
|
+
*/
|
|
24571
|
+
function createHollowClass(pixi) {
|
|
24572
|
+
/** A nine-slice that draws its rim alone: Pixi's mesh over the 16 vertices of the slicing and its eight border cells. */
|
|
24573
|
+
class HollowNineSlice extends pixi.Mesh {
|
|
24574
|
+
/** Tells the rim-only object from Pixi's `NineSliceSprite`. */
|
|
24575
|
+
hollow = true;
|
|
24576
|
+
/** The slice widths, in the units the panel is drawn in. */
|
|
24577
|
+
borders = {
|
|
24578
|
+
left: 0,
|
|
24579
|
+
top: 0,
|
|
24580
|
+
right: 0,
|
|
24581
|
+
bottom: 0
|
|
24582
|
+
};
|
|
24583
|
+
/** The cut lines across, scratch of the next write. */
|
|
24584
|
+
across = {
|
|
24585
|
+
start: 0,
|
|
24586
|
+
near: 0,
|
|
24587
|
+
far: 0,
|
|
24588
|
+
end: 0
|
|
24589
|
+
};
|
|
24590
|
+
/** The cut lines down, scratch of the next write. */
|
|
24591
|
+
down = {
|
|
24592
|
+
start: 0,
|
|
24593
|
+
near: 0,
|
|
24594
|
+
far: 0,
|
|
24595
|
+
end: 0
|
|
24596
|
+
};
|
|
24597
|
+
/** The panel's width the positions are laid out for. */
|
|
24598
|
+
boxWidth = 0;
|
|
24599
|
+
/** The panel's height the positions are laid out for. */
|
|
24600
|
+
boxHeight = 0;
|
|
24601
|
+
/** False while Pixi's constructor writes the first texture: the geometry does not exist yet. */
|
|
24602
|
+
built = false;
|
|
24603
|
+
/**
|
|
24604
|
+
* Builds the mesh over a geometry of its own, 0 × 0 with no border, its uvs cut for the texture.
|
|
24605
|
+
*
|
|
24606
|
+
* @param texture - The texture it draws first.
|
|
24607
|
+
*/
|
|
24608
|
+
constructor(texture) {
|
|
24609
|
+
super({
|
|
24610
|
+
geometry: new pixi.MeshGeometry({
|
|
24611
|
+
positions: /* @__PURE__ */ new Float32Array(32),
|
|
24612
|
+
uvs: /* @__PURE__ */ new Float32Array(32),
|
|
24613
|
+
indices: rimIndices()
|
|
24614
|
+
}),
|
|
24615
|
+
texture
|
|
24616
|
+
});
|
|
24617
|
+
this.built = true;
|
|
24618
|
+
this.layoutUvs();
|
|
24619
|
+
}
|
|
24620
|
+
/**
|
|
24621
|
+
* The width of the left border column.
|
|
24622
|
+
*
|
|
24623
|
+
* @returns Units.
|
|
24624
|
+
*/
|
|
24625
|
+
get leftWidth() {
|
|
24626
|
+
return this.borders.left;
|
|
24627
|
+
}
|
|
24628
|
+
/**
|
|
24629
|
+
* Sets the width of the left border column: the positions and the uvs are written again.
|
|
24630
|
+
*
|
|
24631
|
+
* @param value - Units.
|
|
24632
|
+
*/
|
|
24633
|
+
set leftWidth(value) {
|
|
24634
|
+
if (this.borders.left === value) return;
|
|
24635
|
+
this.borders.left = value;
|
|
24636
|
+
this.layoutAll();
|
|
24637
|
+
}
|
|
24638
|
+
/**
|
|
24639
|
+
* The height of the top border row.
|
|
24640
|
+
*
|
|
24641
|
+
* @returns Units.
|
|
24642
|
+
*/
|
|
24643
|
+
get topHeight() {
|
|
24644
|
+
return this.borders.top;
|
|
24645
|
+
}
|
|
24646
|
+
/**
|
|
24647
|
+
* Sets the height of the top border row: the positions and the uvs are written again.
|
|
24648
|
+
*
|
|
24649
|
+
* @param value - Units.
|
|
24650
|
+
*/
|
|
24651
|
+
set topHeight(value) {
|
|
24652
|
+
if (this.borders.top === value) return;
|
|
24653
|
+
this.borders.top = value;
|
|
24654
|
+
this.layoutAll();
|
|
24655
|
+
}
|
|
24656
|
+
/**
|
|
24657
|
+
* The width of the right border column.
|
|
24658
|
+
*
|
|
24659
|
+
* @returns Units.
|
|
24660
|
+
*/
|
|
24661
|
+
get rightWidth() {
|
|
24662
|
+
return this.borders.right;
|
|
24663
|
+
}
|
|
24664
|
+
/**
|
|
24665
|
+
* Sets the width of the right border column: the positions and the uvs are written again.
|
|
24666
|
+
*
|
|
24667
|
+
* @param value - Units.
|
|
24668
|
+
*/
|
|
24669
|
+
set rightWidth(value) {
|
|
24670
|
+
if (this.borders.right === value) return;
|
|
24671
|
+
this.borders.right = value;
|
|
24672
|
+
this.layoutAll();
|
|
24673
|
+
}
|
|
24674
|
+
/**
|
|
24675
|
+
* The height of the bottom border row.
|
|
24676
|
+
*
|
|
24677
|
+
* @returns Units.
|
|
24678
|
+
*/
|
|
24679
|
+
get bottomHeight() {
|
|
24680
|
+
return this.borders.bottom;
|
|
24681
|
+
}
|
|
24682
|
+
/**
|
|
24683
|
+
* Sets the height of the bottom border row: the positions and the uvs are written again.
|
|
24684
|
+
*
|
|
24685
|
+
* @param value - Units.
|
|
24686
|
+
*/
|
|
24687
|
+
set bottomHeight(value) {
|
|
24688
|
+
if (this.borders.bottom === value) return;
|
|
24689
|
+
this.borders.bottom = value;
|
|
24690
|
+
this.layoutAll();
|
|
24691
|
+
}
|
|
24692
|
+
/**
|
|
24693
|
+
* The width of the panel, as on a `NineSliceSprite`: its box, never a scale.
|
|
24694
|
+
*
|
|
24695
|
+
* @returns Units.
|
|
24696
|
+
*/
|
|
24697
|
+
get width() {
|
|
24698
|
+
return this.boxWidth;
|
|
24699
|
+
}
|
|
24700
|
+
/**
|
|
24701
|
+
* Sets the width of the panel: the positions are written again.
|
|
24702
|
+
*
|
|
24703
|
+
* @param value - Units.
|
|
24704
|
+
*/
|
|
24705
|
+
set width(value) {
|
|
24706
|
+
if (this.boxWidth === value) return;
|
|
24707
|
+
this.boxWidth = value;
|
|
24708
|
+
this.layoutPositions();
|
|
24709
|
+
}
|
|
24710
|
+
/**
|
|
24711
|
+
* The height of the panel, as on a `NineSliceSprite`: its box, never a scale.
|
|
24712
|
+
*
|
|
24713
|
+
* @returns Units.
|
|
24714
|
+
*/
|
|
24715
|
+
get height() {
|
|
24716
|
+
return this.boxHeight;
|
|
24717
|
+
}
|
|
24718
|
+
/**
|
|
24719
|
+
* Sets the height of the panel: the positions are written again.
|
|
24720
|
+
*
|
|
24721
|
+
* @param value - Units.
|
|
24722
|
+
*/
|
|
24723
|
+
set height(value) {
|
|
24724
|
+
if (this.boxHeight === value) return;
|
|
24725
|
+
this.boxHeight = value;
|
|
24726
|
+
this.layoutPositions();
|
|
24727
|
+
}
|
|
24728
|
+
/**
|
|
24729
|
+
* The texture the rim is cut from.
|
|
24730
|
+
*
|
|
24731
|
+
* @returns The texture.
|
|
24732
|
+
*/
|
|
24733
|
+
get texture() {
|
|
24734
|
+
return super.texture;
|
|
24735
|
+
}
|
|
24736
|
+
/**
|
|
24737
|
+
* Sets the texture as Pixi's mesh does; the uvs are cut for its frame.
|
|
24738
|
+
*
|
|
24739
|
+
* @param value - The texture.
|
|
24740
|
+
*/
|
|
24741
|
+
set texture(value) {
|
|
24742
|
+
super.texture = value;
|
|
24743
|
+
if (this.built) this.layoutUvs();
|
|
24744
|
+
}
|
|
24745
|
+
/**
|
|
24746
|
+
* Frees the mesh as Pixi does, and its own geometry with it.
|
|
24747
|
+
*
|
|
24748
|
+
* @param options - Pixi's destroy options; a texture is freed only when they ask.
|
|
24749
|
+
*/
|
|
24750
|
+
destroy(options) {
|
|
24751
|
+
if (this.destroyed) return;
|
|
24752
|
+
const geometry = this.geometry;
|
|
24753
|
+
super.destroy(options);
|
|
24754
|
+
geometry.destroy(true);
|
|
24755
|
+
}
|
|
24756
|
+
/** Writes the positions and the uvs again: a border changed. */
|
|
24757
|
+
layoutAll() {
|
|
24758
|
+
this.layoutPositions();
|
|
24759
|
+
this.layoutUvs();
|
|
24760
|
+
}
|
|
24761
|
+
/**
|
|
24762
|
+
* Writes the 16 positions in place: the cut lines at the borders, all four shrunk by one factor when two corners
|
|
24763
|
+
* would overlap, as Pixi's `NineSliceGeometry` shrinks them.
|
|
24764
|
+
*/
|
|
24765
|
+
layoutPositions() {
|
|
24766
|
+
const across = this.across;
|
|
24767
|
+
const down = this.down;
|
|
24768
|
+
const borders = this.borders;
|
|
24769
|
+
const scale = sliceScale(this, borders);
|
|
24770
|
+
across.start = 0;
|
|
24771
|
+
across.near = borders.left * scale;
|
|
24772
|
+
across.far = this.boxWidth - borders.right * scale;
|
|
24773
|
+
across.end = this.boxWidth;
|
|
24774
|
+
down.start = 0;
|
|
24775
|
+
down.near = borders.top * scale;
|
|
24776
|
+
down.far = this.boxHeight - borders.bottom * scale;
|
|
24777
|
+
down.end = this.boxHeight;
|
|
24778
|
+
writeGrid(this.geometry.positions, across, down);
|
|
24779
|
+
this.geometry.getBuffer("aPosition").update();
|
|
24780
|
+
}
|
|
24781
|
+
/**
|
|
24782
|
+
* Writes the 16 uvs in place: the texture's frame (its trim, when it has one) cut at the unscaled borders, as
|
|
24783
|
+
* Pixi's `NineSliceGeometry` cuts it. Pixi maps them into an atlas page when it batches the mesh.
|
|
24784
|
+
*/
|
|
24785
|
+
layoutUvs() {
|
|
24786
|
+
const across = this.across;
|
|
24787
|
+
const down = this.down;
|
|
24788
|
+
const borders = this.borders;
|
|
24789
|
+
const texture = this.texture;
|
|
24790
|
+
const width = texture.width;
|
|
24791
|
+
const height = texture.height;
|
|
24792
|
+
const trim = texture.trim;
|
|
24793
|
+
const x = trim?.x ?? 0;
|
|
24794
|
+
const y = trim?.y ?? 0;
|
|
24795
|
+
const u0 = x / width;
|
|
24796
|
+
const v0 = y / height;
|
|
24797
|
+
const u1 = (x + (trim?.width ?? width)) / width;
|
|
24798
|
+
const v1 = (y + (trim?.height ?? height)) / height;
|
|
24799
|
+
const texelU = 1 / width;
|
|
24800
|
+
const texelV = 1 / height;
|
|
24801
|
+
across.start = u0;
|
|
24802
|
+
across.near = u0 + texelU * borders.left;
|
|
24803
|
+
across.far = u1 - texelU * borders.right;
|
|
24804
|
+
across.end = u1;
|
|
24805
|
+
down.start = v0;
|
|
24806
|
+
down.near = v0 + texelV * borders.top;
|
|
24807
|
+
down.far = v1 - texelV * borders.bottom;
|
|
24808
|
+
down.end = v1;
|
|
24809
|
+
writeGrid(this.geometry.uvs, across, down);
|
|
24810
|
+
this.geometry.getBuffer("aUV").update();
|
|
24811
|
+
}
|
|
24812
|
+
}
|
|
24813
|
+
return HollowNineSlice;
|
|
24814
|
+
}
|
|
24815
|
+
/**
|
|
24816
|
+
* Builds a rim-only nine-slice of Pixi's white texture, its class made at the first one over the loaded module.
|
|
24817
|
+
*
|
|
24818
|
+
* @param state - The sync branch of the plugin state, which keeps the class.
|
|
24819
|
+
* @param pixi - The Pixi module the renderer loaded.
|
|
24820
|
+
* @returns The object.
|
|
24821
|
+
*/
|
|
24822
|
+
function makeHollow(state, pixi) {
|
|
24823
|
+
state.hollowClass ??= createHollowClass(pixi);
|
|
24824
|
+
return new state.hollowClass(pixi.Texture.WHITE);
|
|
24825
|
+
}
|
|
24826
|
+
//#endregion
|
|
24156
24827
|
//#region src/plugins/renderer/sync/labels.ts
|
|
24157
24828
|
/**
|
|
24158
24829
|
* Builds the label of one view.
|
|
@@ -24189,15 +24860,25 @@ function isGraphics(object) {
|
|
|
24189
24860
|
return "roundRect" in object;
|
|
24190
24861
|
}
|
|
24191
24862
|
/**
|
|
24192
|
-
* Tells whether a display object
|
|
24863
|
+
* Tells whether a display object draws a nine-slice: Pixi's `NineSliceSprite`, or the engine's rim-only mesh of a
|
|
24864
|
+
* `hollow` one. Both carry the slice widths.
|
|
24193
24865
|
*
|
|
24194
24866
|
* @param object - The display object.
|
|
24195
|
-
* @returns True for
|
|
24867
|
+
* @returns True for either nine-slice object.
|
|
24196
24868
|
*/
|
|
24197
24869
|
function isNineSlice(object) {
|
|
24198
24870
|
return "leftWidth" in object;
|
|
24199
24871
|
}
|
|
24200
24872
|
/**
|
|
24873
|
+
* Tells whether a display object is the engine's rim-only nine-slice (F59).
|
|
24874
|
+
*
|
|
24875
|
+
* @param object - The display object.
|
|
24876
|
+
* @returns True for the hollow mesh, false for Pixi's `NineSliceSprite` and every other object.
|
|
24877
|
+
*/
|
|
24878
|
+
function isHollow(object) {
|
|
24879
|
+
return "hollow" in object;
|
|
24880
|
+
}
|
|
24881
|
+
/**
|
|
24201
24882
|
* Takes a display object out of the Pixi tree. Pixi containers are not DOM nodes, so the DOM
|
|
24202
24883
|
* helper the linter suggests does not exist on them.
|
|
24203
24884
|
*
|
|
@@ -24248,18 +24929,20 @@ function dropOutline(view) {
|
|
|
24248
24929
|
view.outline = void 0;
|
|
24249
24930
|
}
|
|
24250
24931
|
/**
|
|
24251
|
-
* The pool an object belongs to: its class and the texture it draws.
|
|
24932
|
+
* The pool an object belongs to: its class and the texture it draws. A rim-only nine-slice has a pool of its own, so
|
|
24933
|
+
* a filled panel never gets a hollow object back, nor the other way round.
|
|
24252
24934
|
*
|
|
24253
24935
|
* @param kind - Which component gave the entity its display object.
|
|
24254
24936
|
* @param textureKey - The asset key.
|
|
24937
|
+
* @param hollow - True for the rim-only object of a `NineSlice` with `hollow: true`.
|
|
24255
24938
|
* @returns The pool key.
|
|
24256
24939
|
* @example
|
|
24257
24940
|
* ```ts
|
|
24258
|
-
* poolKeyOf("
|
|
24941
|
+
* poolKeyOf("NineSlice", "glass.frost", true); // "NineSlice.hollow:glass.frost"
|
|
24259
24942
|
* ```
|
|
24260
24943
|
*/
|
|
24261
|
-
function poolKeyOf(kind, textureKey) {
|
|
24262
|
-
return `${kind}:${textureKey}`;
|
|
24944
|
+
function poolKeyOf(kind, textureKey, hollow) {
|
|
24945
|
+
return hollow ? `${kind}.hollow:${textureKey}` : `${kind}:${textureKey}`;
|
|
24263
24946
|
}
|
|
24264
24947
|
/**
|
|
24265
24948
|
* Brings the object of a view that left back to the state a fresh one has: detached, drawn, opaque, unmoved, white.
|
|
@@ -24269,9 +24952,17 @@ function poolKeyOf(kind, textureKey) {
|
|
|
24269
24952
|
* @param view - The view whose object goes back to its pool; its wrapper is gone already.
|
|
24270
24953
|
*/
|
|
24271
24954
|
function resetObject(view) {
|
|
24272
|
-
|
|
24273
|
-
detach$1(object);
|
|
24955
|
+
detach$1(view.object);
|
|
24274
24956
|
clearHidden(view);
|
|
24957
|
+
resetNode(view.object);
|
|
24958
|
+
}
|
|
24959
|
+
/**
|
|
24960
|
+
* Brings a detached object back to a fresh one's pose and look: drawn, opaque, unmoved, white for a sprite and a
|
|
24961
|
+
* graphics.
|
|
24962
|
+
*
|
|
24963
|
+
* @param object - The object, out of the tree.
|
|
24964
|
+
*/
|
|
24965
|
+
function resetNode(object) {
|
|
24275
24966
|
object.visible = true;
|
|
24276
24967
|
object.alpha = 1;
|
|
24277
24968
|
object.rotation = 0;
|
|
@@ -24321,7 +25012,6 @@ function oldestPool(state) {
|
|
|
24321
25012
|
* @param view - The view that left.
|
|
24322
25013
|
*/
|
|
24323
25014
|
function release$1(sctx, view) {
|
|
24324
|
-
const state = sctx.ctx.state.sync;
|
|
24325
25015
|
dropMask(view);
|
|
24326
25016
|
dropOutline(view);
|
|
24327
25017
|
clearFilters(view.object);
|
|
@@ -24344,15 +25034,50 @@ function release$1(sctx, view) {
|
|
|
24344
25034
|
view.display?.adapter.destroy(view.object);
|
|
24345
25035
|
return;
|
|
24346
25036
|
}
|
|
24347
|
-
|
|
24348
|
-
if (groups.has(view.object)) {
|
|
24349
|
-
view.object.disableRenderGroup();
|
|
24350
|
-
groups.delete(view.object);
|
|
24351
|
-
}
|
|
25037
|
+
dropGroup(sctx, view.object);
|
|
24352
25038
|
resetObject(view);
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
25039
|
+
store$1(sctx, view.object, view.poolKey);
|
|
25040
|
+
}
|
|
25041
|
+
/**
|
|
25042
|
+
* Puts an object a view no longer draws with back into a pool, while the view goes on with another one: the
|
|
25043
|
+
* nine-slice whose `hollow` flipped (F59). Reset as `release` resets a view's object; its view keeps its hidden
|
|
25044
|
+
* reasons, which hang on the view's wrapper.
|
|
25045
|
+
*
|
|
25046
|
+
* @param sctx - Domain context of the sync module.
|
|
25047
|
+
* @param object - The object, still in the view's wrapper.
|
|
25048
|
+
* @param poolKey - The pool it belongs to.
|
|
25049
|
+
*/
|
|
25050
|
+
function releaseObject(sctx, object, poolKey) {
|
|
25051
|
+
dropGroup(sctx, object);
|
|
25052
|
+
detach$1(object);
|
|
25053
|
+
resetNode(object);
|
|
25054
|
+
store$1(sctx, object, poolKey);
|
|
25055
|
+
}
|
|
25056
|
+
/**
|
|
25057
|
+
* Takes away the render group the kept cache made of an object (spec 23 P9.3), before it is pooled.
|
|
25058
|
+
*
|
|
25059
|
+
* @param sctx - Domain context of the sync module.
|
|
25060
|
+
* @param object - The object.
|
|
25061
|
+
*/
|
|
25062
|
+
function dropGroup(sctx, object) {
|
|
25063
|
+
const groups = sctx.ctx.state.kept.madeGroups;
|
|
25064
|
+
if (!groups.has(object)) return;
|
|
25065
|
+
object.disableRenderGroup();
|
|
25066
|
+
groups.delete(object);
|
|
25067
|
+
}
|
|
25068
|
+
/**
|
|
25069
|
+
* Pushes a reset object into its pool and evicts the one that waited longest while the pools hold more than
|
|
25070
|
+
* `poolLimit`.
|
|
25071
|
+
*
|
|
25072
|
+
* @param sctx - Domain context of the sync module.
|
|
25073
|
+
* @param object - The reset object.
|
|
25074
|
+
* @param poolKey - Its pool.
|
|
25075
|
+
*/
|
|
25076
|
+
function store$1(sctx, object, poolKey) {
|
|
25077
|
+
const state = sctx.ctx.state.sync;
|
|
25078
|
+
const pool = state.pools.get(poolKey) ?? emptyPool();
|
|
25079
|
+
pool.push(object);
|
|
25080
|
+
state.pools.set(poolKey, pool);
|
|
24356
25081
|
state.pooled += 1;
|
|
24357
25082
|
while (state.pooled > sctx.ctx.config.poolLimit) {
|
|
24358
25083
|
const oldest = oldestPool(state);
|
|
@@ -24363,21 +25088,32 @@ function release$1(sctx, view) {
|
|
|
24363
25088
|
}
|
|
24364
25089
|
/**
|
|
24365
25090
|
* Destroys the pooled objects that draw one of these keys, so a bundle that left frees its GPU
|
|
24366
|
-
* memory instead of waiting for a reuse that resolves nothing.
|
|
25091
|
+
* memory instead of waiting for a reuse that resolves nothing: sprites, nine-slices and rim-only nine-slices.
|
|
24367
25092
|
*
|
|
24368
25093
|
* @param state - The sync branch of the plugin state.
|
|
24369
25094
|
* @param keys - The asset keys that went stale.
|
|
24370
25095
|
*/
|
|
24371
25096
|
function dropPooled(state, keys) {
|
|
24372
|
-
for (const key of keys)
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
state.pooled -= pool.length;
|
|
24377
|
-
state.pools.delete(poolKeyOf(kind, key));
|
|
25097
|
+
for (const key of keys) {
|
|
25098
|
+
dropPool(state, poolKeyOf("Sprite", key, false));
|
|
25099
|
+
dropPool(state, poolKeyOf("NineSlice", key, false));
|
|
25100
|
+
dropPool(state, poolKeyOf("NineSlice", key, true));
|
|
24378
25101
|
}
|
|
24379
25102
|
}
|
|
24380
25103
|
/**
|
|
25104
|
+
* Destroys one pool and everything in it.
|
|
25105
|
+
*
|
|
25106
|
+
* @param state - The sync branch of the plugin state.
|
|
25107
|
+
* @param poolKey - The pool.
|
|
25108
|
+
*/
|
|
25109
|
+
function dropPool(state, poolKey) {
|
|
25110
|
+
const pool = state.pools.get(poolKey);
|
|
25111
|
+
if (pool === void 0) return;
|
|
25112
|
+
for (const object of pool) object.destroy({ texture: false });
|
|
25113
|
+
state.pooled -= pool.length;
|
|
25114
|
+
state.pools.delete(poolKey);
|
|
25115
|
+
}
|
|
25116
|
+
/**
|
|
24381
25117
|
* Destroys everything the pools hold. Works on the state alone, because `onStop` has no context.
|
|
24382
25118
|
*
|
|
24383
25119
|
* @param state - The sync branch of the plugin state.
|
|
@@ -24543,85 +25279,6 @@ function clearLayers(state) {
|
|
|
24543
25279
|
state.hudLayer = void 0;
|
|
24544
25280
|
}
|
|
24545
25281
|
//#endregion
|
|
24546
|
-
//#region src/plugins/renderer/sync/outline.ts
|
|
24547
|
-
/** Above every child of the wrapper, whatever `Order` a child carries. */
|
|
24548
|
-
const OUTLINE_Z = Number.MAX_SAFE_INTEGER;
|
|
24549
|
-
const CYAN = 65535;
|
|
24550
|
-
const RED = 16711680;
|
|
24551
|
-
/**
|
|
24552
|
-
* The slice widths a texture asks for, in the units it is drawn in: its default borders divided
|
|
24553
|
-
* by the resolution of its source. A texture without borders gets 0 on every side, never Pixi's 10.
|
|
24554
|
-
*
|
|
24555
|
-
* @param texture - The texture a nine-slice draws, or `undefined` while nothing can draw.
|
|
24556
|
-
* @returns The four widths.
|
|
24557
|
-
*/
|
|
24558
|
-
function bordersOf(texture) {
|
|
24559
|
-
const borders = texture?.defaultBorders;
|
|
24560
|
-
if (texture === void 0 || borders === void 0) return {
|
|
24561
|
-
left: 0,
|
|
24562
|
-
top: 0,
|
|
24563
|
-
right: 0,
|
|
24564
|
-
bottom: 0
|
|
24565
|
-
};
|
|
24566
|
-
const resolution = texture.source.resolution > 0 ? texture.source.resolution : 1;
|
|
24567
|
-
return {
|
|
24568
|
-
left: borders.left / resolution,
|
|
24569
|
-
top: borders.top / resolution,
|
|
24570
|
-
right: borders.right / resolution,
|
|
24571
|
-
bottom: borders.bottom / resolution
|
|
24572
|
-
};
|
|
24573
|
-
}
|
|
24574
|
-
/**
|
|
24575
|
-
* The factor Pixi shrinks the corners of a nine-slice by when two of them would overlap.
|
|
24576
|
-
*
|
|
24577
|
-
* @param size - The drawn size.
|
|
24578
|
-
* @param size.width - Drawn width.
|
|
24579
|
-
* @param size.height - Drawn height.
|
|
24580
|
-
* @param borders - The slice widths.
|
|
24581
|
-
* @returns 1 when the corners fit, less when they overlap.
|
|
24582
|
-
* @example
|
|
24583
|
-
* ```ts
|
|
24584
|
-
* sliceScale({ width: 100, height: 200 }, { left: 72, top: 76, right: 72, bottom: 76 }); // 0.6944
|
|
24585
|
-
* ```
|
|
24586
|
-
*/
|
|
24587
|
-
function sliceScale(size, borders) {
|
|
24588
|
-
const across = borders.left + borders.right;
|
|
24589
|
-
const down = borders.top + borders.bottom;
|
|
24590
|
-
const scaleX = across > 0 && size.width < across ? size.width / across : 1;
|
|
24591
|
-
const scaleY = down > 0 && size.height < down ? size.height / down : 1;
|
|
24592
|
-
return Math.min(scaleX, scaleY);
|
|
24593
|
-
}
|
|
24594
|
-
/**
|
|
24595
|
-
* Strokes the bounds of a nine-slice and the four lines Pixi cuts it at, into the wrapper of its
|
|
24596
|
-
* view: cyan, red when the corners overlap or the texture is missing. The graphics is kept on the
|
|
24597
|
-
* view and redrawn in place.
|
|
24598
|
-
*
|
|
24599
|
-
* @param pixi - The Pixi module the renderer loaded.
|
|
24600
|
-
* @param wrapper - The wrapper of the view, which carries its pose.
|
|
24601
|
-
* @param view - The view of the nine-slice.
|
|
24602
|
-
* @param shape - Size, borders and whether the texture is missing.
|
|
24603
|
-
*/
|
|
24604
|
-
function drawOutline(pixi, wrapper, view, shape) {
|
|
24605
|
-
const outline = view.outline ?? new pixi.Graphics();
|
|
24606
|
-
const { width, height, borders } = shape;
|
|
24607
|
-
const scale = sliceScale(shape, borders);
|
|
24608
|
-
const left = borders.left * scale;
|
|
24609
|
-
const right = width - borders.right * scale;
|
|
24610
|
-
const top = borders.top * scale;
|
|
24611
|
-
const bottom = height - borders.bottom * scale;
|
|
24612
|
-
outline.label = `outline#${shape.entity}`;
|
|
24613
|
-
outline.zIndex = OUTLINE_Z;
|
|
24614
|
-
outline.clear();
|
|
24615
|
-
outline.rect(0, 0, width, height).moveTo(left, 0).lineTo(left, height).moveTo(right, 0).lineTo(right, height).moveTo(0, top).lineTo(width, top).moveTo(0, bottom).lineTo(width, bottom);
|
|
24616
|
-
outline.stroke({
|
|
24617
|
-
color: shape.missing || scale < 1 ? RED : CYAN,
|
|
24618
|
-
width: 1,
|
|
24619
|
-
pixelLine: true
|
|
24620
|
-
});
|
|
24621
|
-
if (outline.parent !== wrapper) wrapper.addChild(outline);
|
|
24622
|
-
view.outline = outline;
|
|
24623
|
-
}
|
|
24624
|
-
//#endregion
|
|
24625
25282
|
//#region src/plugins/renderer/sync/overlay-range.ts
|
|
24626
25283
|
/** A range retaken for a mark in this many frames in a row is drawn live instead (O8.4; spec 23's `CHURN_LIMIT`). */
|
|
24627
25284
|
const RETAKE_STREAK = 3;
|
|
@@ -26085,6 +26742,144 @@ function destroyTexture(state, texture) {
|
|
|
26085
26742
|
texture.destroy(true);
|
|
26086
26743
|
}
|
|
26087
26744
|
//#endregion
|
|
26745
|
+
//#region src/plugins/renderer/sync/upload.ts
|
|
26746
|
+
/** The calls of a source nobody waits for: none. */
|
|
26747
|
+
const NOBODY = Object.freeze([]);
|
|
26748
|
+
/**
|
|
26749
|
+
* The resolve of a waiter before its promise exists: answers nobody.
|
|
26750
|
+
*/
|
|
26751
|
+
function unanswered() {}
|
|
26752
|
+
/**
|
|
26753
|
+
* Creates the set of the sources uploaded on a device. It lives in its own non-exported function because lint rule L5
|
|
26754
|
+
* refuses a collection built inside an exported declaration.
|
|
26755
|
+
*
|
|
26756
|
+
* @returns An empty weak set.
|
|
26757
|
+
*/
|
|
26758
|
+
function emptyDone() {
|
|
26759
|
+
return /* @__PURE__ */ new WeakSet();
|
|
26760
|
+
}
|
|
26761
|
+
/**
|
|
26762
|
+
* Counts one source for a call: queued once whoever names it first, waited for once by each call, however many of its
|
|
26763
|
+
* textures share it. A source uploaded on this device, or destroyed, is not waited for.
|
|
26764
|
+
*
|
|
26765
|
+
* @param upload - The upload branch of the sync state.
|
|
26766
|
+
* @param source - The source of one texture of the call.
|
|
26767
|
+
* @param waiter - The call.
|
|
26768
|
+
*/
|
|
26769
|
+
function enqueue(upload, source, waiter) {
|
|
26770
|
+
if (source.destroyed || upload.done.has(source)) return;
|
|
26771
|
+
let waiters = upload.waiting.get(source);
|
|
26772
|
+
if (waiters === void 0) {
|
|
26773
|
+
waiters = [];
|
|
26774
|
+
upload.waiting.set(source, waiters);
|
|
26775
|
+
upload.queue.push(source);
|
|
26776
|
+
}
|
|
26777
|
+
if (waiters.at(-1) === waiter) return;
|
|
26778
|
+
waiters.push(waiter);
|
|
26779
|
+
waiter.left += 1;
|
|
26780
|
+
}
|
|
26781
|
+
/**
|
|
26782
|
+
* Queues the sources of textures for the GPU; resolves true once each went through `initSource` (the body of
|
|
26783
|
+
* `TexturesApi.upload`). Answers false at once while nothing draws or with `uploads: 0`, and true at once when every
|
|
26784
|
+
* source is on this device already. Asks for a frame.
|
|
26785
|
+
*
|
|
26786
|
+
* @param sctx - Domain context of the sync module.
|
|
26787
|
+
* @param textures - The textures.
|
|
26788
|
+
* @returns Whether every source is on the GPU.
|
|
26789
|
+
*/
|
|
26790
|
+
function uploadTextures(sctx, textures) {
|
|
26791
|
+
const { upload } = sctx.ctx.state.sync;
|
|
26792
|
+
if (sctx.ctx.config.uploads === 0 || !sctx.deps.host.ready()) return Promise.resolve(false);
|
|
26793
|
+
const waiter = {
|
|
26794
|
+
left: 0,
|
|
26795
|
+
resolve: unanswered
|
|
26796
|
+
};
|
|
26797
|
+
for (const texture of textures) enqueue(upload, texture.source, waiter);
|
|
26798
|
+
if (waiter.left === 0) return Promise.resolve(true);
|
|
26799
|
+
sctx.deps.demand.request();
|
|
26800
|
+
return new Promise((resolve) => {
|
|
26801
|
+
waiter.resolve = resolve;
|
|
26802
|
+
});
|
|
26803
|
+
}
|
|
26804
|
+
/**
|
|
26805
|
+
* Uploads one source, the error of a throw logged: the source counts as done either way, so a failing one is logged
|
|
26806
|
+
* once and never stops the queue.
|
|
26807
|
+
*
|
|
26808
|
+
* @param sctx - Domain context of the sync module.
|
|
26809
|
+
* @param upload - The upload branch of the sync state.
|
|
26810
|
+
* @param source - The source.
|
|
26811
|
+
*/
|
|
26812
|
+
function put(sctx, upload, source) {
|
|
26813
|
+
try {
|
|
26814
|
+
sctx.deps.host.initSource(source);
|
|
26815
|
+
} catch (error) {
|
|
26816
|
+
sctx.ctx.log.warn("renderer:upload-failed", {
|
|
26817
|
+
source: source.label,
|
|
26818
|
+
error
|
|
26819
|
+
});
|
|
26820
|
+
}
|
|
26821
|
+
upload.done.add(source);
|
|
26822
|
+
}
|
|
26823
|
+
/**
|
|
26824
|
+
* Counts a source off for the calls that wait for it; a call whose last source it was answers true.
|
|
26825
|
+
*
|
|
26826
|
+
* @param upload - The upload branch of the sync state.
|
|
26827
|
+
* @param source - The source uploaded or skipped.
|
|
26828
|
+
*/
|
|
26829
|
+
function settle$2(upload, source) {
|
|
26830
|
+
const waiters = upload.waiting.get(source) ?? NOBODY;
|
|
26831
|
+
upload.waiting.delete(source);
|
|
26832
|
+
for (const waiter of waiters) {
|
|
26833
|
+
waiter.left -= 1;
|
|
26834
|
+
if (waiter.left === 0) waiter.resolve(true);
|
|
26835
|
+
}
|
|
26836
|
+
}
|
|
26837
|
+
/**
|
|
26838
|
+
* Uploads up to `renderer.uploads` queued sources before the stage pass of a drawn frame; a source destroyed while it
|
|
26839
|
+
* waited is skipped and costs nothing of the budget. A queue that still holds sources asks for the next frame; one
|
|
26840
|
+
* that ran out empties. Reads two numbers and returns while the queue is empty.
|
|
26841
|
+
*
|
|
26842
|
+
* @param sctx - Domain context of the sync module.
|
|
26843
|
+
* @returns True when a source went to the GPU in this frame, so the frame is no sample of the adaptive rate.
|
|
26844
|
+
*/
|
|
26845
|
+
function uploadStep(sctx) {
|
|
26846
|
+
const { upload } = sctx.ctx.state.sync;
|
|
26847
|
+
if (upload.head >= upload.queue.length) return false;
|
|
26848
|
+
const budget = sctx.ctx.config.uploads;
|
|
26849
|
+
let made = 0;
|
|
26850
|
+
while (made < budget && upload.head < upload.queue.length) {
|
|
26851
|
+
const source = upload.queue[upload.head];
|
|
26852
|
+
upload.head += 1;
|
|
26853
|
+
if (source === void 0) continue;
|
|
26854
|
+
if (!source.destroyed) {
|
|
26855
|
+
put(sctx, upload, source);
|
|
26856
|
+
made += 1;
|
|
26857
|
+
}
|
|
26858
|
+
settle$2(upload, source);
|
|
26859
|
+
}
|
|
26860
|
+
if (upload.head < upload.queue.length) {
|
|
26861
|
+
sctx.deps.demand.request();
|
|
26862
|
+
return made > 0;
|
|
26863
|
+
}
|
|
26864
|
+
upload.queue.length = 0;
|
|
26865
|
+
upload.head = 0;
|
|
26866
|
+
return made > 0;
|
|
26867
|
+
}
|
|
26868
|
+
/**
|
|
26869
|
+
* Ends the queue of a device that goes, from state alone (`onStop` has no context): every waiting call answers false,
|
|
26870
|
+
* the queue empties, and the sources uploaded on it are forgotten, since a new device holds none of them.
|
|
26871
|
+
*
|
|
26872
|
+
* @param state - The sync branch of the plugin state.
|
|
26873
|
+
*/
|
|
26874
|
+
function stopUploads(state) {
|
|
26875
|
+
const { upload } = state;
|
|
26876
|
+
for (const waiters of upload.waiting.values()) for (const waiter of waiters) waiter.resolve(false);
|
|
26877
|
+
upload.waiting.clear();
|
|
26878
|
+
upload.queue.length = 0;
|
|
26879
|
+
upload.head = 0;
|
|
26880
|
+
upload.done = emptyDone();
|
|
26881
|
+
}
|
|
26882
|
+
//#endregion
|
|
26088
26883
|
//#region src/plugins/renderer/sync/views.ts
|
|
26089
26884
|
/**
|
|
26090
26885
|
* @file renderer/sync — one view per entity: build it from the components, write every value,
|
|
@@ -26104,6 +26899,23 @@ const PLACEHOLDER_BOX = Object.freeze({
|
|
|
26104
26899
|
* sync pass).
|
|
26105
26900
|
*/
|
|
26106
26901
|
const LAYOUT = createSpriteLayout();
|
|
26902
|
+
/** The slice widths of the nine-slice being written: per write, reused, as `LAYOUT`. */
|
|
26903
|
+
const BORDERS = {
|
|
26904
|
+
left: 0,
|
|
26905
|
+
top: 0,
|
|
26906
|
+
right: 0,
|
|
26907
|
+
bottom: 0
|
|
26908
|
+
};
|
|
26909
|
+
/** What the outline of the nine-slice being written is drawn from, written in place, as `LAYOUT`. */
|
|
26910
|
+
const OUTLINE = {
|
|
26911
|
+
entity: 0,
|
|
26912
|
+
width: 0,
|
|
26913
|
+
height: 0,
|
|
26914
|
+
borders: BORDERS,
|
|
26915
|
+
missing: false
|
|
26916
|
+
};
|
|
26917
|
+
/** The box a clipping nine-slice masks its children with, as the shape value `applyClip` reads, written in place. */
|
|
26918
|
+
const NINE_CLIP = { ...Shape.defaults };
|
|
26107
26919
|
/**
|
|
26108
26920
|
* The depth of a wrapper's own visual among the wrapper's children: a child at `Order` 0 or more draws above it, one
|
|
26109
26921
|
* at −1 or less below it, and a ui child at `zIndex` 0 (an `Order` between −0.5 and 0) above it too (spec 16 U1.8).
|
|
@@ -26204,26 +27016,31 @@ function visualOf(sctx, entity) {
|
|
|
26204
27016
|
if (sprite !== void 0) found.push({
|
|
26205
27017
|
kind: "Sprite",
|
|
26206
27018
|
textureKey: sprite.texture,
|
|
27019
|
+
hollow: false,
|
|
26207
27020
|
display: void 0
|
|
26208
27021
|
});
|
|
26209
27022
|
if (nine !== void 0) found.push({
|
|
26210
27023
|
kind: "NineSlice",
|
|
26211
27024
|
textureKey: nine.texture,
|
|
27025
|
+
hollow: nine.hollow === true,
|
|
26212
27026
|
display: void 0
|
|
26213
27027
|
});
|
|
26214
27028
|
if (ecs.has(entity, Shape)) found.push({
|
|
26215
27029
|
kind: "Shape",
|
|
26216
27030
|
textureKey: "",
|
|
27031
|
+
hollow: false,
|
|
26217
27032
|
display: void 0
|
|
26218
27033
|
});
|
|
26219
27034
|
if (ecs.has(entity, Display)) found.push({
|
|
26220
27035
|
kind: "Display",
|
|
26221
27036
|
textureKey: "",
|
|
27037
|
+
hollow: false,
|
|
26222
27038
|
display: void 0
|
|
26223
27039
|
});
|
|
26224
27040
|
for (const entry of adaptersOn(sctx, entity)) found.push({
|
|
26225
27041
|
kind: "adapter",
|
|
26226
27042
|
textureKey: "",
|
|
27043
|
+
hollow: false,
|
|
26227
27044
|
display: entry
|
|
26228
27045
|
});
|
|
26229
27046
|
if (found.length > 1) sctx.ctx.log.warn("renderer: entity has more than one visual", {
|
|
@@ -26244,29 +27061,10 @@ function retargetKey(state, entity, view, key) {
|
|
|
26244
27061
|
if (view.textureKey === key) return;
|
|
26245
27062
|
untrackKey(state, entity, view.textureKey);
|
|
26246
27063
|
view.textureKey = key;
|
|
26247
|
-
view.poolKey = poolKeyOf(view.kind, key);
|
|
27064
|
+
view.poolKey = poolKeyOf(view.kind, key, isHollow(view.object));
|
|
26248
27065
|
if (key !== "") trackKey(state, entity, key);
|
|
26249
27066
|
}
|
|
26250
27067
|
/**
|
|
26251
|
-
* Resolves a key and says whether the placeholder had to step in.
|
|
26252
|
-
*
|
|
26253
|
-
* @param sctx - Domain context of the sync module.
|
|
26254
|
-
* @param key - The asset key.
|
|
26255
|
-
* @returns The texture to draw and whether it is the placeholder.
|
|
26256
|
-
*/
|
|
26257
|
-
function textureFor(sctx, key) {
|
|
26258
|
-
const resolved = resolveTexture(sctx, key);
|
|
26259
|
-
if (resolved !== void 0) return {
|
|
26260
|
-
texture: resolved,
|
|
26261
|
-
missing: false
|
|
26262
|
-
};
|
|
26263
|
-
warnMissing(sctx, key);
|
|
26264
|
-
return {
|
|
26265
|
-
texture: placeholderTexture(sctx),
|
|
26266
|
-
missing: true
|
|
26267
|
-
};
|
|
26268
|
-
}
|
|
26269
|
-
/**
|
|
26270
27068
|
* Writes the texture and the tint of a sprite, each only when it differs from what Pixi holds.
|
|
26271
27069
|
*
|
|
26272
27070
|
* @param sctx - Domain context of the sync module.
|
|
@@ -26475,12 +27273,12 @@ function applyOutline(sctx, view, shape, debug, changed) {
|
|
|
26475
27273
|
sctx.ctx.state.sync.redraws += 1;
|
|
26476
27274
|
}
|
|
26477
27275
|
/**
|
|
26478
|
-
* Writes one number of a nine-slice only when it differs from what
|
|
26479
|
-
* panel on every write of a width, even an equal one.
|
|
27276
|
+
* Writes one number of a nine-slice only when it differs from what the object holds: Pixi rebuilds the
|
|
27277
|
+
* panel on every write of a width, even an equal one, and the rim-only object rewrites its vertices.
|
|
26480
27278
|
*
|
|
26481
27279
|
* @param sctx - Domain context of the sync module.
|
|
26482
27280
|
* @param entity - The entity.
|
|
26483
|
-
* @param object -
|
|
27281
|
+
* @param object - Pixi's nine-slice sprite, or the engine's rim-only one.
|
|
26484
27282
|
* @param field - Which number.
|
|
26485
27283
|
* @param value - The new number.
|
|
26486
27284
|
* @returns True when it was written.
|
|
@@ -26494,7 +27292,9 @@ function writeSlice(sctx, entity, object, field, value) {
|
|
|
26494
27292
|
/**
|
|
26495
27293
|
* Writes the `NineSlice` component onto its display object and recomputes the hit box. Pixi reads
|
|
26496
27294
|
* a texture's borders only in the constructor, so they are copied here on every write: a pooled
|
|
26497
|
-
* object, a new key and a reloaded bundle all get the insets of the texture they draw now.
|
|
27295
|
+
* object, a new key and a reloaded bundle all get the insets of the texture they draw now. A `hollow` that flipped
|
|
27296
|
+
* swaps the view's object first (F59). The hit box is the whole panel, hollow or not. A write that changes nothing,
|
|
27297
|
+
* or only the size, allocates nothing: the borders, the outline's shape and the clip's box are written in place.
|
|
26498
27298
|
*
|
|
26499
27299
|
* @param sctx - Domain context of the sync module.
|
|
26500
27300
|
* @param entity - The entity.
|
|
@@ -26502,11 +27302,16 @@ function writeSlice(sctx, entity, object, field, value) {
|
|
|
26502
27302
|
*/
|
|
26503
27303
|
function applyNineSlice(sctx, entity, view) {
|
|
26504
27304
|
const value = sctx.ctx.deps.world.ecs.get(entity, NineSlice);
|
|
27305
|
+
if (value === void 0 || !isNineSlice(view.object)) return;
|
|
27306
|
+
if (isHollow(view.object) !== (value.hollow === true)) swapSlice(sctx, entity, view);
|
|
26505
27307
|
const object = view.object;
|
|
26506
|
-
if (
|
|
27308
|
+
if (!isNineSlice(object)) return;
|
|
26507
27309
|
retargetKey(sctx.ctx.state.sync, entity, view, value.texture);
|
|
26508
|
-
const
|
|
26509
|
-
const
|
|
27310
|
+
const resolved = resolveTexture(sctx, value.texture);
|
|
27311
|
+
const missing = resolved === void 0;
|
|
27312
|
+
if (missing) warnMissing(sctx, value.texture);
|
|
27313
|
+
const texture = resolved ?? placeholderTexture(sctx);
|
|
27314
|
+
const borders = bordersInto(texture, BORDERS);
|
|
26510
27315
|
view.placeholder = missing;
|
|
26511
27316
|
let changed = false;
|
|
26512
27317
|
if (texture !== void 0 && object.texture !== texture) {
|
|
@@ -26522,25 +27327,59 @@ function applyNineSlice(sctx, entity, view) {
|
|
|
26522
27327
|
applyAlpha(sctx, entity, view, value);
|
|
26523
27328
|
changed = writeSlice(sctx, entity, object, "width", value.width) || changed;
|
|
26524
27329
|
changed = writeSlice(sctx, entity, object, "height", value.height) || changed;
|
|
26525
|
-
view.hitBox
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
26534
|
-
|
|
26535
|
-
|
|
26536
|
-
|
|
26537
|
-
|
|
26538
|
-
|
|
26539
|
-
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
|
|
27330
|
+
writeHitBox(view.hitBox, value.width, value.height);
|
|
27331
|
+
OUTLINE.entity = entity;
|
|
27332
|
+
OUTLINE.width = value.width;
|
|
27333
|
+
OUTLINE.height = value.height;
|
|
27334
|
+
OUTLINE.missing = missing;
|
|
27335
|
+
applyOutline(sctx, view, OUTLINE, value.debug, changed);
|
|
27336
|
+
NINE_CLIP.w = value.width;
|
|
27337
|
+
NINE_CLIP.h = value.height;
|
|
27338
|
+
NINE_CLIP.clip = value.clip;
|
|
27339
|
+
applyClip(sctx, entity, view, NINE_CLIP);
|
|
27340
|
+
}
|
|
27341
|
+
/**
|
|
27342
|
+
* A new nine-slice object of one kind: the engine's rim-only mesh, or Pixi's `NineSliceSprite`; both of the white
|
|
27343
|
+
* texture until the first write.
|
|
27344
|
+
*
|
|
27345
|
+
* @param sctx - Domain context of the sync module.
|
|
27346
|
+
* @param pixi - The Pixi module.
|
|
27347
|
+
* @param hollow - True for the rim-only object.
|
|
27348
|
+
* @returns The object.
|
|
27349
|
+
*/
|
|
27350
|
+
function newSlice(sctx, pixi, hollow) {
|
|
27351
|
+
return hollow ? makeHollow(sctx.ctx.state.sync, pixi) : new pixi.NineSliceSprite({ texture: pixi.Texture.WHITE });
|
|
27352
|
+
}
|
|
27353
|
+
/**
|
|
27354
|
+
* Gives a nine-slice view whose `hollow` flipped the other object (F59), on the write that flipped it: the view gets
|
|
27355
|
+
* its wrapper first (it keeps the pose, the alpha, the filters, the clip and the outline), then the other kind takes
|
|
27356
|
+
* the old object's place in it, out of its pool or new, and the old object goes back to its own pool. The entity, its
|
|
27357
|
+
* children and its hit box stay as they were; the caller writes the new object in full.
|
|
27358
|
+
*
|
|
27359
|
+
* @param sctx - Domain context of the sync module.
|
|
27360
|
+
* @param entity - The entity.
|
|
27361
|
+
* @param view - Its view, drawn by a nine-slice object.
|
|
27362
|
+
*/
|
|
27363
|
+
function swapSlice(sctx, entity, view) {
|
|
27364
|
+
const pixi = sctx.deps.host.pixi();
|
|
27365
|
+
const wrapper = ensureWrapper(sctx, entity);
|
|
27366
|
+
if (pixi === void 0 || wrapper === void 0) return;
|
|
27367
|
+
const state = sctx.ctx.state.sync;
|
|
27368
|
+
const old = view.object;
|
|
27369
|
+
const hollow = !isHollow(old);
|
|
27370
|
+
const poolKey = poolKeyOf("NineSlice", view.textureKey, hollow);
|
|
27371
|
+
const next = acquire(state, poolKey) ?? newSlice(sctx, pixi, hollow);
|
|
27372
|
+
markView(sctx, entity);
|
|
27373
|
+
next.label = old.label;
|
|
27374
|
+
next.zIndex = old.zIndex;
|
|
27375
|
+
wrapper.addChildAt(next, Math.max(0, wrapper.children.indexOf(old)));
|
|
27376
|
+
state.entityOf.delete(old);
|
|
27377
|
+
state.entityOf.set(next, entity);
|
|
27378
|
+
view.object = next;
|
|
27379
|
+
releaseObject(sctx, old, view.poolKey);
|
|
27380
|
+
view.poolKey = poolKey;
|
|
27381
|
+
writeRenderable(view);
|
|
27382
|
+
applyTransform(sctx, entity, view);
|
|
26544
27383
|
}
|
|
26545
27384
|
/**
|
|
26546
27385
|
* Gives a clipping view the masked container its children hang in, next to its own visual in the
|
|
@@ -26558,7 +27397,9 @@ function ensureClipped(pixi, entity, view, wrapper) {
|
|
|
26558
27397
|
const clipped = new pixi.Container();
|
|
26559
27398
|
clipped.label = `children#${entity}`;
|
|
26560
27399
|
clipped.sortableChildren = true;
|
|
26561
|
-
|
|
27400
|
+
let index = 0;
|
|
27401
|
+
for (let child = wrapper.children[0]; child !== void 0; child = wrapper.children[index]) if (child === view.object || child === view.outline) index += 1;
|
|
27402
|
+
else clipped.addChild(child);
|
|
26562
27403
|
wrapper.addChild(clipped);
|
|
26563
27404
|
view.clipped = clipped;
|
|
26564
27405
|
return clipped;
|
|
@@ -26990,6 +27831,7 @@ function attach$1(sctx, entity, view) {
|
|
|
26990
27831
|
* @param visual - Which component gives the entity its object, and its texture key.
|
|
26991
27832
|
* @param visual.kind - Which component gives the entity its object.
|
|
26992
27833
|
* @param visual.textureKey - The asset key the object draws.
|
|
27834
|
+
* @param visual.hollow - True for the rim-only object of a hollow `NineSlice`.
|
|
26993
27835
|
* @returns The object, or `undefined` while inert.
|
|
26994
27836
|
*/
|
|
26995
27837
|
function makeObject(sctx, entity, visual) {
|
|
@@ -27003,7 +27845,7 @@ function makeObject(sctx, entity, visual) {
|
|
|
27003
27845
|
const pixi = sctx.deps.host.pixi();
|
|
27004
27846
|
if (pixi === void 0) return void 0;
|
|
27005
27847
|
if (visual.display !== void 0) return adapterObject(sctx, entity, visual.display);
|
|
27006
|
-
const pooled = acquire(sctx.ctx.state.sync, poolKeyOf(visual.kind, visual.textureKey));
|
|
27848
|
+
const pooled = acquire(sctx.ctx.state.sync, poolKeyOf(visual.kind, visual.textureKey, visual.hollow));
|
|
27007
27849
|
if (pooled !== void 0) return {
|
|
27008
27850
|
object: pooled,
|
|
27009
27851
|
value: void 0
|
|
@@ -27017,7 +27859,7 @@ function makeObject(sctx, entity, visual) {
|
|
|
27017
27859
|
value: void 0
|
|
27018
27860
|
};
|
|
27019
27861
|
return {
|
|
27020
|
-
object:
|
|
27862
|
+
object: newSlice(sctx, pixi, visual.hollow),
|
|
27021
27863
|
value: void 0
|
|
27022
27864
|
};
|
|
27023
27865
|
}
|
|
@@ -27060,7 +27902,7 @@ function createView(sctx, entity) {
|
|
|
27060
27902
|
const view = {
|
|
27061
27903
|
object,
|
|
27062
27904
|
kind: visual.kind,
|
|
27063
|
-
poolKey: poolKeyOf(visual.kind, visual.textureKey),
|
|
27905
|
+
poolKey: poolKeyOf(visual.kind, visual.textureKey, visual.hollow),
|
|
27064
27906
|
layer: "",
|
|
27065
27907
|
textureKey: visual.textureKey,
|
|
27066
27908
|
wrapper: void 0,
|
|
@@ -27185,8 +28027,9 @@ function dropView(sctx, entity) {
|
|
|
27185
28027
|
release$1(sctx, view);
|
|
27186
28028
|
}
|
|
27187
28029
|
/**
|
|
27188
|
-
* Drops every view, every layer, every pool, every filter slot and filter area,
|
|
27189
|
-
* answer false, its probe tree is destroyed). Works on the state
|
|
28030
|
+
* Drops every view, every layer, every pool, every filter slot and filter area, the warm-up (its waiting promises
|
|
28031
|
+
* answer false, its probe tree is destroyed) and the upload queue (its waiting calls answer false). Works on the state
|
|
28032
|
+
* alone,
|
|
27190
28033
|
* because `onStop` has no context. A `Display` object belongs to the game: it is detached, never
|
|
27191
28034
|
* destroyed, and leaves without the filters, the filter area and the hidden reasons the renderer gave it.
|
|
27192
28035
|
*
|
|
@@ -27231,6 +28074,7 @@ function stopSync(state) {
|
|
|
27231
28074
|
state.areas.clear();
|
|
27232
28075
|
state.viewlessEntries = false;
|
|
27233
28076
|
stopWarm(state);
|
|
28077
|
+
stopUploads(state);
|
|
27234
28078
|
state.held.clear();
|
|
27235
28079
|
state.redraws = 0;
|
|
27236
28080
|
state.overdraw.listeners.length = 0;
|
|
@@ -27374,7 +28218,8 @@ function withDeps$8(ctx) {
|
|
|
27374
28218
|
* takes the same path: the backing store changes, the frame and the scale do not), then the
|
|
27375
28219
|
* overlay step (spec 22), then `kept` reads the marks before `demand` decides whether the frame is
|
|
27376
28220
|
* drawn and clears them; a drawn frame draws the probes of the warm-up onto the canvas first (F4: the
|
|
27377
|
-
* stage pass clears them, in the same task, so they never show), then the
|
|
28221
|
+
* stage pass clears them, in the same task, so they never show), then uploads the queued texture sources (F60), then
|
|
28222
|
+
* the kept pictures
|
|
27378
28223
|
* (`kept.flush`, so the canvas stays one pass, E7), then the stage, then the pixel leg of the kept
|
|
27379
28224
|
* oracle; `demand` settles the
|
|
27380
28225
|
* frame (the dev check, the pixel oracle), and `monitor` closes it and hands a drawn one to a
|
|
@@ -27409,10 +28254,11 @@ function renderFrame(ctx, modules) {
|
|
|
27409
28254
|
const drawn = modules.demand.decide(modules.monitor.waiting());
|
|
27410
28255
|
if (drawn) {
|
|
27411
28256
|
const probed = modules.sync.warmStep();
|
|
28257
|
+
const uploaded = modules.sync.uploadStep();
|
|
27412
28258
|
modules.kept.flush();
|
|
27413
28259
|
if (overdraw ? modules.sync.drawOverdraw(modules.host.render) : modules.host.render()) {
|
|
27414
28260
|
ctx.deps.time.frameDrawn();
|
|
27415
|
-
if (probed) ctx.deps.time.frameProbed();
|
|
28261
|
+
if (probed || uploaded) ctx.deps.time.frameProbed();
|
|
27416
28262
|
}
|
|
27417
28263
|
modules.kept.after();
|
|
27418
28264
|
}
|
|
@@ -29616,6 +30462,7 @@ function createSyncApi(ctx, deps) {
|
|
|
29616
30462
|
const forgetTree = () => {
|
|
29617
30463
|
deps.kept.reset();
|
|
29618
30464
|
forgetOverlays(state);
|
|
30465
|
+
stopUploads(state);
|
|
29619
30466
|
state.held.clear();
|
|
29620
30467
|
for (const view of state.views.values()) {
|
|
29621
30468
|
state.viewOfTop.delete(view.wrapper ?? view.object);
|
|
@@ -29658,7 +30505,7 @@ function createSyncApi(ctx, deps) {
|
|
|
29658
30505
|
state.removed.clear();
|
|
29659
30506
|
state.reparented.clear();
|
|
29660
30507
|
state.outlinesStale = false;
|
|
29661
|
-
startWarm$
|
|
30508
|
+
startWarm$2(sctx);
|
|
29662
30509
|
};
|
|
29663
30510
|
return {
|
|
29664
30511
|
hitTest: (x, y, accept) => hitTest(sctx, x, y, accept),
|
|
@@ -29685,7 +30532,8 @@ function createSyncApi(ctx, deps) {
|
|
|
29685
30532
|
if (state.root === void 0) return;
|
|
29686
30533
|
for (const key of keys) state.invalidated.add(key);
|
|
29687
30534
|
dropPooled(state, keys);
|
|
29688
|
-
}
|
|
30535
|
+
},
|
|
30536
|
+
upload: (textures) => uploadTextures(sctx, textures)
|
|
29689
30537
|
},
|
|
29690
30538
|
displays: { provide: (component, adapter) => provideDisplay(sctx, component, adapter, {
|
|
29691
30539
|
added: markAdded,
|
|
@@ -29733,6 +30581,7 @@ function createSyncApi(ctx, deps) {
|
|
|
29733
30581
|
stepFilters: () => applySwitched(state, deps.kept),
|
|
29734
30582
|
drainOverlayEvents: (monitor) => drainOverlayEvents(sctx, monitor),
|
|
29735
30583
|
warmStep: () => warmStep(sctx),
|
|
30584
|
+
uploadStep: () => uploadStep(sctx),
|
|
29736
30585
|
onWarm: (fn) => {
|
|
29737
30586
|
state.warm.listeners.push(fn);
|
|
29738
30587
|
},
|
|
@@ -30524,8 +31373,22 @@ function createWarmState() {
|
|
|
30524
31373
|
};
|
|
30525
31374
|
}
|
|
30526
31375
|
/**
|
|
31376
|
+
* Creates the empty upload queue: nothing queued, nobody waiting, nothing uploaded. Its own non-exported function, as
|
|
31377
|
+
* `emptyMap`.
|
|
31378
|
+
*
|
|
31379
|
+
* @returns The upload branch of the sync state.
|
|
31380
|
+
*/
|
|
31381
|
+
function createUploadState() {
|
|
31382
|
+
return {
|
|
31383
|
+
queue: [],
|
|
31384
|
+
head: 0,
|
|
31385
|
+
waiting: emptyMap$6(),
|
|
31386
|
+
done: emptyWeakSet()
|
|
31387
|
+
};
|
|
31388
|
+
}
|
|
31389
|
+
/**
|
|
30527
31390
|
* Creates the initial sync state: no root, no layers, no view, no orphan, no pool, no provider, no filter, no
|
|
30528
|
-
* filter area, no slice, no hidden mask, no overlay, no warm-up, no redraw counted and no unseen walk kept.
|
|
31391
|
+
* filter area, no slice, no hidden mask, no overlay, no warm-up, no upload, no redraw counted and no unseen walk kept.
|
|
30529
31392
|
*
|
|
30530
31393
|
* @param debug - The debug switches of the config; copied, so the frozen config stays untouched.
|
|
30531
31394
|
* @returns The sync branch of the plugin state.
|
|
@@ -30564,6 +31427,8 @@ function createSyncState(debug) {
|
|
|
30564
31427
|
areas: emptyMap$6(),
|
|
30565
31428
|
viewlessEntries: false,
|
|
30566
31429
|
warm: createWarmState(),
|
|
31430
|
+
upload: createUploadState(),
|
|
31431
|
+
hollowClass: void 0,
|
|
30567
31432
|
filterKinds: emptyWeakMap$1(),
|
|
30568
31433
|
filterOwners: emptyWeakMap$1(),
|
|
30569
31434
|
objectHidden: emptyWeakMap$1(),
|
|
@@ -30631,6 +31496,8 @@ const RING_FRAMES = {
|
|
|
30631
31496
|
};
|
|
30632
31497
|
/** The most probe draws of the warm-up a drawn frame may make. */
|
|
30633
31498
|
const MAX_WARM = 16;
|
|
31499
|
+
/** The most textures the upload queue may put on the GPU in one drawn frame: about 9 ms each on a Galaxy A26. */
|
|
31500
|
+
const MAX_UPLOADS = 8;
|
|
30634
31501
|
/**
|
|
30635
31502
|
* Tells a finite number of at least 0 from anything else a config can hold.
|
|
30636
31503
|
*
|
|
@@ -30677,14 +31544,14 @@ function checkAdaptiveScale(adaptiveScale) {
|
|
|
30677
31544
|
}
|
|
30678
31545
|
/**
|
|
30679
31546
|
* Checks the numbers of the config a game can get wrong, at `createApp`, in every build: the
|
|
30680
|
-
* resolution cap, the pool trim, the adaptive render scale, the performance monitor, the budget of the kept pictures
|
|
30681
|
-
*
|
|
31547
|
+
* resolution cap, the pool trim, the adaptive render scale, the performance monitor, the budget of the kept pictures,
|
|
31548
|
+
* the probe draws of the warm-up and the textures uploaded per drawn frame.
|
|
30682
31549
|
*
|
|
30683
31550
|
* @param config - The resolved plugin config.
|
|
30684
31551
|
* @throws {Error} When `maxResolution` is not a number above 0, `poolTrim` or `adaptiveScale` fails its check,
|
|
30685
31552
|
* `monitor.budgetMs` is neither `"rate"` nor a number above 0, `monitor.frames` is not a whole
|
|
30686
|
-
* number from 64 to 16384, `keptMb` is not a finite number of at least 0,
|
|
30687
|
-
* whole number from 0 to 16.
|
|
31553
|
+
* number from 64 to 16384, `keptMb` is not a finite number of at least 0, `warm` is not a
|
|
31554
|
+
* whole number from 0 to 16, or `uploads` is not a whole number from 0 to 8.
|
|
30688
31555
|
*/
|
|
30689
31556
|
function checkConfig(config) {
|
|
30690
31557
|
const { maxResolution, keptMb } = config;
|
|
@@ -30697,6 +31564,8 @@ function checkConfig(config) {
|
|
|
30697
31564
|
if (typeof keptMb !== "number" || !Number.isFinite(keptMb) || keptMb < 0) throw new Error(`[game] renderer.keptMb is ${String(keptMb)}.\n Give the MiB kept pictures may hold, or 0 to keep none.`);
|
|
30698
31565
|
const { warm } = config;
|
|
30699
31566
|
if (!Number.isInteger(warm) || warm < 0 || warm > MAX_WARM) throw new Error(`[game] renderer.warm is ${String(warm)}.\n Give the probe draws per frame, a whole number from 0 to 16.`);
|
|
31567
|
+
const { uploads } = config;
|
|
31568
|
+
if (!Number.isInteger(uploads) || uploads < 0 || uploads > MAX_UPLOADS) throw new Error(`[game] renderer.uploads is ${String(uploads)}.\n Give the textures uploaded per drawn frame, a whole number from 0 to 8.`);
|
|
30700
31569
|
}
|
|
30701
31570
|
/**
|
|
30702
31571
|
* Creates the initial renderer state: one branch per module, composed from the module state
|
|
@@ -30778,6 +31647,7 @@ const rendererPlugin = /*#__PURE__*/ createPlugin$1("renderer", {
|
|
|
30778
31647
|
onDemand: false,
|
|
30779
31648
|
keptMb: 64,
|
|
30780
31649
|
warm: 1,
|
|
31650
|
+
uploads: 1,
|
|
30781
31651
|
adaptiveScale: {
|
|
30782
31652
|
floor: .75,
|
|
30783
31653
|
devices: "phone"
|
|
@@ -34291,8 +35161,9 @@ function reportSettled(ctx, progress, signal) {
|
|
|
34291
35161
|
ctx.emit("assets:bundle-progress", { ...progress });
|
|
34292
35162
|
}
|
|
34293
35163
|
/**
|
|
34294
|
-
* Publishes a loaded bundle: what was loaded moves into the record, `
|
|
34295
|
-
* keys, the event goes out, the frame loop wakes and the
|
|
35164
|
+
* Publishes a loaded bundle: what was loaded moves into the record, a failure `ready` remembered
|
|
35165
|
+
* is forgotten, `renderer` re-resolves the keys, the event goes out, the frame loop wakes and the
|
|
35166
|
+
* budget is enforced.
|
|
34296
35167
|
*
|
|
34297
35168
|
* @param ctx - Domain context of the plugin.
|
|
34298
35169
|
* @param bundle - Name of the bundle.
|
|
@@ -34309,6 +35180,7 @@ function finish$1(ctx, bundle, entry, assets, reason) {
|
|
|
34309
35180
|
record.status = "loaded";
|
|
34310
35181
|
record.inflight = void 0;
|
|
34311
35182
|
touch(ctx.state, record);
|
|
35183
|
+
ctx.state.readyFailed.delete(bundle);
|
|
34312
35184
|
ctx.deps.renderer.sync.textures.invalidate(entry.files.map((file) => file.key));
|
|
34313
35185
|
ctx.emit("assets:bundle-loaded", {
|
|
34314
35186
|
bundle,
|
|
@@ -34646,6 +35518,8 @@ async function bootTiers(ctx) {
|
|
|
34646
35518
|
*/
|
|
34647
35519
|
/** How many of the heaviest entries the over-budget warning names. */
|
|
34648
35520
|
const HEAVIEST = 5;
|
|
35521
|
+
/** No feature holds a bundle: the stack as a leaving scene's non-`feature` bundle sees it. */
|
|
35522
|
+
const NO_FEATURES = Object.freeze(/* @__PURE__ */ new Set());
|
|
34649
35523
|
/**
|
|
34650
35524
|
* Adds up the estimated texture memory of every loaded bundle.
|
|
34651
35525
|
*
|
|
@@ -34712,9 +35586,9 @@ function releaseAssets(io, assets) {
|
|
|
34712
35586
|
assets.audio.clear();
|
|
34713
35587
|
}
|
|
34714
35588
|
/**
|
|
34715
|
-
* Frees one loaded bundle: the textures are destroyed, every companion drops
|
|
34716
|
-
* it, `renderer` is told that its keys are gone and the event goes out. A bundle
|
|
34717
|
-
* loaded is left alone.
|
|
35589
|
+
* Frees one loaded bundle: the textures are destroyed, its warm is forgotten, every companion drops
|
|
35590
|
+
* what travels with it, `renderer` is told that its keys are gone and the event goes out. A bundle
|
|
35591
|
+
* that is not loaded is left alone.
|
|
34718
35592
|
*
|
|
34719
35593
|
* @param ctx - Domain context of the plugin.
|
|
34720
35594
|
* @param bundle - Name of the bundle.
|
|
@@ -34728,6 +35602,7 @@ function unloadBundle(ctx, bundle, reason) {
|
|
|
34728
35602
|
releaseAssets(state.io, record);
|
|
34729
35603
|
record.status = "idle";
|
|
34730
35604
|
record.lastUsed = 0;
|
|
35605
|
+
state.warmed.delete(bundle);
|
|
34731
35606
|
unloadCompanions(ctx, state.companions, bundle);
|
|
34732
35607
|
const keys = entry.files.map((file) => file.key);
|
|
34733
35608
|
ctx.deps.renderer.sync.textures.invalidate(keys);
|
|
@@ -34875,10 +35750,13 @@ function nameSceneBundles(state, named) {
|
|
|
34875
35750
|
/**
|
|
34876
35751
|
* Lets the bundles a leaving scene named go once nothing carries `Exiting` any more: each loaded
|
|
34877
35752
|
* `"leave"` bundle among them that nothing else holds is unloaded with the reason `"leave"`, at
|
|
34878
|
-
* once, not at the next rest.
|
|
34879
|
-
*
|
|
34880
|
-
*
|
|
34881
|
-
*
|
|
35753
|
+
* once, not at the next rest. Its feature on the stack holds only a `feature`-tier bundle, which
|
|
35754
|
+
* lives with its feature's flow: a scene's bundle goes with the scene, also while another scene of
|
|
35755
|
+
* its feature shows (the splash's 7.5 MiB under the ward). A bundle of another release stays, as
|
|
35756
|
+
* it would have. The system `assets.leave` calls it in every phase `sync`: after the reconcile of
|
|
35757
|
+
* `layout`, which tags the leaving elements `Exiting` in the frame after the switch, so the hold
|
|
35758
|
+
* never ends before the leave begins (F51b). It costs a length read while no scene leaves, and a
|
|
35759
|
+
* count read while one does.
|
|
34882
35760
|
*
|
|
34883
35761
|
* @param ctx - Domain context of the plugin.
|
|
34884
35762
|
*/
|
|
@@ -34889,7 +35767,10 @@ function releaseSceneLeft(ctx) {
|
|
|
34889
35767
|
state.sceneLeaving = [];
|
|
34890
35768
|
if (state.io === void 0) return;
|
|
34891
35769
|
const onStack = featuresOnStack(ctx);
|
|
34892
|
-
for (const name of names)
|
|
35770
|
+
for (const name of names) {
|
|
35771
|
+
const tier = state.manifest.bundles[name]?.tier;
|
|
35772
|
+
unloadIfLeft(ctx, name, tier === "feature" ? onStack : NO_FEATURES);
|
|
35773
|
+
}
|
|
34893
35774
|
}
|
|
34894
35775
|
/**
|
|
34895
35776
|
* Unloads one bundle with the reason `"leave"` when it is a loaded `"leave"` bundle nobody holds.
|
|
@@ -35378,9 +36259,125 @@ function startPreload(ctx) {
|
|
|
35378
36259
|
runQueue(ctx, queue).catch(ignoreFailure$1);
|
|
35379
36260
|
}
|
|
35380
36261
|
//#endregion
|
|
36262
|
+
//#region src/plugins/assets/ready.ts
|
|
36263
|
+
/**
|
|
36264
|
+
* @file assets plugin — a bundle ready to show. `warm` loads a bundle and puts its textures on the
|
|
36265
|
+
* GPU before a scene shows them (F60): on a Galaxy A26 the frame that showed 6 cold textures took
|
|
36266
|
+
* 55 ms, 8 ms once they were uploaded before. The `ready` effect lets a node rest on that work and
|
|
36267
|
+
* stay answerable (F52). Nothing here is on the frame path: one promise per bundle, and the inbox
|
|
36268
|
+
* event is posted from a promise.
|
|
36269
|
+
*/
|
|
36270
|
+
/**
|
|
36271
|
+
* Runs one warm: loads the bundle when it is not loaded, then hands every texture and atlas page
|
|
36272
|
+
* of it to the renderer's upload queue. The answer of the upload is not read: when the renderer
|
|
36273
|
+
* could not upload, the first draw does. A failure drops the entry, so the next `warm` tries again.
|
|
36274
|
+
*
|
|
36275
|
+
* @param ctx - Domain context of the plugin.
|
|
36276
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
36277
|
+
* @param entry - The entry of this warm in `state.warmed`.
|
|
36278
|
+
* @returns A promise that resolves once the upload answered.
|
|
36279
|
+
* @throws {Error} When the load fails or is aborted.
|
|
36280
|
+
*/
|
|
36281
|
+
async function runWarm(ctx, bundle, entry) {
|
|
36282
|
+
try {
|
|
36283
|
+
await loadBundle(ctx, bundle, void 0, "request");
|
|
36284
|
+
const { textures, pages } = ctx.state.records.get(bundle) ?? emptyAssets();
|
|
36285
|
+
await ctx.deps.renderer.sync.textures.upload([...textures.values(), ...pages.values()]);
|
|
36286
|
+
entry.done = true;
|
|
36287
|
+
} catch (error) {
|
|
36288
|
+
if (ctx.state.warmed.get(bundle) === entry) ctx.state.warmed.delete(bundle);
|
|
36289
|
+
throw error;
|
|
36290
|
+
}
|
|
36291
|
+
}
|
|
36292
|
+
/**
|
|
36293
|
+
* Starts the one warm of a bundle and records it.
|
|
36294
|
+
*
|
|
36295
|
+
* @param ctx - Domain context of the plugin.
|
|
36296
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
36297
|
+
* @returns The entry every later caller joins.
|
|
36298
|
+
*/
|
|
36299
|
+
function startWarm$1(ctx, bundle) {
|
|
36300
|
+
const entry = {
|
|
36301
|
+
promise: Promise.resolve(),
|
|
36302
|
+
done: false
|
|
36303
|
+
};
|
|
36304
|
+
ctx.state.warmed.set(bundle, entry);
|
|
36305
|
+
entry.promise = runWarm(ctx, bundle, entry);
|
|
36306
|
+
return entry;
|
|
36307
|
+
}
|
|
36308
|
+
/**
|
|
36309
|
+
* Warms one bundle, or joins its running or finished warm. Headless it resolves at once, whatever
|
|
36310
|
+
* the name, as `load` does.
|
|
36311
|
+
*
|
|
36312
|
+
* @param ctx - Domain context of the plugin.
|
|
36313
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
36314
|
+
* @returns A promise that resolves once the bundle is loaded and its textures went to the GPU.
|
|
36315
|
+
* @throws {Error} When the manifest has no such bundle, and when the load fails or is aborted.
|
|
36316
|
+
*/
|
|
36317
|
+
async function warmBundle(ctx, bundle) {
|
|
36318
|
+
const state = ctx.state;
|
|
36319
|
+
if (state.io === void 0) return;
|
|
36320
|
+
if (state.manifest.bundles[bundle] === void 0) throw unknownBundle(bundle);
|
|
36321
|
+
await (state.warmed.get(bundle) ?? startWarm$1(ctx, bundle)).promise;
|
|
36322
|
+
}
|
|
36323
|
+
/**
|
|
36324
|
+
* Tells whether the `ready` effect counts a bundle as there: loaded and warmed, or its last warm
|
|
36325
|
+
* for `ready` failed.
|
|
36326
|
+
*
|
|
36327
|
+
* @param state - The plugin state.
|
|
36328
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
36329
|
+
* @returns True when a node need not rest on it.
|
|
36330
|
+
*/
|
|
36331
|
+
function isThere(state, bundle) {
|
|
36332
|
+
if (state.readyFailed.has(bundle)) return true;
|
|
36333
|
+
return state.records.get(bundle)?.status === "loaded" && state.warmed.get(bundle)?.done === true;
|
|
36334
|
+
}
|
|
36335
|
+
/**
|
|
36336
|
+
* Warms one bundle for the `ready` effect. It never rejects: a failure that is not an abort marks
|
|
36337
|
+
* the bundle failed and is logged, so the node resting on it moves on; an abort (an unload, a stop)
|
|
36338
|
+
* marks nothing, and the next `ready` starts the bundle again.
|
|
36339
|
+
*
|
|
36340
|
+
* @param ctx - Domain context of the plugin.
|
|
36341
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
36342
|
+
* @returns A promise that resolves once the warm settled.
|
|
36343
|
+
*/
|
|
36344
|
+
async function warmForReady(ctx, bundle) {
|
|
36345
|
+
try {
|
|
36346
|
+
await warmBundle(ctx, bundle);
|
|
36347
|
+
} catch (error) {
|
|
36348
|
+
if (isAbortError(error)) return;
|
|
36349
|
+
ctx.state.readyFailed.add(bundle);
|
|
36350
|
+
ctx.log.warn("assets:ready-failed", {
|
|
36351
|
+
bundle,
|
|
36352
|
+
error: String(error)
|
|
36353
|
+
});
|
|
36354
|
+
}
|
|
36355
|
+
}
|
|
36356
|
+
/**
|
|
36357
|
+
* The handler of the `ready` effect (F52). Headless it answers `true` before any name check, as the
|
|
36358
|
+
* `load` effect does. Every bundle there: `true`, and nothing is posted. Otherwise it starts the
|
|
36359
|
+
* warm of each bundle that is not there, answers `false`, and posts `{ type: "ready" }` into
|
|
36360
|
+
* `flow.inbox` once all of them settled. The posting does not wait on the node's signal: the warm
|
|
36361
|
+
* is shared work, and the inbox keeps only the latest `ready`.
|
|
36362
|
+
*
|
|
36363
|
+
* @param ctx - Domain context of the plugin.
|
|
36364
|
+
* @param bundles - The bundle names of the descriptor.
|
|
36365
|
+
* @returns True when the node may go on, false when it should rest until the inbox event `ready`.
|
|
36366
|
+
* @throws {Error} When the manifest has no such bundle.
|
|
36367
|
+
*/
|
|
36368
|
+
function answerReady(ctx, bundles) {
|
|
36369
|
+
const state = ctx.state;
|
|
36370
|
+
if (state.io === void 0) return true;
|
|
36371
|
+
for (const name of bundles) if (state.manifest.bundles[name] === void 0) throw unknownBundle(name);
|
|
36372
|
+
const missing = bundles.filter((name) => !isThere(state, name));
|
|
36373
|
+
if (missing.length === 0) return true;
|
|
36374
|
+
Promise.all(missing.map((name) => warmForReady(ctx, name))).then(() => ctx.deps.flow.inbox.post({ type: "ready" }));
|
|
36375
|
+
return false;
|
|
36376
|
+
}
|
|
36377
|
+
//#endregion
|
|
35381
36378
|
//#region src/plugins/assets/lifecycle.ts
|
|
35382
36379
|
/**
|
|
35383
|
-
* @file assets plugin — lifecycle: the
|
|
36380
|
+
* @file assets plugin — lifecycle: the seven registrations of `onInit`, the boot of `onStart`
|
|
35384
36381
|
* and the teardown that frees every texture and cancels every fetch.
|
|
35385
36382
|
*/
|
|
35386
36383
|
/**
|
|
@@ -35411,7 +36408,7 @@ function withDeps$6(ctx) {
|
|
|
35411
36408
|
};
|
|
35412
36409
|
}
|
|
35413
36410
|
/**
|
|
35414
|
-
* Reads the bundle names out of a `load` descriptor.
|
|
36411
|
+
* Reads the bundle names out of a `load` or a `ready` descriptor.
|
|
35415
36412
|
*
|
|
35416
36413
|
* @param descriptor - What the node awaited.
|
|
35417
36414
|
* @returns The bundle names, in the order the node listed them.
|
|
@@ -35494,17 +36491,17 @@ async function enterNode(ctx, node, run) {
|
|
|
35494
36491
|
}
|
|
35495
36492
|
}
|
|
35496
36493
|
/**
|
|
35497
|
-
* Connects the plugin in `onInit`: the `load` stage of `onEnter`, the
|
|
35498
|
-
*
|
|
35499
|
-
* and the system `assets.leave` of phase `sync`,
|
|
35500
|
-
* elements played their exit (F51). The
|
|
35501
|
-
* but the state.
|
|
36494
|
+
* Connects the plugin in `onInit`: the `load` stage of `onEnter`, the handlers of the `load` and
|
|
36495
|
+
* the `ready` effects (F52), both run in fast mode too, the texture provider of `renderer`, the
|
|
36496
|
+
* start and the end of an exit in `world`, counted, and the system `assets.leave` of phase `sync`,
|
|
36497
|
+
* which lets a leaving scene's bundles go once its elements played their exit (F51). The seven
|
|
36498
|
+
* removers go to the state, because `onStop` has nothing but the state.
|
|
35502
36499
|
*
|
|
35503
36500
|
* @param ctx - Kernel context of the assets plugin.
|
|
35504
36501
|
*/
|
|
35505
36502
|
function connectAssets(ctx) {
|
|
35506
36503
|
const assets = withDeps$6(ctx);
|
|
35507
|
-
assets.state.removers.push(assets.deps.flow.onEnter("load", (node, run) => enterNode(assets, node, run)), assets.deps.flow.fx.handle("load", (descriptor, run) => runLoadEffect(assets, descriptor, run), { runInFast: true }), assets.deps.renderer.sync.textures.provide((key) => lookupTexture(assets, key)), assets.deps.world.ecs.onAdded(Exiting, () => {
|
|
36504
|
+
assets.state.removers.push(assets.deps.flow.onEnter("load", (node, run) => enterNode(assets, node, run)), assets.deps.flow.fx.handle("load", (descriptor, run) => runLoadEffect(assets, descriptor, run), { runInFast: true }), assets.deps.flow.fx.handle("ready", (descriptor) => answerReady(assets, bundlesOfDescriptor(descriptor)), { runInFast: true }), assets.deps.renderer.sync.textures.provide((key) => lookupTexture(assets, key)), assets.deps.world.ecs.onAdded(Exiting, () => {
|
|
35508
36505
|
assets.state.exiting += 1;
|
|
35509
36506
|
}), assets.deps.world.ecs.onRemoved(Exiting, () => {
|
|
35510
36507
|
assets.state.exiting -= 1;
|
|
@@ -35584,7 +36581,7 @@ async function startAssets(ctx) {
|
|
|
35584
36581
|
}
|
|
35585
36582
|
/**
|
|
35586
36583
|
* Frees everything in `onStop`: the preload and every running load are aborted, every texture is
|
|
35587
|
-
* destroyed, the
|
|
36584
|
+
* destroyed, the seven registrations are removed and the registries are emptied, the companions
|
|
35588
36585
|
* too. A teardown context carries the state and nothing else, so no other plugin is called here:
|
|
35589
36586
|
* no companion's `unload` either, its plugin stopped before this one.
|
|
35590
36587
|
*
|
|
@@ -35607,6 +36604,8 @@ function releaseAll(state) {
|
|
|
35607
36604
|
state.featureOfFlow.clear();
|
|
35608
36605
|
state.pinned.clear();
|
|
35609
36606
|
state.warned.clear();
|
|
36607
|
+
state.warmed.clear();
|
|
36608
|
+
state.readyFailed.clear();
|
|
35610
36609
|
state.companions.length = 0;
|
|
35611
36610
|
state.current = void 0;
|
|
35612
36611
|
state.sceneId = void 0;
|
|
@@ -35753,6 +36752,7 @@ function createAssetsApi(ctx) {
|
|
|
35753
36752
|
const assets = withDeps$6(ctx);
|
|
35754
36753
|
return {
|
|
35755
36754
|
load: (bundle) => loadBundle(assets, bundle, void 0, "request"),
|
|
36755
|
+
warm: (bundle) => warmBundle(assets, bundle),
|
|
35756
36756
|
unload: (bundle) => unload(assets, bundle),
|
|
35757
36757
|
isLoaded: (bundle) => isLoaded(assets, bundle),
|
|
35758
36758
|
texture: (key) => lookupTexture(assets, key),
|
|
@@ -36430,7 +37430,9 @@ function createAssetsState() {
|
|
|
36430
37430
|
sceneLeaving: [],
|
|
36431
37431
|
exiting: 0,
|
|
36432
37432
|
companions: [],
|
|
36433
|
-
restPending: false
|
|
37433
|
+
restPending: false,
|
|
37434
|
+
readyFailed: emptySet$2(),
|
|
37435
|
+
warmed: emptyMap$3()
|
|
36434
37436
|
};
|
|
36435
37437
|
}
|
|
36436
37438
|
/**
|
|
@@ -41339,7 +42341,7 @@ function complete(ectx) {
|
|
|
41339
42341
|
ectx.log.info("effects:checks", {
|
|
41340
42342
|
definitions: checks.checked,
|
|
41341
42343
|
broken: checks.failed,
|
|
41342
|
-
ms: tenths(pageNow() - checks.startedAt)
|
|
42344
|
+
ms: tenths(pageNow$1() - checks.startedAt)
|
|
41343
42345
|
});
|
|
41344
42346
|
checks.resolve();
|
|
41345
42347
|
}
|
|
@@ -41482,7 +42484,7 @@ function checkGlsl(ectx, run, definition) {
|
|
|
41482
42484
|
function startChecks(ectx) {
|
|
41483
42485
|
const { checks } = ectx.state;
|
|
41484
42486
|
const backend = ectx.deps.renderer.host.kind();
|
|
41485
|
-
checks.startedAt = pageNow();
|
|
42487
|
+
checks.startedAt = pageNow$1();
|
|
41486
42488
|
for (const definition of checkList(ectx)) {
|
|
41487
42489
|
if (checks.status.has(definition)) continue;
|
|
41488
42490
|
checks.status.set(definition, "pending");
|
|
@@ -41618,12 +42620,12 @@ async function gateChecks(ectx, node, run) {
|
|
|
41618
42620
|
if (!ectx.state.checks.started) startChecks(ectx);
|
|
41619
42621
|
const { checks } = ectx.state;
|
|
41620
42622
|
if (checks.waiting === 0) return;
|
|
41621
|
-
const began = pageNow();
|
|
42623
|
+
const began = pageNow$1();
|
|
41622
42624
|
await Promise.race([checks.settled, whenAborted(run.signal)]);
|
|
41623
42625
|
if (run.signal.aborted) return;
|
|
41624
42626
|
ectx.log.info("effects:mount-waited", {
|
|
41625
42627
|
node: node.path,
|
|
41626
|
-
ms: tenths(pageNow() - began)
|
|
42628
|
+
ms: tenths(pageNow$1() - began)
|
|
41627
42629
|
});
|
|
41628
42630
|
}
|
|
41629
42631
|
//#endregion
|
|
@@ -49781,9 +50783,23 @@ function buildRun(ctx, pixi, run, style, at) {
|
|
|
49781
50783
|
return objects;
|
|
49782
50784
|
}
|
|
49783
50785
|
/**
|
|
50786
|
+
* Tells whether a label of a `"box"` style is drawn outside a ui layout: it has no box, it is not
|
|
50787
|
+
* empty (ui lays an empty one out 0 wide), and no layout measured its style at a width (a laid-out
|
|
50788
|
+
* label that fits its row gets no box, F65).
|
|
50789
|
+
*
|
|
50790
|
+
* @param ctx - Domain context of the text plugin.
|
|
50791
|
+
* @param style - The style of the label.
|
|
50792
|
+
* @param value - The component value.
|
|
50793
|
+
* @returns True when the label wraps at no box for want of a layout.
|
|
50794
|
+
*/
|
|
50795
|
+
function isOutsideLayout(ctx, style, value) {
|
|
50796
|
+
if (style.wrap !== "box") return false;
|
|
50797
|
+
return (value.box ?? 0) <= 0 && value.resolved !== "" && !ctx.state.laidOut.has(value.style);
|
|
50798
|
+
}
|
|
50799
|
+
/**
|
|
49784
50800
|
* The style and the laid-out block of one component value, under the generation of this moment.
|
|
49785
|
-
* A `"box"` style wraps at the box `ui` wrote; with none
|
|
49786
|
-
*
|
|
50801
|
+
* A `"box"` style wraps at the box `ui` wrote; with none it is one line, and a dev warning says
|
|
50802
|
+
* so once per style unless a layout measured that style at a width (a label that fits its row).
|
|
49787
50803
|
*
|
|
49788
50804
|
* @param ctx - Domain context of the text plugin.
|
|
49789
50805
|
* @param value - The component value.
|
|
@@ -49791,7 +50807,7 @@ function buildRun(ctx, pixi, run, style, at) {
|
|
|
49791
50807
|
*/
|
|
49792
50808
|
function labelOf(ctx, value) {
|
|
49793
50809
|
const style = styleOf$1(ctx, value.style);
|
|
49794
|
-
if (
|
|
50810
|
+
if (isOutsideLayout(ctx, style, value)) warnOnce(ctx, `wrap-box:${value.style}`, "text: wrap \"box\" outside layout", { style: value.style });
|
|
49795
50811
|
return {
|
|
49796
50812
|
style,
|
|
49797
50813
|
layout: layoutFor(ctx, value.resolved, value.style, value.box),
|
|
@@ -50159,6 +51175,7 @@ function stopText(state) {
|
|
|
50159
51175
|
state.added.length = 0;
|
|
50160
51176
|
state.rewrite.length = 0;
|
|
50161
51177
|
state.warned.clear();
|
|
51178
|
+
state.laidOut.clear();
|
|
50162
51179
|
}
|
|
50163
51180
|
//#endregion
|
|
50164
51181
|
//#region src/plugins/text/api.ts
|
|
@@ -50183,6 +51200,7 @@ function createTextApi(ctx) {
|
|
|
50183
51200
|
return {
|
|
50184
51201
|
measure: (content, style, width = 0) => {
|
|
50185
51202
|
const tctx = text();
|
|
51203
|
+
if (width > 0) tctx.state.laidOut.add(style);
|
|
50186
51204
|
return sizeOf(layoutFor(tctx, sourceOf(tctx, content), style, width));
|
|
50187
51205
|
},
|
|
50188
51206
|
hasGlyph: (char, style) => hasGlyphOf(text(), char, style),
|
|
@@ -50311,6 +51329,7 @@ function createTextState$1() {
|
|
|
50311
51329
|
rewrite: [],
|
|
50312
51330
|
generation: 0,
|
|
50313
51331
|
warned: emptySet$1(),
|
|
51332
|
+
laidOut: emptySet$1(),
|
|
50314
51333
|
removers: []
|
|
50315
51334
|
};
|
|
50316
51335
|
}
|
|
@@ -52766,6 +53785,26 @@ function nineSliceClipped(element) {
|
|
|
52766
53785
|
};
|
|
52767
53786
|
}
|
|
52768
53787
|
/**
|
|
53788
|
+
* Reports an element whose style is `hollow` but names no nine-slice: only a nine-slice has a
|
|
53789
|
+
* centre to leave out, so the field does nothing there. `hollow: false` asks for nothing either way.
|
|
53790
|
+
*
|
|
53791
|
+
* @param element - The element to check.
|
|
53792
|
+
* @returns The finding, or `undefined`.
|
|
53793
|
+
* @example
|
|
53794
|
+
* ```ts
|
|
53795
|
+
* hollowWithoutNineSlice({ key: "memFrost", type: "stack", style: { hollow: true } } as Element);
|
|
53796
|
+
* // { rule: "hollow-without-nine-slice", key: "memFrost", detail: "stack" }
|
|
53797
|
+
* ```
|
|
53798
|
+
*/
|
|
53799
|
+
function hollowWithoutNineSlice(element) {
|
|
53800
|
+
if (element.style.hollow !== true || element.style.nineSlice !== void 0) return void 0;
|
|
53801
|
+
return {
|
|
53802
|
+
rule: "hollow-without-nine-slice",
|
|
53803
|
+
key: nameOf(element),
|
|
53804
|
+
detail: element.type
|
|
53805
|
+
};
|
|
53806
|
+
}
|
|
53807
|
+
/**
|
|
52769
53808
|
* Reports a root element whose style sets a `zIndex`: a root draws at the order of its layer,
|
|
52770
53809
|
* so the value is ignored.
|
|
52771
53810
|
*
|
|
@@ -52786,7 +53825,7 @@ function zIndexOnRoot(element) {
|
|
|
52786
53825
|
};
|
|
52787
53826
|
}
|
|
52788
53827
|
/**
|
|
52789
|
-
* Runs the
|
|
53828
|
+
* Runs the six rules over every live element.
|
|
52790
53829
|
*
|
|
52791
53830
|
* @param ctx - Domain context of the ui plugin.
|
|
52792
53831
|
* @returns One finding per rule and element, in element order.
|
|
@@ -52801,6 +53840,7 @@ function runLint(ctx) {
|
|
|
52801
53840
|
textOverflow(ctx, element),
|
|
52802
53841
|
absoluteWithoutReason(element),
|
|
52803
53842
|
nineSliceClipped(element),
|
|
53843
|
+
hollowWithoutNineSlice(element),
|
|
52804
53844
|
zIndexOnRoot(element)
|
|
52805
53845
|
]) if (finding !== void 0) findings.push(finding);
|
|
52806
53846
|
}
|
|
@@ -55453,33 +56493,40 @@ function createReconciler(ctx, modules, fields) {
|
|
|
55453
56493
|
*
|
|
55454
56494
|
* @param element - The element that left the description.
|
|
55455
56495
|
* @param cause - Why it leaves: `"change"` for a commit, `"mount"` when it goes with its screen.
|
|
56496
|
+
* @param leads - The element is the top of a root that goes with its screen.
|
|
55456
56497
|
*/
|
|
55457
|
-
function exitElement(element, cause) {
|
|
56498
|
+
function exitElement(element, cause, leads = false) {
|
|
55458
56499
|
forgetSubtree(element);
|
|
55459
56500
|
forgetInstances(state, element.identity);
|
|
55460
56501
|
state.exiting.add(element.entity);
|
|
55461
|
-
exitSubtree(element, cause);
|
|
56502
|
+
exitSubtree(element, cause, false, leads);
|
|
55462
56503
|
}
|
|
55463
56504
|
/**
|
|
55464
56505
|
* Plays the exit hook of an element and of everything under it, each told why it leaves. A child
|
|
55465
|
-
* keeps its rect and its `Exiting` tag until the whole subtree stopped moving.
|
|
56506
|
+
* keeps its rect and its `Exiting` tag until the whole subtree stopped moving. Under the top of a
|
|
56507
|
+
* root that goes with its screen and plays an exit of its own, the elements stand and go with it
|
|
56508
|
+
* (F55b): an exit of theirs would change the subtree in every frame of the top's, so a kept
|
|
56509
|
+
* picture of it would be drawn live through its filters.
|
|
55466
56510
|
*
|
|
55467
56511
|
* @param element - The element that leaves, or one of its children.
|
|
55468
56512
|
* @param cause - Why the subtree leaves.
|
|
56513
|
+
* @param stands - The element stands under an exit that leads the leave: its own hook does not play.
|
|
56514
|
+
* @param leads - The element is the top of a root that goes with its screen.
|
|
55469
56515
|
*/
|
|
55470
|
-
function exitSubtree(element, cause) {
|
|
56516
|
+
function exitSubtree(element, cause, stands, leads) {
|
|
55471
56517
|
dropHost(state, element.entity);
|
|
55472
56518
|
state.scrolls.delete(element.entity);
|
|
55473
|
-
if (overlayOf(element) === void 0) modules.layout.exit(element, cause);
|
|
56519
|
+
if (overlayOf(element) === void 0) modules.layout.exit(element, cause, void 0, stands);
|
|
55474
56520
|
else {
|
|
55475
56521
|
const span = createSpan();
|
|
55476
|
-
modules.layout.exit(element, cause, span);
|
|
56522
|
+
modules.layout.exit(element, cause, span, stands);
|
|
55477
56523
|
exitOverlay(ctx, modules.layout, element, span);
|
|
55478
56524
|
}
|
|
55479
56525
|
if (element.type === "input") fields.exit(element);
|
|
56526
|
+
const under = stands || leads && !modules.layout.settled(element);
|
|
55480
56527
|
for (const child of element.children) {
|
|
55481
56528
|
const childElement = state.elements.get(child);
|
|
55482
|
-
if (childElement !== void 0) exitSubtree(childElement, cause);
|
|
56529
|
+
if (childElement !== void 0) exitSubtree(childElement, cause, under, false);
|
|
55483
56530
|
}
|
|
55484
56531
|
}
|
|
55485
56532
|
/**
|
|
@@ -56186,7 +57233,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
56186
57233
|
state.removing.add(entity);
|
|
56187
57234
|
hostsMoved(state);
|
|
56188
57235
|
if (element !== void 0) {
|
|
56189
|
-
exitElement(element, cause);
|
|
57236
|
+
exitElement(element, cause, cause === "mount");
|
|
56190
57237
|
if (cause === "mount" && !modules.layout.settled(element)) state.leading.add(element.entity);
|
|
56191
57238
|
}
|
|
56192
57239
|
ctx.deps.time.wake();
|
|
@@ -57174,15 +58221,16 @@ const EXIT_WAITS_FOREVER = "ui:exit-waits-forever";
|
|
|
57174
58221
|
* @param element - The element that left the description.
|
|
57175
58222
|
* @param cause - Why it leaves: a `when: "change"` hook plays nothing for `"mount"`, and the next sweep takes it.
|
|
57176
58223
|
* @param span - Notes the longest finite track the exit hook starts, for the level of an overlay.
|
|
58224
|
+
* @param stands - The element stands and goes with the exit over it: its own exit hook does not play (F55b).
|
|
57177
58225
|
*/
|
|
57178
|
-
function beginExit(ctx, state, element, cause, span) {
|
|
58226
|
+
function beginExit(ctx, state, element, cause, span, stands = false) {
|
|
57179
58227
|
detachNode(state, element);
|
|
57180
58228
|
element.dropKey?.();
|
|
57181
58229
|
element.dropKey = void 0;
|
|
57182
58230
|
if (!element.live) return;
|
|
57183
58231
|
ctx.deps.world.ecs.tag(element.entity, Exiting);
|
|
57184
58232
|
element.handles = keptExtras(ctx, element);
|
|
57185
|
-
const hook = element.motion?.exit;
|
|
58233
|
+
const hook = stands ? void 0 : element.motion?.exit;
|
|
57186
58234
|
play(ctx, element, hook === void 0 ? void 0 : (view) => hook(view, element.node, void 0, cause), span);
|
|
57187
58235
|
}
|
|
57188
58236
|
/**
|
|
@@ -58611,7 +59659,7 @@ function createLayoutApi(ctx) {
|
|
|
58611
59659
|
changeExtra: (element, name, values, hint) => playExtraChange(ctx, element, name, values, hint),
|
|
58612
59660
|
repose: (element, parent, hooked, hint) => repose(ctx, element, parent, hooked, hint),
|
|
58613
59661
|
lift: (element, units) => liftRest(ctx, element, units),
|
|
58614
|
-
exit: (element, cause, span) => beginExit(ctx, state, element, cause, span),
|
|
59662
|
+
exit: (element, cause, span, stands) => beginExit(ctx, state, element, cause, span, stands),
|
|
58615
59663
|
playTrack: (element, hook) => {
|
|
58616
59664
|
play(ctx, element, hook);
|
|
58617
59665
|
},
|
|
@@ -59985,19 +61033,48 @@ function load(bundle) {
|
|
|
59985
61033
|
};
|
|
59986
61034
|
}
|
|
59987
61035
|
/**
|
|
59988
|
-
*
|
|
61036
|
+
* Creates the effect a node awaits to rest on bundles and stay answerable (F52). The handler of the
|
|
61037
|
+
* kind `"ready"` is registered by this plugin and runs in fast mode too. It answers `true` when every
|
|
61038
|
+
* bundle is loaded and its textures went to the GPU (`assets.warm`), or its last load failed, so a
|
|
61039
|
+
* node never hangs on it. Otherwise it starts what is missing, answers `false`, and posts the world
|
|
61040
|
+
* event `ready` into `flow.inbox` once all of it settled: the node rests on `inbox: ["ready"]`
|
|
61041
|
+
* meanwhile, and Back still answers. A headless `assets` answers `true`; an app without `assets`,
|
|
61042
|
+
* such as the headless app of `game.headless()`, resolves `undefined`, hence `=== false`. A name
|
|
61043
|
+
* the manifest lacks throws, as for `load`.
|
|
61044
|
+
*
|
|
61045
|
+
* @param bundle - One bundle name, or a list of them.
|
|
61046
|
+
* @returns The descriptor a transit node awaits.
|
|
61047
|
+
* @example
|
|
61048
|
+
* ```ts
|
|
61049
|
+
* ready("ep01.memory"); // { kind: "ready", payload: { bundles: ["ep01.memory"] } }
|
|
61050
|
+
* // The transit node in front of the rest node `wait` (inbox: ["elapsed", "ready"]):
|
|
61051
|
+
* if ((await fx(ready(["ep01.memory", "ui"]))) === false) return out.wait(); // false: still loading
|
|
61052
|
+
* return out.open(); // true: loaded and on the GPU
|
|
61053
|
+
* ```
|
|
61054
|
+
*/
|
|
61055
|
+
function ready(bundle) {
|
|
61056
|
+
return {
|
|
61057
|
+
kind: "ready",
|
|
61058
|
+
payload: { bundles: typeof bundle === "string" ? [bundle] : [...bundle] }
|
|
61059
|
+
};
|
|
61060
|
+
}
|
|
61061
|
+
/**
|
|
61062
|
+
* Binds `defineBundles`, `load` and `ready` to one game's bundle keys. Type-only: the same
|
|
61063
|
+
* functions.
|
|
59989
61064
|
*
|
|
59990
|
-
* @returns The
|
|
61065
|
+
* @returns The three helpers whose keys are checked by the compiler.
|
|
59991
61066
|
* @example
|
|
59992
61067
|
* ```ts
|
|
59993
|
-
* const { load } = bundlesFor<"board" | "board.chains">();
|
|
61068
|
+
* const { load, ready } = bundlesFor<"board" | "board.chains">();
|
|
59994
61069
|
* load("board.chains"); // { kind: "load", payload: { bundles: ["board.chains"] } }
|
|
61070
|
+
* ready("board"); // { kind: "ready", payload: { bundles: ["board"] } }
|
|
59995
61071
|
* ```
|
|
59996
61072
|
*/
|
|
59997
61073
|
function bundlesFor() {
|
|
59998
61074
|
return {
|
|
59999
61075
|
defineBundles,
|
|
60000
|
-
load
|
|
61076
|
+
load,
|
|
61077
|
+
ready
|
|
60001
61078
|
};
|
|
60002
61079
|
}
|
|
60003
61080
|
//#endregion
|
|
@@ -61203,6 +62280,7 @@ function cubicBezier(x1, y1, x2, y2) {
|
|
|
61203
62280
|
* | renderer | monitor | { budgetMs: "rate", frames: 1024, stillFrames: 60, stillMpx: 0.25, churnPerSecond: 4, churnSeconds: 3, churnAfterTrimMs: 1000 }, the performance log of an agent build, the thresholds of its `problem` rules and `sections`, the sections it starts with (`MonitorSections`, every one on when left out); shallow merge: a game that sets `monitor` gives `budgetMs` and `frames`, a threshold it leaves out keeps its default, a section it leaves out is on |
|
|
61204
62281
|
* | renderer | keptMb | 64, MiB the kept pictures may hold: a filtered view that did not change is drawn from its picture, the least recently drawn goes first; 0 keeps none |
|
|
61205
62282
|
* | renderer | warm | 1, probe draws a drawn frame that create the GPU pipelines while the game loads; 0 creates each in the first frame that needs it |
|
|
62283
|
+
* | renderer | uploads | 1, texture sources `sync.textures.upload` puts on the GPU a drawn frame (~9 ms each on the A26); 0 turns the queue off |
|
|
61206
62284
|
* | input | tapSlopPx | 12 |
|
|
61207
62285
|
* | input | longPressMs | 450 |
|
|
61208
62286
|
* | input | dragStartPx | 8 |
|
|
@@ -61360,4 +62438,4 @@ const screen = [
|
|
|
61360
62438
|
uiPlugin
|
|
61361
62439
|
];
|
|
61362
62440
|
//#endregion
|
|
61363
|
-
export {
|
|
62441
|
+
export { sfx as $, refuseStart as $t, types_exports$10 as A, Noise as At, defineMotion as B, Frames as Bt, types_exports$3 as C, effectsPlugin as Ct, types_exports$7 as D, ColorMatrix as Dt, types_exports$6 as E, Blur as Et, types_exports$15 as F, scenesPlugin as Ft, external as G, PERF_CAUSES as Gt, platformPlugin as H, PASS_KINDS as Ht, types_exports$16 as I, assetsPlugin as It, mark as J, PROBLEM_RULES as Jt, frames as K, PERF_GAP_BUCKETS as Kt, types_exports$17 as L, animPlugin as Lt, types_exports$12 as M, defineFilter as Mt, types_exports$13 as N, debugPlugin as Nt, types_exports$8 as O, Displacement as Ot, types_exports$14 as P, audioPlugin as Pt, set as Q, flowPlugin as Qt, uiPlugin as R, AnimPlayer as Rt, types_exports$2 as S, messageDuration as St, types_exports$5 as T, Alpha as Tt, HAPTIC_KINDS as U, frameAt as Ut, textPlugin as V, rendererPlugin as Vt, defineAnimation as W, MONITOR_SECTIONS as Wt, play$1 as X, DEMAND_REASONS as Xt, parallel as Y, SHADER_WHATS as Yt, sequence as Z, worldPlugin as Zt, defineBundles as _, Touchable as _t, cubicBezier as a, SaveUnreadableError as an, wait as at, types_exports as b, i18nPlugin as bt, hint as c, clockPlugin as cn, DropTarget as ct, defineScene as d, Pointer as dt, exit as en, spawn as et, tr as f, PointerOver as ft, music as g, Tappable as gt, defineEmitter as h, Swipeable as ht, screen as i, modelPlugin as in, use as it, types_exports$11 as j, Outline as jt, types_exports$9 as k, Glow as kt, schedule as l, Held as lt, flattenFeatures as m, Pressed as mt, createPlugin as n, to as nn, stagger as nt, defineEffect as o, lifecyclePlugin as on, inputPlugin as ot, defineFeature as p, Pressable as pt, haptic as q, PERF_LOG_VERSION as qt, defineGame as r, type as rn, tween as rt, guide as s, timePlugin as sn, Draggable as st, createApp as t, slot as tn, spawned as tt, projection as u, Hovered as ut, load as v, Traceable as vt, types_exports$4 as w, Emitter as wt, types_exports$1 as x, messageArgument as xt, ready as y, Traced as yt, resolve as z, Animation as zt };
|