re(ui): settle 8AX vs ptbase statically -- the game draws the full-res one

I had parked this as "needs a per-draw capture recording texture base
addresses". It did not. 8AX (1280x720) and ptbase (640x360 at 200%) are
the SAME artwork at two resolutions, which is exactly why comparing
either against a capture is inconclusive -- and why comparing their
DIFFERENCE is not. Compute 8AX - upscale(ptbase), the detail only 8AX
has, and ask whether the capture contains it.

Both candidates are first mapped into the capture's tone domain with the
measured gamma; without that the residual is dominated by the tone
difference and the test is blind.

  main menu  corr +0.0475   controls +0.0032 shift, -0.0075 flip   68% of ceiling
  title      corr +0.0634   controls +0.0095 shift, +0.0086 flip   68% of ceiling

Two independent screens, both at 68% of the theoretical ceiling
(sd of the 8AX-only detail over sd of the capture residual), 7-15x their
matched controls. The controls preserve spatial correlation and destroy
only alignment, so they are what "no signal" looks like.

So the recommendation changes: resolve the name and draw 8AX at 1:1.
Upscaling ptbase 2x is wrong, not merely softer. Still do not draw both --
an opaque layer over an identical one costs fill and hides later changes,
and ptbase's element is the one carrying the keyframes, so a consumer
needs its timing with 8AX's pixels.

Also recorded and withdrawn: a cruder pixel-pair test gave 0.00-0.72 for
upscales, 0.98 native and 1.01 for the capture -- apparently decisive.
Additive noise raises both terms of that ratio equally and drives any
value toward 1; fitting a noise term, both "native + noise" and "bilinear
+ noise" reproduce the observed numbers. The conclusion is right, that
test does not establish it, and it is in REFUTED because the number looks
conclusive and is not.

Not shown: whether ptbase is also drawn underneath. 8AX is ~86% opaque
and carries the same art, so it would hide it either way.
This commit is contained in:
Sylpheed RE agent
2026-08-29 02:40:53 +00:00
parent 6c8ee2ee6a
commit 6c4e5d3d71
6 changed files with 140 additions and 15 deletions

View File

@@ -68,17 +68,56 @@ Resolving `pteff05 → 8AX` and drawing it *in addition to* `ptbase` would
one. It would not be visible as a doubling, which is worse: it would silently
cost fill and hide any future change to either layer.
🟡 **The free win, if a port wants it:** use `8AX` at 1:1 and *drop* `ptbase`,
rather than upscaling a 640×360 copy 2×. That is a sharper background from a
texture already in the bundle. Not done here — it is a rendering choice, and
`ptbase`'s element carries the keyframes.
**And that is now the recommendation, not just an option:** use `8AX` at 1:1.
The detail test above says the game's background carries 8AX's full-resolution
detail, so upscaling the 640×360 `ptbase` 2× is *wrong*, not merely softer.
⚠️ Still do not draw **both** — an opaque full-screen layer over an identical one
costs fill and hides any later change to either. `ptbase`'s element carries the
keyframes, so a consumer needs its timing with `8AX`'s pixels.
## ✅ Settled statically: the game draws the FULL-RES `8AX`
This was parked as "needs a per-draw capture". It did not — the two candidates
carry the same art at two resolutions, so what separates them is **the detail
`8AX` has that an upscale cannot**. Compute `8AX upscale(ptbase)` and ask
whether the live capture contains it.
[`tools/re-capture/eightax_detail_test.py`](../../../tools/re-capture/eightax_detail_test.py),
output at [`data/eightax-detail-test.txt`](../data/eightax-detail-test.txt).
Both candidates are first mapped into the capture's tone domain with the measured
gamma ([tone curve](ui-render-tone-curve.md)); without that the capture's
residual is dominated by the tone difference and the test is blind.
| screen | corr | control (shift 7 px) | control (flip) | ceiling | % of ceiling |
|---|---|---|---|---|---|
| main menu | **+0.0475** | +0.0032 | 0.0075 | 0.070 | **68 %** |
| title | **+0.0634** | +0.0095 | +0.0086 | 0.094 | **68 %** |
The "ceiling" is what a clean *"8AX is drawn"* would give, `sd(8AX-only detail) /
sd(capture residual)` — the 8AX-only detail is small (sd 1.94.1) against
everything else that differs (sd 2743), which is why the absolute correlation is
small and why the controls matter more than the magnitude.
**Two independent screens both land at 68 % of ceiling, 715× their matched
controls.** The controls preserve the spatial correlation structure and destroy
only the alignment, so they are what "no signal" looks like here.
### 🔴 An earlier, cruder version of this test does NOT support the conclusion
A pixel-pair test (a 2×-nearest upscale has identical adjacent columns) gave
ratio 0.00 for nearest, 0.600.72 for bilinear/bicubic/lanczos, 0.98 for native
`8AX`, and **1.01 for the capture** — apparently decisive. It is not: additive
noise raises both terms equally and pushes *any* ratio toward 1. Fitting a noise
term, both "native + noise ≈ 1.2" and "bilinear upscale + noise ≈ 1.8" reproduce
the observed numbers. Recorded because the number looks conclusive and is not.
### ⚠️ What it still does not show
Whether `ptbase` is **also** drawn underneath. `8AX` is ~86 % opaque and carries
the same art, so it would hide it either way. The observable fact is that 8AX's
detail reaches the screen.
## ❔ Not established
* **Whether the game draws one, the other, or both.** The pixel evidence says
our background matches `8AX`'s art, which `ptbase` also carries — it cannot
separate them. A per-draw capture of the main menu recording texture *base
addresses* would, since the two textures are different sizes.
* The capture is ~4× darker than the render in these patches (4.5 vs 17.7), and
not by a constant ratio. That is a separate colour/gamma question, untouched
here.