0.2.6-dev: DESIGN §31.6 impostor bounds and slot clearing (#542: seen, cause, now, cost, [ASSUMED], tests), docs/design/client.md describes the impostor box and slot clipping, CHANGELOG 0.2.6-dev 界面 credits @ImPQlc; golden: unchanged
This commit is contained in:
1 parent
c45d86f70c
commit
0b88a0f515
3 files changed
+53
-1
No files matched your search
@@ -104,3 +104,54 @@ keeps finished players for display, so its total length cannot establish that a
|
||||
counts order entries absent from `draft.picks`, alongside the existing turn and quota checks. The server's
|
||||
`skipBand` already rejects the final player with `BAD_TARGET / nobody to pass to`; its rule remains unchanged.
|
||||
Regression covers normal two-, three- and four-human browser flows, the final turn, and exhausted quota.
|
||||
|
||||
### 31.6 Impostor bounds and slot clearing (#542)
|
||||
|
||||
`render/spine.js clipBounds / stepClipBounds`, `render/units.js impostorBox / _impBox / _impBypass`, `render/impostor.js`.
|
||||
|
||||
- **Seen** (GitHub #542 by @ImPQlc; quality 高, usually from round 7 on): 野鬃's flag cut, offset and leaving stray pixels
|
||||
while she idles; 浊心斯卡蒂's head cut in her S3 flight (Skill_3_Begin); 隐现's pistol muzzle cut, offset and leaving
|
||||
stray pixels during her skill.
|
||||
- **Cause** (reproduced in headless Chrome: the dev demo's stress crowd, 120 units at impostor interval 4, the three held
|
||||
in their widest poses): a unit's impostor slot was sized once from the pose it showed when it first became an impostor,
|
||||
plus a margin (`_impBox`: the pose's bounds + 30 % / 22 %). A later pose past that box was drawn past the slot's edge:
|
||||
cut in the unit's own sprite and painted into whatever lay beside the slot on the atlas page. A page is cleared on its
|
||||
first flush only and each eraser covers its own slot, so a spill into free space stayed, and a spill into another unit's
|
||||
slot showed on that unit until it was redrawn in a frame without the spill. 浊心斯卡蒂's flight reaches y −704 (skeleton
|
||||
units) against a box top near −516 (54–57 px past her slot in 5 of 5 runs); 野鬃's idle flag reaches x −434, 103 units
|
||||
past a box sized from her setup pose (0–47 px past her slot over 5 runs: it depended on that first pose); after one clean
|
||||
redraw of every impostor the atlas pages held 0–1,405 pixels outside every live slot. 隐现's own poses fit her box (the
|
||||
silencer reaches x 197, her box 225): her muzzle sits at her slot's edge, where a neighbour's spill lands. Of the
|
||||
roster's 681 skeletons, 598 play a clip that leaves a box sized that way from their idle's first frame. No clipping
|
||||
attachment is involved (`spine.js _eyeMaskFallback`, #177, unchanged).
|
||||
- **Now**: (1) every clip a unit can play (its manifest roles with every skill's set and the Run cycle, its `FORMS` clip
|
||||
sets and change clips) is measured once per skeleton data on a scratch skeleton: 60 poses a second, at most 49 per clip;
|
||||
a region by its attachment rectangle, a mesh by its vertices; slots drawn at alpha 0 skipped. It is queued when the model
|
||||
loads and sampled within 2.5 ms per frame (`stepClipBounds`, called by `render/app.js`), ≈ 9 ms per skeleton on the
|
||||
roster (53 ms at most), shared by every unit of that skeleton; a unit is drawn directly until its box is ready. (2) The
|
||||
box is the setup pose ∪ the idle clip (never smaller than the setup pose), then each other clip in the order of the area
|
||||
it adds while the box stays within 4 × that core (a looping or held clip) or 2 × (a one-off clip: the deploy clip, a
|
||||
skill's, stun's or move's begin and end, a form's change), padded by 3 % + 8 units. A clip that does not fit (`over`: 319
|
||||
of the roster's 681 deploy clips — the drop from above, up to 1,700 units tall — and the looping clips of 6 skeletons with
|
||||
screen-wide effects) is drawn directly while it plays; the unit keeps its slot (`_impBypass`). The box is computed once
|
||||
per unit: no bounds work per frame. (3) The atlas draws each slot's skeleton in a container masked by the slot rectangle,
|
||||
which PIXI draws as a scissor rect (no stencil, no render-pass break): nothing paints outside a slot, and the slot's
|
||||
eraser clears all that an earlier pose can have left in it.
|
||||
- **Cost**: slot sizes grow with the boxes. The 120-unit stress crowd at 1600 × 900 (DPR 1) uses 8.7 M px of slots instead
|
||||
of 4.4 M (2.7 instead of 1.2 atlas pages, still no private render target). On the roster the boxes add up to 1.86 × the
|
||||
old area (the median skeleton 1.39 ×; without the setup-pose floor it would be 1.23 ×). The atlas keeps its ≤ 3 + 2 pages;
|
||||
a unit that finds no slot keeps a private render target, as before. CPU: one batch flush per redrawn slot.
|
||||
`tools/perfbench.mjs` (844 × 390, DPR 2, quality medium / low, where those battles draw through the atlas; 12 runs of
|
||||
6 s): total busy JS +0.8 %, the impostor-redraw stage +14 % (+0.1–0.25 ms per frame at a 4 × CPU throttle), 60 fps in
|
||||
every run before and after.
|
||||
- [ASSUMED]: the growth limits (4 × / 2 ×), the 3 % + 8 padding, the sampling rate and the 2.5 ms budget. A pose between two
|
||||
samples can still pass the padded box for a frame (33 of the 681 skeletons by a denser 60 Hz check, all in long clips
|
||||
sampled sparser than that); the scissor cuts it and leaves nothing behind.
|
||||
- Tests: `test/render/impostor-bounds.test.js` (synthetic Spine 3.8 skeletons: setup and clip bounds, an attachment shown
|
||||
for 0.05 s, an alpha-0 slot, the budgeted queue; `impostorBox` / `playedClips`; every atlas slot's clip box and eraser;
|
||||
UnitView: drawn directly until measured, the over-clip bypass keeping its slot, the legacy fallback, a pen figure),
|
||||
`test/render/impostor-bounds-models.test.js` (the three operators through the real runtime: every pose of every clip
|
||||
they play, at 30 Hz, inside their box; the old box cut 野鬃 by 103 units and 浊心斯卡蒂 by 188; skips without the
|
||||
assets), `test/render/impostor-bounds.browser.test.js` (`RENDER_E2E=1`: the held stress crowd — inside their slots, no
|
||||
atlas pixel outside a live slot; the scissor on a bare page; 野鬃's deploy drawn directly), `test/render/boardart.test.js`
|
||||
(the allocator's test doubles). Dev: `render-demo.html?scene=stress&ops=<chessId>,…`. Golden: unchanged.
|
||||
Reference in new issue
Block a user