diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index cb528a1..89a4758 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -393,12 +393,18 @@ authored version can be deleted. `a=255`). That is why a splash render needs `--black` rather than the default backdrop. ⚠️ Not a general rule: the main menu's measured order puts one `.prm` at position 4 and the other **last** (the transition fade). - ⚠️ **And the splash's recorded paint order `[0,2,4,6,1,3,5]` is a temporal - sequence, not a depth order.** Its glows and logos never share a frame, so - their relative depth was never observable — the vector records the order they - were *seen in*. Element 0 (the `.prm`) is a genuine depth observation because it - co-occurs with everything. **Do not read the rest as depth and composite all - seven at once** — that is precisely what fails to reproduce the screen. + ✅ **The splash's recorded paint order `[0,2,4,6,1,3,5]` is a real depth + order** — read off the runtime child array, and independently backed by the + static layer key (glows `0xa100` sort before logos `0xa110`; + `paint_order_audit` reports derived == measured, 0 ties). A same-day claim on + this page that it was "a temporal sequence, not depth" was **withdrawn**; see + the ❌ section in the linked page for why, if you read it before the fix. + ⚠️ Separately and still true — and it is an **activation** fact, not a + paint-order one: the splash's glows and logos are never on screen together + (glows f94–115, logos f116–211, + [timeline](../re/data/splash-phase-timeline.txt)). Compositing all seven at + once reproduces no frame of the real screen. Depth says *what covers what*; + it does not say *what is up*. [`structures/ui-prm-primitives.md`](../re/structures/ui-prm-primitives.md) * **Menu order is geometric.** Buttons sorted top-to-bottom by resting Y. This is diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index 0e408a5..68c6fd8 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -826,11 +826,15 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the four-entry enumeration showed none exists, and the question closed statically. A hypothesis that predicts an artefact can be killed by looking for the artefact, which is often far cheaper than measuring the behaviour. -* **Two things that never co-occur have no observable relative order.** A - "measured paint order" recorded from a running game looks like depth - information throughout, but between elements that never share a frame it can - only be the sequence they were *seen in*. The splash's vector mixes both kinds - — element 0 is real depth (it co-occurs with everything), the glow/logo split - is not — and the difference matters, because reading it as depth invites - compositing every element at once. When recording an order, note which pairs - actually appeared together. +* **Check what a "measured" value was measured *from* before reasoning about + its limits.** Two things that never co-occur have no observable relative order + — true, and a real limit on any order read from a *draw capture*. I applied it + to the splash's paint order and withdrew it hours later: that vector is a read + of the runtime **child array**, which has a definite order whether or not its + children are ever drawn together, and the two halves in question also carry + distinct static layer keys. Both the provenance and an independent static field + said the order was real. The general trap is not the co-occurrence rule, it is + applying a source-specific limit to a value from a different source: the word + "measured" covers several kinds of evidence, and the page that recorded it + usually says which — `ui-screen-runtime.md` said "child slots" in as many + words. Read that line before building an argument on top of it. diff --git a/docs/re/REFUTED.md b/docs/re/REFUTED.md index e8937ec..40f53e2 100644 --- a/docs/re/REFUTED.md +++ b/docs/re/REFUTED.md @@ -595,9 +595,13 @@ neighbourhood, not just the line. glows — so whichever was active, a subset of its elements was drawn at a time. Selective activation is reinstated on evidence. [`ui-group-start-time.md`](structures/ui-group-start-time.md) -* "the splash's `measured_paint_order` records a front-to-back depth order" → - **mis-typed.** Its glows and logos never share a frame (0 overlap in 235), so - their relative depth was never observable — between those halves the vector - records the *temporal* order they were seen in. Element 0 (`palogo_eff0.prm`) - is a genuine depth observation, because it co-occurs with everything. - [`ui-prm-primitives.md`](structures/ui-prm-primitives.md) +* ~~"the splash's `measured_paint_order` records a front-to-back depth order" → + mis-typed; between its glow and logo halves it records only the temporal order + they were seen in."~~ → **that refutation is itself refuted (same day).** The + vector is a read of the runtime **child array** (`ui-screen-runtime.md`: + "paint order (child slots)"), not of the draw capture, so co-occurrence does + not bear on it; and the halves carry *distinct* T8aD layer keys + (`0xa100` < `0xa110`, `paint_order_audit`: 0 same-key ties), so the file orders + them regardless. The no-overlap measurement was correct; the inference from it + was not. What survives: a capture of this screen can only cross-check the order + *within* each half. [`ui-prm-primitives.md`](structures/ui-prm-primitives.md) diff --git a/docs/re/data/order-crosscheck-reach.txt b/docs/re/data/order-crosscheck-reach.txt new file mode 100644 index 0000000..932bb8a --- /dev/null +++ b/docs/re/data/order-crosscheck-reach.txt @@ -0,0 +1,23 @@ +# How much of each recorded paint order its draw capture actually cross-checked. +# 2026-08-29, tools/re-capture/order_crosscheck_reach.py +# +# The three measured_paint_order vectors are reads of the runtime CHILD ARRAY +# (ui-screen-runtime.md: "paint order (child slots)"). These captures are the +# cross-check, not the source. Quads = indices/4: a single draw can carry +# several (the menu's draw 9 has indices=24 = 6 quads), so counting draws +# under-reads what is on screen. + +== docs/re/captures/title-screen-draw-order.log + frame 1: 8 draws, 12 quads, 5 textures + frame 2: 8 draws, 12 quads, 5 textures + frame 3: 8 draws, 12 quads, 5 textures + textures in EVERY captured frame: 5 of 5 +== docs/re/captures/title-draw-order-with-alpha.log + frame 1: 8 draws, 12 quads, 5 textures + frame 2: 8 draws, 12 quads, 5 textures + textures in EVERY captured frame: 5 of 5 +== docs/re/captures/main-menu-draw-order.log + frame 0: 9 draws, 16 quads, 5 textures + frame 3: 7 draws, 9 quads, 4 textures + textures in EVERY captured frame: 4 of 5 + absent from some frame: ['11C30000'] diff --git a/docs/re/structures/ui-paint-order-key.md b/docs/re/structures/ui-paint-order-key.md index 6b36bd7..d58aa75 100644 --- a/docs/re/structures/ui-paint-order-key.md +++ b/docs/re/structures/ui-paint-order-key.md @@ -624,3 +624,37 @@ searches produced a positive account. The bit blocks nothing: the port's screens composite at 0.947 correlation against a capture without it. **Parked**, with the search space narrowed and the attribution method (child order, not size) left sound for whoever picks it up. + +## The reach of the draw-capture cross-check (2026-08-29) + +The orders above are read off the **live child list**; the draw captures confirm +them. That confirmation is partial, and this is how partial — counted by +[`order_crosscheck_reach.py`](../../../tools/re-capture/order_crosscheck_reach.py), +data in [`order-crosscheck-reach.txt`](../data/order-crosscheck-reach.txt): + +| screen | captured frames | draws/frame | quads/frame | order length | positions the capture confirms | +|---|---|---|---|---|---| +| title (entry 4) | 3 + 2 | 8 | 12 | 24 | **7** named in [`ui-screen-runtime.md`](ui-screen-runtime.md) | +| main menu (entry 8) | 2 | 9 / 7 | 16 / 9 | 16 | ≤ the richest frame | +| splash (11/14) | 235 | — | — | 7 | within each half only | + +Three things this makes explicit, none of which changes an order: + +* **Count quads, not draws.** The menu's draw 9 is `indices=24` — **six** quads + batched out of one texture. A per-draw count reads 9 where 16 are on screen, + and an earlier pass of this analysis "found" three quads for six declarations + that way and briefly concluded elements were missing. They were batched. +* **The title capture is stable**: 8 draws / 12 quads / 5 textures, byte-for-byte + the same census in every one of the five captured frames across two logs. So + everything it shows co-occurs, and the 7 confirmed positions are mutually + ordered by observation. The other 17 rest on the child-list read alone — which + is the ground truth here, but it is one source, not two. (The census counts 8 + draws where that page names 7 confirmed elements, so the capture carried a + little more than it was read for — not chased, and not a claim either way.) +* **The menu capture is not stable**: texture `0x11C30000` is in frame 0 and gone + by frame 3. Its order is still confirmed as far as any single frame goes. +* **The splash capture cannot cross-check its middle.** Glows (f94–115) and logos + (f116–211) never share a frame, so no capture of this screen orders one against + the other. The static layer key does (`0xa100` < `0xa110`), which is why the + order stands — see the ❌ withdrawn section in + [`ui-prm-primitives.md`](ui-prm-primitives.md). diff --git a/docs/re/structures/ui-prm-primitives.md b/docs/re/structures/ui-prm-primitives.md index 36fae6e..cf28e89 100644 --- a/docs/re/structures/ui-prm-primitives.md +++ b/docs/re/structures/ui-prm-primitives.md @@ -209,19 +209,39 @@ splash renders need `--black` to match. position 4 and `pteff00.prm` **last** — that one is the screen-transition fade. A `.prm` paints where its screen's order says; the splash's happens to be first. -## ⚠️ And the splash's "measured paint order" is a SEQUENCE, not a depth order +## ❌ WITHDRAWN (2026-08-29, same day): "the splash's measured order is a sequence, not depth" -`measured_paint_order` returns `[0, 2, 4, 6, 1, 3, 5]` for the splash, commented -*"the full-screen `.prm`, then all three glows, then the three logos"*. +This page briefly claimed that `measured_paint_order`'s `[0, 2, 4, 6, 1, 3, 5]` +records, between its glow half and its logo half, the *temporal* order they were +seen in rather than a depth order — on the grounds that the two halves never +share a frame (0 overlap in 235, which is +[measured and still true](../data/splash-phase-timeline.txt): glows f94–115, +logos f116–211). -The draw capture shows the glows and the logos **never appear in the same frame** -— 0 overlapping frames in 235 ([group start time](ui-group-start-time.md)). Two -elements that never co-occur have **no observable relative depth**. What that -vector records between its glow half and its logo half is the *temporal* order -they were seen in, not a front-to-back relationship. +**The measurement was right and the conclusion was wrong**, on two independent +grounds, either of which is fatal: -🟡 This does not make the render wrong — compositing them in that order is -harmless, and the ✅ for element 0 (the `.prm` first) *is* a real depth -observation, since it co-occurs with everything. But the type of the claim -matters for a consumer: **treating the vector as depth invites compositing all -seven elements at once, which is exactly what does not reproduce the screen.** +* **Wrong source.** The vector was never read off the draw capture. It is a read + of the live screen object's **child array** — + [`ui-screen-runtime.md`](ui-screen-runtime.md) records it literally as + `paint order (child slots): 0 2 4 6 1 3 5`. A child list has a definite order + whether or not any two of its children are ever drawn together, so a + co-occurrence argument cannot touch it. The capture was the *cross-check*, not + the source. That page had already stated the limit honestly for the title + build: the `kind = 0x4` instances "draw at α=0 and so never appeared in the + capture, but their placement in the list is consistent with the grouping". +* **The order is in the file anyway.** `examples/paint_order_audit` on + `GP_TITLE.pak` entry 11: `derived == measured : YES`, `inverted pairs: 0`, + **`same-layer-key ties: 0`**. The glows and the logos carry *distinct* T8aD + layer keys (`0xa100` vs `0xa110` — [key](ui-paint-order-key.md)), so the glow + half sorts before the logo half **statically**, with no capture involved. The + relationship I called unobservable is decoded. + +What survives is narrower and worth keeping: **a draw capture of this screen +cannot cross-check the glow-vs-logo half of the order**, because the two halves +never co-occur. The capture validates the order *within* each half only. So the +line in [`ui-paint-order-key.md`](ui-paint-order-key.md) — "read off the live +child list, checked against a draw capture" — has that reach limit on the splash. +The order itself is not in doubt; the *capture's* power to confirm all of it is. + +The ✅ above for element 0 is unaffected: it is a direct draw observation. diff --git a/tools/re-capture/order_crosscheck_reach.py b/tools/re-capture/order_crosscheck_reach.py new file mode 100755 index 0000000..3c4b427 --- /dev/null +++ b/tools/re-capture/order_crosscheck_reach.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""How much of a recorded paint order did its draw capture actually cross-check? + +The three `measured_paint_order` vectors in ui_layout.rs are reads of the live +screen object's CHILD ARRAY (ui-screen-runtime.md: "paint order (child slots)"). +The draw captures are the cross-check, not the source -- so they bound how much +of each vector was independently confirmed, nothing more. + +An element that is never drawn in the captured window (alpha 0, or its phase +never ran) contributes no confirmation. This counts what each capture could see: +draws, quads (indices/4 -- batched draws carry several), and distinct textures +per frame, plus which textures fail to appear in every frame. +""" +import re +import sys +import collections + + +def census(path): + frame, per, draws, tex = 0, collections.Counter(), collections.Counter(), collections.defaultdict(set) + for ln in open(path): + if ln.startswith('--- frame'): + frame = int(re.search(r'frame (\d+)', ln).group(1)) + continue + m = re.match(r'\s*(\d+) prim=(\d+) indices=(\d+)', ln) + if not m or int(m.group(2)) != 13: + continue + per[frame] += int(m.group(3)) // 4 + draws[frame] += 1 + t = re.search(r'base=0x([0-9A-F]+)', ln) + tex[frame].add(t.group(1) if t else 'untextured') + return per, draws, tex + + +for path in sys.argv[1:]: + print(f'== {path}') + per, draws, tex = census(path) + if not per: + print(' no prim=13 draws') + continue + for f in sorted(per): + print(f' frame {f}: {draws[f]} draws, {per[f]} quads, {len(tex[f])} textures') + sets = [tex[f] for f in sorted(tex)] + common, allt = set.intersection(*sets), set.union(*sets) + print(f' textures in EVERY captured frame: {len(common)} of {len(allt)}') + if allt - common: + print(f' absent from some frame: {sorted(allt - common)}')