Closes the open second reading in ui-forced-backdrop.md: that a .tbm contributes no pixels, leaving 24 of its 62 deciding verdicts harmless rather than correct. The TUTORIAL screen was reached and captured. It carries a full-screen blue circuit/hex background. GP_TUTORIAL build 0's element 0 is pubase.tbm with pivot (640,360) -- 1280x720, the only full-screen TEXTURED element in the bundle; the one other full-screen element is pueff00.prm, an untextured primitive the colour census puts at pure black. Our render of the same build is the identical layout on pure black, 6.0-6.4 % inked against the game's 99.7 %. The only difference is the background and the only thing it can be is the .tbm. So the 24 .tbm verdicts are correct rather than harmless, and they are load-bearing in the full sense. Reach: one .tbm observed; the class question is settled, the ten other families are not individually seen. Also: screen render is wrong on every screen carrying a .tbm -- it drops the background silently, with no diagnostic. And the identification is worth its own METHOD entry. Two statistical identifiers were built. Masked correlation FAILED its control, picking EXTRAS over the known main menu by 0.004 because the shared background dominates. A high-passed variant PASSED by 1.28x, which is not a margin that licenses identifying an unknown, so it was not used. The screen says TUTORIAL across the top. Ask whether the artefact already states the answer before building a matcher. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
475 lines
25 KiB
Markdown
475 lines
25 KiB
Markdown
# Where a keyless primitive paints, when the file forces it
|
||
|
||
**Classification: decoded.** Derived from the keyframes and the element's own
|
||
geometry, checked disc-wide, and validated against **both** primitives whose
|
||
position was measured in the running game — one it must reproduce, one it must
|
||
not disturb.
|
||
|
||
## The question
|
||
|
||
A `.prm` / `.tbm` primitive carries **no layer key**: the key is read from a
|
||
sprite's header, and a primitive has no sprite.
|
||
[`ui-prm-primitives.md`](ui-prm-primitives.md) established that the key is not in
|
||
the bundle either — the declaration entry's unread words are constant, and the
|
||
bundle has no RATC child for a primitive — so `implied_layer_key` is a **measured
|
||
per-name table**, and anything not in it keeps `u32::MAX` and sorts **last**.
|
||
|
||
"❔ Where an *unmeasured* primitive paints" has been that page's standing blocker.
|
||
|
||
The port hit it: `build_12` / `build_15` composited to solid black at every
|
||
instant of their declared life, because `pgloading_eff00.prm` — a full-screen
|
||
opaque quad — sorted on top.
|
||
|
||
## The rule
|
||
|
||
> **An element that covers the screen and is fully opaque at some instant cannot
|
||
> paint above anything visible at that instant.** Where the elements visible
|
||
> during its opaque span are *all* of them, its position is forced to first.
|
||
|
||
This is a constraint read off the file, not a preference, and it is not an
|
||
analogy to a neighbouring screen.
|
||
|
||
`pgloading_eff00.prm` is opaque for **39** instants, and **all 9** other elements
|
||
on the loading screen are visible inside that span. **Forced first**, 4/4
|
||
instances.
|
||
|
||
## The controls — the rule has to survive both, and it does
|
||
|
||
| primitive | measured in the game | opaque instants | forced below | rule says |
|
||
|---|---|---|---|---|
|
||
| `palogo_eff0.prm` (entry 11, developer) | **paints FIRST** | 211 | **6 of 6** | ✅ forced first |
|
||
| `palogo_eff0.prm` (entry 10/13, publisher) | **paints FIRST** | 256 | 2 of 2 | ✅ forced first |
|
||
| `pteff00.prm` (title) | **paints LAST** | 2 | 3 of 23 | ✅ permitted on top |
|
||
| `pteff00.prm` (menu) | **paints LAST** | 2 | 7 of 15 | ✅ permitted on top |
|
||
|
||
🔴 **The first row is the one that matters.** `palogo_eff0.prm` is named like an
|
||
overlay, and a rule keyed on the *name* would sort it last — against a measured
|
||
order. Occlusion gets it right. `pteff00.prm` is opaque only for two instants, at
|
||
its screen's entry and exit, so the constraint never binds it: it is the fade
|
||
cover, and it belongs on top.
|
||
|
||
## The span, and what it costs to get wrong
|
||
|
||
The rule quantifies over "every instant the primitive is opaque" and "every
|
||
element visible then", so it depends on where a screen's timeline ends and on what
|
||
an element does after its own last keyframe. The port asked, having got **256**
|
||
opaque instants for `palogo_eff0.prm` where this page said 211.
|
||
|
||
⚠️ **That pair was a bundle mismatch, not a definitional one** — `palogo_eff0.prm`
|
||
appears on both splashes, and the publisher (entries 10, 13) runs to t=255 while
|
||
the developer (11, 14) runs to t=210. 256 and 211 are both right, for their own
|
||
screen. The definitions already agreed.
|
||
|
||
**The definition:** the span is `0 ..= max keyframe time over every element in the
|
||
build`, and an element **holds its final pose** past its own last keyframe — which
|
||
is what `pose_at` does, and it is decoded rather than assumed: a group holds at its
|
||
last keyframe rather than looping
|
||
([`ui-keyframe-time-unit.md`](../ui-keyframe-time-unit.md)), and
|
||
[`ui-record-loop-length.md`](ui-record-loop-length.md) shows the declared length
|
||
never falls short of the last keyframe, the slack being exactly that hold.
|
||
|
||
**How much rests on it — 130 keyless full-screen primitives with an opaque
|
||
interval, and how many verdicts change:**
|
||
|
||
| alternative convention | verdicts changed |
|
||
|---|---|
|
||
| span = the bundle header's declared `+0x08` | **0** |
|
||
| span = the primitive's **own** last keyframe | 72 |
|
||
| elements counted **gone** after their last keyframe (no hold) | **72** |
|
||
|
||
🔴 **The hold decides 55 % of the verdicts, and dropping it is refuted by a
|
||
measured order.** `palogo_eff0.prm` is a *single* keyframe at t=0. Without the
|
||
hold it would be opaque for one instant, no other element would be up yet, and the
|
||
rule would call it **free** — against the order measured in the running game,
|
||
which paints it first. Pinned by
|
||
`the_hold_after_a_final_keyframe_is_required_by_a_measured_order`.
|
||
|
||
✅ **And the header length is interchangeable with the elements' maximum**: zero
|
||
disagreements disc-wide. Either may be used.
|
||
|
||
✅ **The verdicts that matter are convention-independent.**
|
||
`pgloading_eff00.prm` comes out **first** under all four conventions;
|
||
`pteff00.prm` comes out **free** under all four. Only `palogo_eff0.prm` moves, and
|
||
only under the convention its own measured order rules out.
|
||
|
||
## Disc-wide
|
||
|
||
Keyless **full-screen** primitives with an opaque interval:
|
||
|
||
| | count |
|
||
|---|---|
|
||
| position **forced first** | **80** |
|
||
| constrained below some, not all | 50 |
|
||
| occluding nothing | 0 |
|
||
|
||
The split falls almost exactly along the names — every `*base*` is forced, every
|
||
`*eff00*` is not — with three families crossing it: `palogo_eff0.prm`,
|
||
`pgloading_eff00.prm` and `pzeff00.prm` are named like overlays and are forced
|
||
first. **That is precisely why the name is not the rule.**
|
||
|
||
✅ **And it explains a symptom the corpus had recorded without a cause.**
|
||
`ui-prm-primitives.md` notes 36 builds that "come out one colour ... wiped by
|
||
`pzeff00.prm` and `pceff00.prm`, whose positions have never been measured".
|
||
`pzeff00.prm` is forced first in **32 of 32** instances. Those builds were wiped by
|
||
our own sort, not by the game.
|
||
|
||
## 🔴 The rule's real limit, found by its own test
|
||
|
||
An earlier version applied to any element. Its disc-wide test asserted that no
|
||
*keyed* element is ever forced — and that assertion failed, on **22** of them:
|
||
`pneff01.t32` (key `0xd850`, paints #8 of 13) and `pbfriendly.t32` (key `0x9230`,
|
||
#17 of 49).
|
||
|
||
Both are `.t32` **sprites**, and that is the flaw: a sprite's *element* alpha
|
||
being 255 says nothing about whether its **texture** covers the screen. Most of it
|
||
may be transparent. The disagreements were the rule overreaching, not the keys
|
||
being wrong.
|
||
|
||
`forced_backdrop` is now restricted to elements with **no sprite** — untextured
|
||
primitives, which are solid quads and do occlude what they cover. That is also the
|
||
only case `derived_paint_order` consults it for.
|
||
|
||
## 🔴 Self-refutation: the argument is sound for only 42 of the 80
|
||
|
||
A disc-wide census of the *colour* these elements carry breaks the rule's premise
|
||
for nearly half its verdicts.
|
||
|
||
| the 80 forced-first instances | count | fade ARGB |
|
||
|---|---|---|
|
||
| `.prm` — untextured solid quads | **42** | pure black (`ff000000`, `7f000000`, `40000000`, …) |
|
||
| `.tbm` | **38** | **`ffffffff`** — white at full alpha |
|
||
|
||
**A solid white quad at alpha 255 painted first would make the screen white.** No
|
||
screen is white. So a `.tbm` is not a solid quad: `ffffffff` is a white
|
||
*modulation* on a texture, which is exactly what a background bitmap carries.
|
||
|
||
🔴 **And that means element alpha does not establish coverage for them** — the same
|
||
error the `.t32` guard already caught, one file extension further out. I fixed that
|
||
symptom (`el.sprite.is_some()`) rather than its cause: **an element's alpha is not
|
||
its texture's opacity, and only an untextured primitive makes the two the same
|
||
thing.**
|
||
|
||
**What this does and does not change:**
|
||
|
||
* ✅ The **42 `.prm`** verdicts stand as decoded. For a solid colour quad the fade
|
||
*is* the pixel, so opacity and coverage are the same fact.
|
||
* ✅ **One of the 13 `.tbm` names is measured, not inferred.** `pfbase.tbm` is
|
||
element 0 of the save/load frame, and the order read off the running game
|
||
(`the_save_load_screens_match_what_the_running_game_paints`) starts `[0, 1, 2,
|
||
…]` — the game paints it first. The other twelve names rest on the rule.
|
||
* 🟡 The **38 `.tbm`** verdicts are **not** decoded. They are almost certainly still
|
||
right — every one is named `*base*`, is full-screen, and one of them
|
||
(`pfbase.tbm`) has its first position **measured in the running game** — but that
|
||
is a name-and-role argument, which this page elsewhere argues is the weaker kind.
|
||
* ⚠️ **The code is deliberately unchanged.** Restricting `forced_backdrop` to
|
||
`.prm` would send `pcbase`, `pnbase`, `pqbase`, `pubase`, `pvbase`, `pjbgbase2`,
|
||
`po_menu_base` and the four `px_*_base` back to `u32::MAX` — last — which is the
|
||
blank-screen bug this rule was written to fix. Downgrading their *status* is
|
||
honest; reverting their *position* would be wrong.
|
||
|
||
## ❔ Where a `.tbm`'s pixels live — looked, not found
|
||
|
||
If a `.tbm`'s texture could be decoded, its alpha coverage would settle the 38
|
||
directly. It cannot be located:
|
||
|
||
* **not in its bundle** — no RATC record and no sprite-table entry, for any of the
|
||
13 names;
|
||
* **not a file** — there is no `.tbm` anywhere on the disc;
|
||
* **not a pak entry** — its own archive's hashed TOC contains none of `pfbase.tbm`,
|
||
its uppercase form, its stem, `.t32`/`.tga`/`.xpr` variants, or `ui\`/`tex\`
|
||
prefixes, across four archives;
|
||
* **not visible in our composite** — `compose` skips an element with no resolvable
|
||
sprite, so our renderer draws **no pixels at all** for a `.tbm`, and no committed
|
||
capture covers a screen that has one (`GP_SAVE_LOAD`, `GP_BUNK`,
|
||
`GP_DEBRIEFING_PILOTLOG`), so nothing says whether the game draws any either.
|
||
|
||
🔴 **REFUTED 2026-08-30 — a `.tbm` DRAWS, and the capture is now in hand.**
|
||
|
||
This paragraph used to say a second reading survived: that a `.tbm` contributes no
|
||
pixels, so its paint position is *inert* rather than wrong, leaving the verdicts
|
||
harmless instead of correct. **That reading is dead.**
|
||
|
||
The `TUTORIAL` screen was reached in the running game and captured
|
||
([`live-tutorial-screen.png`](../captures/title-builds/live-tutorial-screen.png)).
|
||
It carries a **full-screen blue circuit/hex background**. `GP_TUTORIAL` build 0 has
|
||
18 elements, and **element 0 is `pubase.tbm` with pivot (640, 360)** — 1280×720,
|
||
the only full-screen *textured* element in the bundle. The one other full-screen
|
||
element is `pueff00.prm`, an untextured primitive, and this page's own colour census
|
||
says every full-screen `*eff00*` primitive is **pure black**.
|
||
|
||
Our render of the same build
|
||
([committed beside it](../captures/title-builds/render-tutorial-build0-for-comparison.png))
|
||
is the **identical layout** — same title, same seven items, same footer — on **pure
|
||
black**: 6.0–6.4 % of the frame inked against the game's 99.7 %. The only difference
|
||
is the background, and the only thing that can be is the `.tbm`.
|
||
|
||
✅ **So the `.tbm` verdicts are CORRECT, not merely harmless.** They are load-bearing
|
||
in the full sense, and if the alpha-over assumption under this rule ever fails, those
|
||
24 deciders go with it for real.
|
||
|
||
⚠️ **Reach: one `.tbm`, `pubase.tbm`.** The other families —
|
||
`px_deb_base`, `px_bunk_base`, `pvbase`, `pqbase`, `pnbase`, `pjbgbase2`, `pcbase`,
|
||
`px_replay_base`, `px_movie_base`, `px_mission_base` — are not individually
|
||
observed. What is settled is the *class* question this paragraph posed: a `.tbm` is
|
||
not an element that draws nothing.
|
||
|
||
🔴 **And it means `screen render` is WRONG on every screen carrying a `.tbm`** — it
|
||
omits the background silently, with no diagnostic. That is a limitation of the
|
||
reference renderer, not of the game.
|
||
|
||
## 🔴 Stability is not necessity — 62 of the 80 are DECIDED by this rule (2026-08-30)
|
||
|
||
**Raised by the port agent, and it is right.** Every check this page has run — and
|
||
every re-check after a change elsewhere — measured whether a verdict *moved*. That
|
||
is the rule's **stability**. It never measured its **necessity**: an element whose
|
||
position is already fixed by a read `T8aD` key or an `implied_layer_key` is
|
||
*confirmed* by the rule, not *decided* by it, and on those screens removing the
|
||
rule entirely costs nothing.
|
||
|
||
So the question was asked directly. `derived_paint_order` was recomputed with the
|
||
`forced_backdrop` fallback removed and the two orders compared, over every
|
||
`dat/*.pak`:
|
||
|
||
| | instances |
|
||
|---|---|
|
||
| the rule **DECIDES** the order — it moves without it | **62** |
|
||
| the rule merely **AGREES** — order unchanged | 18 |
|
||
| **total forced** | **80** |
|
||
|
||
The 80 reproduces this page's own census exactly, which is the check that the probe
|
||
is looking at the same set. Data:
|
||
[`../data/forced-backdrop-necessity.txt`](../data/forced-backdrop-necessity.txt) ·
|
||
instrument: `crates/sylpheed-formats/examples/forced_backdrop_necessity.rs`.
|
||
|
||
**Every one of the 62 deciders is keyless — no keyed element is ever moved.** That
|
||
is the rule behaving as designed: it is a fallback, and it only ever fires where
|
||
nothing else can speak.
|
||
|
||
### 🔴 Not one of the 80 has a key read from the file
|
||
|
||
The necessity probe asked whether an element had *a* key, collapsing
|
||
`sprite_layer_key` — a `u16` read out of the `T8aD` header, **decoded** — with
|
||
`implied_layer_key` — this crate's per-name table, whose doc comment says outright
|
||
*"only names whose position has actually been measured"*. The port agent pointed out
|
||
that for describing what a *confirmation is made of* those are different claims, and
|
||
splitting them gives a sharper answer than either of us had
|
||
([`../data/forced-backdrop-key-source.txt`](../data/forced-backdrop-key-source.txt)):
|
||
|
||
| key source | instances |
|
||
|---|---|
|
||
| read from the `T8aD` header (decoded) | **0** |
|
||
| `implied_layer_key` (measured in the running game) | 14 |
|
||
| nothing at all | 66 |
|
||
|
||
**Zero.** There is no instance anywhere on the disc where a forced element also
|
||
carries a file-read layer key — which means **this rule has never been checked
|
||
against a decoded field, because there is no case in which both can speak.** That is
|
||
not a defect of the rule; it is what a fallback for keyless elements necessarily
|
||
looks like. But it removes a check a reader would reasonably assume exists, and the
|
||
page should not have let "own key" stand for it.
|
||
|
||
The 14 are two names: `pfbase.tbm` ×10 (`GP_SAVE_LOAD`) and `palogo_eff0.prm` ×4
|
||
(`GP_TITLE`), both implied `0x00000000`.
|
||
|
||
The 18 that merely agree split two ways, and the distinction matters:
|
||
|
||
* **14 have a key of their own, and it is an IMPLIED one** — `pfbase.tbm` (10) and
|
||
`palogo_eff0.prm` (4). ⚠️ Two questions come apart here and an earlier version of
|
||
this page ran them together:
|
||
* *Does the rule change the composite?* **No** — the sort already had the key, so
|
||
these contribute nothing to the necessity count. That much was right.
|
||
* *Does the rule get the right answer?* **Yes, and this is the only place that can
|
||
be asked.** `implied_layer_key` is a position measured in the running game, so
|
||
the rule forcing these 14 first is the rule agreeing with the **oracle**.
|
||
🔴 So "none of the 18 is evidence for the rule in any direction" — said to the port
|
||
agent on 2026-08-30 and corrected here — **was wrong about the 14**. They are not
|
||
a file-side check, and they are not independent of the sort, but they are the
|
||
rule's *only* external corroboration and there are 14 of them.
|
||
* **4 are keyless but inert** — `po_menu_base.tbm`, twice in each of two 2-element
|
||
builds. *Every* element on those screens is forced, so all keys collapse to the
|
||
same value and the declaration-index tie-break gives the identical order either
|
||
way.
|
||
|
||
### What actually holds the 62 up
|
||
|
||
Not a key — there is none — and not the `palogo_eff0.prm` control, which as above
|
||
is only decisive under a convention its own key already satisfies. **It is the
|
||
impossibility argument alone**: a full-screen quad that is opaque at some instant
|
||
cannot paint above everything visible then, or the screen is blank at that instant.
|
||
That argument is doing all the work on 62 instances, and its assumptions are
|
||
exactly the ones listed under *Reach* below — which is now a larger exposure than
|
||
this page previously implied.
|
||
|
||
By element, the 62:
|
||
|
||
| | count | kind |
|
||
|---|---|---|
|
||
| `pzeff00.prm` | 32 | `.prm`, colour census says pure black |
|
||
| `pgloading_eff00.prm` | 4 | `.prm` — 2 in `GP_TITLE` (entries 12/15), 2 in `GP_SAVE_LOAD` |
|
||
| `esrb_base.prm` | 2 | `.prm` |
|
||
| ten `*base*.tbm` families | 24 | `.tbm`, ❔ pixels never located |
|
||
|
||
⚠️ **24 of the 62 are `.tbm`**, and this page already records that a `.tbm`'s
|
||
pixels cannot be found anywhere on the disc. For those the two readings — "the rule
|
||
places it correctly" and "the element draws nothing, so its position is inert" —
|
||
remain indistinguishable, and both leave the composite right. The 38 `.prm`
|
||
deciders have no such escape: those are real quads with real colour, and the rule is
|
||
load-bearing on them in the full sense.
|
||
|
||
### For the port specifically
|
||
|
||
On the five menu screens the exposure is **two**: `GP_TITLE` entries **12 and 15**,
|
||
the dressed loading bundles, where `pgloading_eff00.prm` has neither a read nor an
|
||
implied key. Entries 10, 11, 13 and 14 — the four splashes — are unchanged with the
|
||
rule removed.
|
||
|
||
### The cost in pixels: 38 of the 62 turn the screen black without it
|
||
|
||
"The order moves" is a property of the sort. The tie-break work already found
|
||
overlapping reorders costing **zero** pixels, so it does not follow that the picture
|
||
moves. Each of the 62 deciding builds was therefore rendered twice — once in
|
||
`derived_paint_order`, once with the fallback removed — and diffed
|
||
([`../data/forced-backdrop-pixel-cost.txt`](../data/forced-backdrop-pixel-cost.txt),
|
||
instrument `examples/forced_backdrop_pixel_cost.rs`).
|
||
|
||
The split is perfect, and it falls exactly along the element kind:
|
||
|
||
| | builds | changed pixels |
|
||
|---|---|---|
|
||
| `.prm` deciders | **38** | **0.69 % … 94.41 %** of the frame |
|
||
| `.tbm` deciders | 24 | **0** — but see below |
|
||
|
||
**On all 38 `.prm` builds, `changed_px` equals the composite's total ink exactly.**
|
||
Not approximately — identically, 38 times out of 38. Without the rule the primitive
|
||
sorts last, paints over everything, and the screen composites to **pure black**.
|
||
That is the port's original contradiction argument, and it is now measured on 38
|
||
builds across seven archives rather than argued on two.
|
||
|
||
For the port's two: `GP_TITLE` entries 12 and 15 each move **49 771 px = 5.40 %** of
|
||
the frame, which is their entire ink.
|
||
|
||
### ✅ Second witness (2026-08-30): the strong form holds in Godot too
|
||
|
||
The port agent re-checked the 38-`.prm` result in **Godot**, which shares no code
|
||
with `compose`, applying this crate's fallback to its own element list and swapping
|
||
**only** `paint_order` on one screen file — renderer, textures and pose all held
|
||
fixed, so the order is the single variable. `GP_TITLE` entry 12 came out at
|
||
**59 530 px** ink with the rule and **exactly 0 without it**, at both of its
|
||
thresholds.
|
||
|
||
That is the claim that needed a second renderer, because it is the strong form: not
|
||
"a large difference" but *the screen ceasing to exist*. Two renderers, and this
|
||
time genuinely two — unlike the necessity census, where the port's re-run was this
|
||
crate's code executed twice.
|
||
|
||
#### ⚠️ …and the two ink figures were never counting the same pixels
|
||
|
||
The port reported 59 530 (>0) and 48 368 (>1); this page said 49 771. Counting the
|
||
same composite every way
|
||
([`../data/forced-backdrop-ink-thresholds.txt`](../data/forced-backdrop-ink-thresholds.txt)):
|
||
|
||
| threshold | this crate | Godot | gap |
|
||
|---|---|---|---|
|
||
| RGB > 0 | 49 771 | **59 530** | 16 % |
|
||
| RGB > 1 | **48 043** | **48 368** | **0.68 %** |
|
||
|
||
**The whole disagreement lives in pixels whose value is exactly 1.** Above that the
|
||
two renderers agree to 325 px on a 921 600-px frame. So it is a 1-LSB sampling
|
||
artefact — a different sampler putting a faint non-zero where this one puts exact
|
||
zero — and not a different set of inked pixels.
|
||
|
||
Two things to carry from that:
|
||
|
||
* **`> 0` is not a portable ink convention between renderers on a mostly-dark
|
||
frame; `> 1` is.** Any future cross-renderer ink comparison should say which.
|
||
* **The 49 771 in this page was never a threshold figure at all.** It is exact RGBA
|
||
inequality between the two paint orders, which over a black backdrop coincides
|
||
with ink > 0 — so it belongs against the port's 59 530, not its 48 368. Reading it
|
||
as the `>1` number would have made the two renderers look like they agreed for the
|
||
wrong reason.
|
||
|
||
✅ The *without-the-rule* column is **0 at every threshold** here as well, matching
|
||
Godot. The strong form is not threshold-sensitive in either renderer.
|
||
|
||
### 🔴 The zero on the 24 `.tbm` builds is my instrument, not a finding
|
||
|
||
**The control I wrote was the wrong control and it passed anyway.** It asked
|
||
whether the *composite* had ink — it always does, the rest of the screen draws —
|
||
when the question is whether *the element being reordered* has ink. This page
|
||
already records that `compose` draws **no pixels at all** for a `.tbm`, because it
|
||
has no resolvable sprite. So a `.tbm`'s paint position cannot change a pixel in our
|
||
renderer **by construction**, and those 24 zeros measure that and nothing else.
|
||
|
||
`tie_break_pixel_cost.rs` got this right and has the per-element `ink_mask` this
|
||
one needed. Reported rather than quietly patched, because the shape — a control
|
||
that cannot fail — is the one this corpus keeps paying for.
|
||
|
||
⚠️ So the `.tbm` half of the necessity result stands where it stood: **"correct or
|
||
inert", indistinguishable**, and no closer to being distinguished than before.
|
||
The 38 `.prm` are the part this measurement moves.
|
||
|
||
### 🔴 "Two renderers, same answer" was true of six instances, not eighty
|
||
|
||
That sentence stood here and it overstated the evidence. **The port agent caught it
|
||
and it is worth stating precisely, because the failure it guards against is the one
|
||
that started this whole thread** — `verify-screen` scoring two blank frames `OK`.
|
||
|
||
| | witnesses |
|
||
|---|---|
|
||
| the six `GP_TITLE` instances | **two, genuinely independent** — the port removed *its own* post-pass in its exporter and diffed its export; different code, different language, different layer. My crate-side run agrees |
|
||
| the other **74** | **one measurement, executed twice.** The port re-ran *this crate's* probe. A fault in the instrument reproduces identically for both of us |
|
||
|
||
So the disc-wide 62 is **not** independently confirmed and this page will not claim
|
||
it is. What the port's re-run does establish is that the probe is deterministic and
|
||
that I transcribed its output correctly — worth having, and much less than
|
||
agreement.
|
||
|
||
⚠️ **And the instrument had a real trap.** `forced_backdrop_necessity.rs` defaulted
|
||
to `GP_TITLE` when given no argument, so a bare run printed **6 instances in the
|
||
same format as 80**. The port hit it and nearly filed the discrepancy back at me. It
|
||
now walks every `dat/*.pak` by default and reports the archive count on stderr.
|
||
"I ran your instrument" has to mean the same thing to both of us.
|
||
|
||
## Reach
|
||
|
||
⚠️ **Assumes straight alpha-over blending.** Blend mode is ❔ on
|
||
[`ui-prm-primitives.md`](ui-prm-primitives.md): an *additive* quad at alpha 255
|
||
would not occlude, and the rule would then be placing it wrongly. The
|
||
`palogo_eff0.prm` control is evidence the assumption holds at least there.
|
||
|
||
✅ **The colour census narrows this a long way.** Every full-screen `*eff00*`
|
||
primitive on the disc is **pure black** at its various alphas — `ff000000`,
|
||
`7f000000`, `40000000`, `b2000000`, `cc000000`, `d4000000`, `00000000`. Black at
|
||
alpha *a* over content is exactly what an alpha-over dim or fade looks like, and an
|
||
*additive* black quad would be a no-op, so a designer would not author one. The
|
||
single non-black primitive on the disc is **`pbafc.prm`**, RGB `00e8e0` (cyan) at
|
||
alphas up to `ff` — and it is **844×600, not full-screen**, so it is outside this
|
||
rule's geometry guard entirely. ❔ Whether *it* is additive is still open, and it is
|
||
now the only candidate.
|
||
|
||
✅ **Coverage is tested per instant, against the SCALED size, and two-sidedly.**
|
||
An earlier version rejected on the *declared* size (pivot doubled), which the port
|
||
pointed out replaces one error with its mirror: an element scaled **above** 100 %
|
||
could cover the screen from a smaller declared size. Checked across **921** keyless
|
||
elements: **0** do, so the mirror case does not occur on this disc — the per-instant
|
||
test is in because it does not need that to stay true. Verdicts before and after:
|
||
80, split 42 `.prm` / 38 `.tbm`, unchanged.
|
||
|
||
⚠️ **It gives a lower bound, not an ordering.** It settles the 80 instances where
|
||
occlusion forces the position, and says nothing about the 50 where the primitive
|
||
is opaque only part of the time — including `pteff00.prm`, whose place on top is
|
||
still a *measured* per-name entry, not a decoded one.
|
||
|
||
⚠️ **No new oracle measurement.** The two controls are orders measured previously;
|
||
nothing here was captured from a running game. A draw capture of a loading screen
|
||
would confirm it directly, and the loading screens are not reachable from the
|
||
title path.
|
||
|
||
## Reproducing
|
||
|
||
```bash
|
||
cargo run -p sylpheed-formats --example prm_forced_first
|
||
cargo run -p sylpheed-formats --example prm_occlusion_check
|
||
SYLPHEED_DISC=/disc cargo test -p sylpheed-formats --test ui_forced_backdrop_disc
|
||
```
|