diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index a4cc60a2..9007652d 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -2074,3 +2074,71 @@ following up my own claims: Detail and the ruled-out explanations: [`docs/re/structures/ui-title-buildin-measured.md`](../re/structures/ui-title-buildin-measured.md). +## 2026-08-29 β€” the boot splash black gap is ~9 units, not 12; and I was wrong to dismiss it + +πŸ”΄ **First, my part in the miss.** You put the `publisher_logo` residual at 0.03 s +against a bound built from two measured ranges plus jitter slack, and I agreed it +said more about the bound than the game. It did not. You filmed it and the gap was +0.2 s. **A plausible explanation for a small number is how a real defect stays +hidden**, and I supplied one. + +βœ… **Now measured properly, in the draw stream rather than luminance** β€” which +matters, because luminance cannot separate the outgoing screen's fade tail from +true black, and the draw stream can: + +| frames | submitted | +|---|---| +| 21 – 125 | `palogo_sqex`, fading to alpha **7** | +| **126 – 129** | πŸ”΄ **no sprite quad at all** | +| 130 – 153 | the developer splash, fading in from alpha **34** | + +**4 presented frames**, the only such run in the whole sequence. + +Converted with the **disc as its own clock** rather than a frame rate (this run +presented at 13.1 fps, against 28 elsewhere β€” not usable): `palogo_sqex` declares +alpha β‰₯ 1 for **239.8 units** and is drawn in **105** frames β†’ **2.284 +units/frame**, which the title capture independently corroborates at 2.231. + +| | units | seconds | +|---|---|---| +| **measured, 4 frames** | **9.1** | **0.152** | +| Β±1 frame | 6.9 – 11.4 | 0.114 – 0.190 | +| *your authored 12* | *12* | *0.200* | + +**Author ~9 units, not 12.** ⚠️ And the true black is *shorter* than 9, not +longer: the last publisher frame still carries alpha 7 and the first developer +frame alpha 34, so both boundary frames contain picture I am counting as black. + +βœ… **Second, and you will want this for the splash renderer: the developer splash +is ONE composited quad.** It declares three logos β€” `palogo_gamearts` (390,164), +`palogo_seta` (521,316), `palogo_anima` β€” and **none of their sizes is ever +submitted**. What the game draws is a single **525Γ—259 quad at (378,155)**, the +bounding box of the three. + +❌ **Nothing on the disc declares the gap**, so you are right to author it: +`palogo_eff0.prm` is a single static keyframe, and the top-level `+0x08` is a +family constant (300 for every title/splash entry, 60 for loading) whose slack +runs 12–226 units. ❔ I have **not** looked in the executable; that is the next +place and I am naming it rather than claiming reach I do not have. + +### Your sweep question: `+0x08` does not settle it, but the oracle does β€” for the title + +`ptloop01.rat` declares 600 with keyframes to exactly t=600; `ptloop02.rat` 720 to +720. **Slack zero**, which is exactly the case the field cannot discriminate: +"loops at 600" and "runs once and stops" write the identical header. + +The draw stream is unambiguous for the **title**: across two dwells the sweep quad +oscillates over its whole x range and resets hard to the same start value β€” +**1 reset inside dwell 1, 2 inside dwell 2**. It does not park. + +⚠️ **But you asked about the main menu, and that is not what I measured.** Both +screens declare the same 600/720. Either the menu behaves differently, or "best +match" is weak at detecting an absence β€” your own caveat. **Unresolved for the +menu.** + +πŸ“Œ And your bounding-box refutation is taken: I have had those box figures from you +and did not question them. A box over scattered pixels locates the outermost +differing pixels, not the difference. + +Detail: [`docs/re/structures/boot-splash-gap-measured.md`](../re/structures/boot-splash-gap-measured.md). + diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md index aa206218..41e8962c 100644 --- a/docs/re/INDEX.md +++ b/docs/re/INDEX.md @@ -172,3 +172,4 @@ files, which is how the same ground got covered twice. | [`structures/ui-record-loop-length.md`](structures/ui-record-loop-length.md) | Where a looping record's cycle restarts β€” and the `PRESS β’Ά` plate's real period | βœ… **decoded**: a nested record is itself a RATC bundle and its header **`+0x08` is the loop length**; its keyframes need not fill it, and the slack is a hold at the final pose. Disc-wide over **1 781** timed nested records: 92.3 % declare exactly their last keyframe time, **7.7 % declare more**, and **0 declare less** β€” the falsifier (a cycle cannot restart before its own last pose) never fires. πŸ”΄ **The plate's `ptbtn00f` is 105 units of ramp inside a 120-unit cycle, so it holds dark for 15 units** β€” the port was shipping **105**, and the answer is **120**. βœ… Falsification test against the running game, using a pacing factor measured *independently* on the focus ring (declared 120 β†’ **2.177 s**, factor **1.0885**): to reach the corpus's measured 2.12–2.34 s, 105 units needs a factor of **1.211–1.337** (πŸ”΄ excludes the ring's) while 120 needs **1.060–1.170** (βœ… contains it). Different elements, different bundles, separate runs β€” tied only by both declaring 120. ⚠️ Says where a cycle *ends*, not which records cycle. ❔ the **top-level** `+0x08` (300 on every `GP_TITLE` entry, elements ending at 244–269) is a different question, untouched | | [`structures/ui-focus-record-pulse-census.md`](structures/ui-focus-record-pulse-census.md) | Every focus record whose glow pulses, and where `rest()` puts it | βœ… **decoded**, disc-wide: **1 130** focus records, **2 664** timed elements, **210 with a varying alpha** β€” of which **202** have `rest()` == the **peak** (burns bright forever) and **8** land **mid-ramp**. By pak: `PILOTLOG` 116, `MOVIE_THEATER` 54, `HANGAR_ARSENAL` 30, `LEADERBOARD` 8, **`GP_TITLE` 2**. 🟑 Bounds rather than refutes the port's "34 in the export, 2 varying, nothing to fix" β€” correct, and correct *because* `GP_TITLE` has 2; the pathology sits in the screens a wider port needs next. πŸ”΄ The 8 mid-ramp ones are the worse mode: `py_ranking_btn01f` swings 255β†’127β†’255 and `rest()` returns **244**, neither extreme, which looks entirely plausible and nothing reports it. βœ… Control: `ptbtn01f` is genuinely constant (255 throughout) and is **not** flagged; two hits verified keyframe by keyframe. ⚠️ A pulsing element has no resting pose β€” the question is malformed, not mis-answered; `pose_at(t)` inside the record's declared cycle ([`ui-record-loop-length.md`](structures/ui-record-loop-length.md)) is the only well-formed query. ⚠️ 210 is a **floor**: focus records are matched by the `Xf.rat` name rule, and varying scale/rotation/position is not counted | | [`structures/ui-title-buildin-measured.md`](structures/ui-title-buildin-measured.md) | The title's build-in and the plate glow, read out of the guest's own draw stream | βœ… **measured** (Canary, `ARM=early` draw capture): the decoded *mechanism* is observed, not just its end state. **The five flashes fire in a six-frame window and are absent from all 155 other sampled frames**; `ptlogo_back2eff1` is drawn in exactly 2 frames at **t = 54.0** against a decoded peak of **t54–56**, and `ptlogo1` first appears at **t = 42.2** against a decoded **t42** β€” with units/frame taken from the **glow's period alone**, a different element. The two holders (`ptlogo_back2eff`, `ptlogo_back2`) are continuous from frame 134. βœ… The glow's per-vertex colour alpha IS its fade alpha: **observed range 0…80 against a decoded peak of 80**, exact and unfitted; **period 51.158 presented frames** over 20 cycle starts; fitting the decoded ramp gives RMS **13.16** against **38.18 reversed** (2.9Γ—), so the asymmetry is real and correctly directed. Structure: the settled title is 10–11 draws naming no sprite β€” which is why arming at the title sees nothing. ⚠️ Frame **107** is a 27-draw spike between the movie's last frame and the title's first; calling it "the composite" was an **over-read** β€” it binds **no texture** and only 4 of its 27 draws log geometry. The second title entry has no such frame. ⚠️ The two entries are the same animation at **different sampling phases** (only 4 of 46 aligned frames match), which is what makes the `eff3` result robust. πŸ”΄ **The game does not draw `ptlogo_back2eff3` at all** β€” reproduced over **two** title entries 5 800 frames apart, and three alternative explanations tested and failed: *phase* (its window is **6 units** against a **2.23-unit** step, so it cannot be missed β€” frames 133/134 sit at t=60.1/62.3 inside it and draw `eff2` and `eff4` instead), *an unlogged draw* (exactly 2 blind draws/frame, always the same full-screen-triangle shader, present when no wipe is active), and *a bad position guess* (dropping position entirely, **zero** quads anywhere have a width within Β±30 of 408; the spectrum jumps 262 β†’ 748). Draw counts across both entries: eff1 **4**, eff2 **3**, eff3 **0**, eff4 **6**. πŸ”΄ **The port draws `eff3` at t=60–62 and the console does not.** ❔ Why is not established β€” nothing in its element record differs from its neighbours. ⚠️ An earlier "sub-frame phase" explanation and the advice that drawing all five "shows more sweep than the console" are both **withdrawn**. ⚠️ What a frame-by-frame build-in comparison *will* show is disagreement about which flash lands in which frame β€” 2 units/submitted frame against this run's 2.231 units/presented frame β€” and neither side is wrong. πŸ”΄ **Trap:** matching a bound texture's dimensions to a sprite fails both ways β€” it missed every flash *and* read the intro movie's 640Γ—360 YUV planes as `ptbase2`. βœ… A regression of five events' observed frames against their declared times (residuals ≀0.9 frames) recovers the intercept at frame **106.1** when the composite spike, not in the fit, is frame **107**. ⚠️ Per-vertex alpha = fade alpha holds for the **glow** and does not generalise β€” `eff4` reads 255/127/254 on consecutive frames. ❔ Frame rate not recorded, so nothing is in seconds; the glow's period implies a **114**-unit cycle against a declared 120, unexplained; `eff5` vs `ptlogo_back2eff` not separated | +| [`structures/boot-splash-gap-measured.md`](structures/boot-splash-gap-measured.md) | The black gap between the two boot splashes | βœ… **measured** in the guest's **draw stream**, which separates true black from a fade tail where luminance cannot: the publisher's last sprite is frame 125 (alpha 7), then **frames 126–129 submit NO sprite quad at all**, then the developer fades in at alpha 34. **The gap is 4 presented frames.** Converted with the disc as its own clock β€” `palogo_sqex` declares alphaβ‰₯1 for **239.8 units** and is drawn in **105** frames β†’ **2.284 units/frame** (the title capture independently gave 2.231) β€” that is **~9.1 units β‰ˆ 0.152 s**, against the **12** the port authored; ⚠️ and the true black is *shorter*, since both boundary frames still carry picture. βœ… Second finding: the **developer splash is ONE composited 525Γ—259 quad** at the bounding box of its three declared logos, none of whose individual sizes is ever submitted. ❌ Not declared on the disc: `palogo_eff0.prm` is a single static keyframe, and the top-level `+0x08` is a **family constant** (300 / 60) whose slack ranges 12–226 units. ❔ The executable is **not** looked at β€” named, not claimed. πŸ”΄ The instrument was perturbing the measurement: the capture script taps β’Ά on "screen changed a lot", which is also true of a fading splash β€” it tapped through the publisher and the developer never appeared. `GRACE=1` and `NOTAP=1` knobs added | diff --git a/docs/re/data/boot-splash-gap-draws.csv b/docs/re/data/boot-splash-gap-draws.csv new file mode 100644 index 00000000..7456d98b --- /dev/null +++ b/docs/re/data/boot-splash-gap-draws.csv @@ -0,0 +1,234 @@ +# Every sprite quad submitted during the boot splashes, from the guest's +# own draw stream. Capture: ui_draw_capture.sh GRACE=1 NOTAP=1 ARM=early, +# 2026-08-29. Full-screen quads (w>=1250) are omitted. +# frames 126..129 carry NO sprite quad at all -- that is the black gap. +frame,x,y,w,h,vertex_alpha +2,301,317,685,90,119 +3,301,317,685,90,153 +4,301,317,685,90,187 +6,301,317,685,90,246 +7,301,317,685,90,240 +9,301,317,685,90,229 +10,301,317,685,90,223 +11,301,317,685,90,220 +12,301,317,685,90,214 +13,301,317,685,90,211 +14,301,317,685,90,183 +15,301,317,685,90,155 +16,301,317,685,90,127 +17,301,317,685,90,98 +18,301,317,685,90,70 +19,301,317,685,90,56 +20,301,317,685,90,28 +21,307,331,666,65,255 +22,307,331,666,65,255 +23,307,331,666,65,255 +24,307,331,666,65,255 +25,307,331,666,65,255 +26,307,331,666,65,255 +27,307,331,666,65,255 +28,307,331,666,65,255 +29,307,331,666,65,255 +30,307,331,666,65,255 +31,307,331,666,65,255 +32,307,331,666,65,255 +34,307,331,666,65,255 +35,307,331,666,65,255 +37,307,331,666,65,255 +38,307,331,666,65,255 +39,307,331,666,65,255 +40,307,331,666,65,255 +41,307,331,666,65,255 +42,307,331,666,65,255 +43,307,331,666,65,255 +44,307,331,666,65,255 +45,307,331,666,65,255 +46,307,331,666,65,255 +47,307,331,666,65,255 +48,307,331,666,65,255 +49,307,331,666,65,255 +50,307,331,666,65,255 +51,307,331,666,65,255 +52,307,331,666,65,255 +53,307,331,666,65,255 +54,307,331,666,65,255 +55,307,331,666,65,255 +56,307,331,666,65,255 +57,307,331,666,65,255 +58,307,331,666,65,255 +59,307,331,666,65,255 +60,307,331,666,65,255 +61,307,331,666,65,255 +62,307,331,666,65,255 +63,307,331,666,65,255 +64,307,331,666,65,255 +65,307,331,666,65,255 +66,307,331,666,65,255 +67,307,331,666,65,255 +68,307,331,666,65,255 +69,307,331,666,65,255 +70,307,331,666,65,255 +71,307,331,666,65,255 +72,307,331,666,65,255 +73,307,331,666,65,255 +74,307,331,666,65,255 +75,307,331,666,65,255 +76,307,331,666,65,255 +77,307,331,666,65,255 +78,307,331,666,65,255 +79,307,331,666,65,255 +80,307,331,666,65,255 +81,307,331,666,65,255 +82,307,331,666,65,255 +83,307,331,666,65,255 +84,307,331,666,65,255 +85,307,331,666,65,255 +86,307,331,666,65,255 +87,307,331,666,65,255 +88,307,331,666,65,255 +89,307,331,666,65,255 +90,307,331,666,65,255 +91,307,331,666,65,255 +92,307,331,666,65,255 +93,307,331,666,65,255 +94,307,331,666,65,255 +95,307,331,666,65,255 +96,307,331,666,65,255 +97,307,331,666,65,255 +98,307,331,666,65,255 +99,307,331,666,65,255 +100,307,331,666,65,255 +102,307,331,666,65,255 +103,307,331,666,65,255 +105,307,331,666,65,255 +106,307,331,666,65,255 +107,307,331,666,65,255 +108,307,331,666,65,255 +109,307,331,666,65,255 +110,307,331,666,65,255 +111,307,331,666,65,255 +112,307,331,666,65,255 +113,307,331,666,65,249 +114,307,331,666,65,243 +115,307,331,666,65,231 +116,307,331,666,65,198 +117,307,331,666,65,181 +118,307,331,666,65,165 +119,307,331,666,65,148 +120,307,331,666,65,115 +121,307,331,666,65,81 +122,307,331,666,65,65 +123,307,331,666,65,31 +124,307,331,666,65,15 +125,307,331,666,65,7 +130,378,155,525,259,34 +131,378,155,525,259,51 +132,378,155,525,259,85 +133,378,155,525,259,119 +134,378,155,525,259,136 +135,378,155,525,259,153 +136,378,155,525,259,170 +137,378,155,525,259,221 +138,378,155,525,259,255 +139,378,155,525,259,255 +140,378,155,525,259,255 +141,378,155,525,259,255 +142,378,155,525,259,255 +143,378,155,525,259,255 +144,378,155,525,259,255 +145,378,155,525,259,255 +146,378,155,525,259,254 +147,378,155,525,259,220 +148,378,155,525,259,186 +149,378,155,525,259,152 +151,378,155,525,259,84 +152,378,155,525,259,50 +153,378,155,525,259,33 +154,390,162,499,241,255 +155,390,162,499,241,255 +156,390,162,499,241,255 +157,390,162,499,241,255 +158,390,162,499,241,255 +159,390,162,499,241,255 +160,390,162,499,241,255 +161,390,162,499,241,255 +162,390,162,499,241,255 +163,390,162,499,241,255 +164,390,162,499,241,255 +165,390,162,499,241,255 +166,390,162,499,241,255 +167,390,162,499,241,255 +168,390,162,499,241,255 +169,390,162,499,241,255 +170,390,162,499,241,255 +171,390,162,499,241,255 +172,390,162,499,241,255 +173,390,162,499,241,255 +174,390,162,499,241,255 +176,390,162,499,241,255 +177,390,162,499,241,255 +179,390,162,499,241,255 +180,390,162,499,241,255 +181,390,162,499,241,255 +183,390,162,499,241,255 +184,390,162,499,241,255 +185,390,162,499,241,255 +186,390,162,499,241,255 +187,390,162,499,241,255 +188,390,162,499,241,255 +189,390,162,499,241,255 +190,390,162,499,241,255 +191,390,162,499,241,255 +192,390,162,499,241,255 +193,390,162,499,241,255 +194,390,162,499,241,255 +195,390,162,499,241,255 +196,390,162,499,241,255 +197,390,162,499,241,255 +198,390,162,499,241,255 +199,390,162,499,241,255 +200,390,162,499,241,255 +201,390,162,499,241,255 +202,390,162,499,241,255 +203,390,162,499,241,255 +204,390,162,499,241,255 +205,390,162,499,241,255 +206,390,162,499,241,255 +207,390,162,499,241,255 +208,390,162,499,241,255 +209,390,162,499,241,255 +210,390,162,499,241,255 +211,390,162,499,241,255 +212,390,162,499,241,255 +213,390,162,499,241,255 +214,390,162,499,241,255 +215,390,162,499,241,255 +216,390,162,499,241,255 +217,390,162,499,241,255 +218,390,162,499,241,255 +219,390,162,499,241,255 +221,390,162,499,241,255 +223,390,162,499,241,255 +224,390,162,499,241,255 +225,390,162,499,241,255 +226,390,162,499,241,255 +227,390,162,499,241,255 +228,390,162,499,241,255 +229,390,162,499,241,255 +230,390,162,499,241,255 +231,390,162,499,241,255 +232,390,162,499,241,255 +233,390,162,499,241,255 +234,390,162,499,241,255 +235,390,162,499,241,255 +236,390,162,499,241,254 +237,390,162,499,241,243 +238,390,162,499,241,231 +239,390,162,499,241,198 +240,390,162,499,241,165 +241,390,162,499,241,131 +242,390,162,499,241,115 +243,390,162,499,241,81 +244,390,162,499,241,48 +245,390,162,499,241,23 +246,390,162,499,241,7 diff --git a/docs/re/structures/boot-splash-gap-measured.md b/docs/re/structures/boot-splash-gap-measured.md new file mode 100644 index 00000000..93b977d3 --- /dev/null +++ b/docs/re/structures/boot-splash-gap-measured.md @@ -0,0 +1,110 @@ +# The black gap between the boot splashes β€” measured in draws, not luminance + +**Classification: measured.** Xenia Canary, `ui_draw_capture.sh GRACE=1 NOTAP=1 +ARM=early`, 2026-08-29. Evidence: +[`boot-splash-gap-draws.csv`](../data/boot-splash-gap-draws.csv). + +## Why this was open + +The port found that its boot had **no black frame at all** between the publisher +and developer splashes, and authored **12 units** (0.200 s) by analogy with the +menus' transition quad. On the boot path that analogy has nothing behind it: +`palogo_eff0.prm` is a **single static keyframe**, so the splash bundles declare +no fade quad at all. + +πŸ”΄ **And I had agreed with the dismissal that hid it.** Told that the residual was +0.03 s against a bound built from two measured ranges plus jitter slack, I said it +said more about the bound than the game. It did not β€” the underlying gap was 0.2 s +and the port had been missing it since P3. A plausible explanation for a small +number is exactly how a real defect stays hidden. + +## The measurement + +Luminance cannot separate the outgoing screen's fade tail from true black. The +**draw stream** can: it says exactly which frames submit a sprite quad at all. + +| frames | what is submitted | +|---|---| +| 2 – 20 | `palogo_sqex_eff` (685Γ—90) β€” the publisher's glow | +| 21 – 125 | `palogo_sqex` (666Γ—65) β€” the publisher wordmark, fading to alpha **7** | +| **126 – 129** | πŸ”΄ **nothing β€” 4 frames with no sprite quad at all** | +| 130 – 153 | the developer splash, fading in from alpha **34** | + +**The gap is 4 presented frames**, and it is the only such run anywhere between +the first and last sprite of the sequence. + +## Converting it without a frame rate + +The run's presented rate is not usable β€” measured at **13.1 fps** while the title +was up, against the corpus's 28 fps for other runs, and it is not stable enough to +convert a 4-frame interval. + +So the **disc's own timeline is the clock**. `palogo_sqex` declares alpha β‰₯ 1 from +t β‰ˆ 15.06 to t β‰ˆ 254.9 β€” **239.8 units** β€” and is drawn in **105 frames**: + +> **2.284 units per presented frame**, from the same screen in the same capture. +> (The title capture, independently, gave 2.231.) + +| | units | seconds at 60 units/s | +|---|---|---| +| if the gap were 3 frames | 6.9 | 0.114 | +| **measured β€” 4 frames** | **9.1** | **0.152** | +| if the gap were 5 frames | 11.4 | 0.190 | +| *the port's authored value* | *12* | *0.200* | + +**The measured gap is ~9 units, and 12 is at or just past the top of the +quantisation range.** ⚠️ And the true black is *shorter* than this, not longer: +the last publisher frame still carries alpha 7 and the first developer frame +alpha 34, so both boundary frames contain some picture that this count treats as +black. + +## βœ… A second thing the draw stream shows: the developer splash is ONE quad + +The developer bundle declares three logos β€” `palogo_gamearts` at (390,164), +`palogo_seta` at (521,316), `palogo_anima` β€” and **none of their sizes is ever +submitted**. What is submitted is a single **525Γ—259 quad at (378,155)**, which is +the bounding box of the three. The game composites them and blits the group. + +This is why an earlier pass reported "developer splash: 0 frames" β€” it was +matching individual logo sizes that the game never draws. + +## Is the gap declared anywhere? Not that I can find + +* ❌ **In the splash bundles.** `palogo_eff0.prm` is one static keyframe + (`t0 a255`). No fade quad, no gap. +* ❌ **In the top-level header `+0x08`.** It is a **family constant** β€” 300 for + every title/splash entry in `GP_TITLE`, 60 for the loading bundles β€” and the + slack against each bundle's last keyframe ranges from **12 to 226 units** + (the main menu's is 220, i.e. 3.7 s). It cannot be a declared gap. +* ❔ **In the executable β€” not looked at.** The corpus has the boot phase machine + at `this+132`; whether a dwell or gap constant sits near it is untested. That is + the next place, and I am naming it rather than claiming reach I do not have. + +So the port is right to author this, and should author **~9 units** rather than 12. + +## Reach + +⚠️ **One boot, one machine.** The 4-frame count is quantised and the Β±1 frame is +the dominant uncertainty (6.9 – 11.4 units). + +⚠️ The units-per-frame calibration assumes the guest's animation clock advances +uniformly across the gap, which is the same assumption the rest of this corpus's +frameβ†’unit conversions make. + +## πŸ”΄ The instrument was perturbing what it measured + +`ui_draw_capture.sh` taps β’Ά whenever the screen changes a lot, to skip the attract +movie. **That trigger is also true while a boot splash is fading.** A first run +classified the publisher splash as a movie at t = 3 s, tapped through it, and the +developer splash never appeared. Two knobs now exist and a boot run needs both: + +* `GRACE=1` β€” the fixed 8 s wait before arming meant an `ARM=early` capture always + missed both splashes, which run at ~1.2 – 9.5 s of guest time; +* `NOTAP=1` β€” no input at all. + +## Reproducing + +```bash +GRACE=1 NOTAP=1 FRAMES=9000 MAXDRAWS=5000000 ARM=early \ + tools/re-capture/ui_draw_capture.sh 300 /tmp/uicap-boot +``` diff --git a/docs/re/structures/ui-record-loop-length.md b/docs/re/structures/ui-record-loop-length.md index a2458a6b..36aa1889 100644 --- a/docs/re/structures/ui-record-loop-length.md +++ b/docs/re/structures/ui-record-loop-length.md @@ -121,6 +121,35 @@ visible in the draw stream as the frames where the game submits no draw at all. hold; it is what a four-sample wall-clock measurement of a ~2 s period in this emulator looks like. +## ❔ What this field does NOT settle: do the `ptloop` sweeps loop? + +Asked by the port, hoping `+0x08` would decide it. **It does not.** + +`ptloop01.rat` declares `+0x08` = **600** with keyframes reaching exactly t=600; +`ptloop02.rat` declares **720**, keyframes to t=720. **Slack zero** β€” and a +zero-slack record is precisely the case this field cannot discriminate: "loops at +600" and "runs once for 600 and stops" produce the identical header. 92.3 % of +records on the disc are in that state. + +βœ… **The oracle answers it for the title, and the answer is that they keep +moving.** Tracking the sweep quad across two title dwells in a draw capture: + +| | sweep draws | x range (NDC) | backward jumps to the start | +|---|---|---|---| +| title dwell 1 (frames 138–1220) | 1008 | βˆ’3.02 … βˆ’0.33 | **1** (frame 1161) | +| title dwell 2 (frames 5963–7025) | 908 | βˆ’3.02 … +0.42 | **2** (frames 6370, 6822) | + +The x position oscillates across the whole range for the entire dwell and resets +hard to the same start value (βˆ’2.38). A run-once-and-park would show one traverse +and then a constant x. **It does not park.** + +⚠️ **This is the TITLE, and the port asked about the MAIN MENU.** Both declare +`ptloop01`/`ptloop02` with the same 600/720, but I have not captured the menu, and +the port's own evidence β€” an idle menu capture matching best with the sweeps +off-screen β€” points the other way. Either the menu's sweeps behave differently, or +"best match" is doing badly at detecting an absence, which the port said itself. +**Unresolved for the menu; measured for the title.** + ## Reach ⚠️ **This says where a cycle ends, not that every record cycles.** 92.3 % of diff --git a/tools/re-capture/ui_draw_capture.sh b/tools/re-capture/ui_draw_capture.sh index d5d8bb5f..1547322e 100755 --- a/tools/re-capture/ui_draw_capture.sh +++ b/tools/re-capture/ui_draw_capture.sh @@ -33,7 +33,13 @@ if [ "${ATTACH:-0}" != "1" ]; then # Grace period before the liveness check: run-canary is a shell that execs the # binary, and polling `ps -C xenia_canary` in the first moments reports GONE # for a process that is merely not exec'd yet. - sleep 8 + # + # ⚠️ GRACE is why an ARM=early capture still MISSES BOTH BOOT SPLASHES. They + # run at roughly 1.2-9.5 s of guest time (`boot-order-and-splash-dwell.md`), + # and a fixed 8 s wait plus the window poll arms after the publisher has been + # and gone. Set GRACE=1 to catch them; the liveness check below tolerates it + # because it polls rather than sampling once. + sleep "${GRACE:-8}" fi until xdotool search --name "Xenia-canary" >/dev/null 2>&1; do @@ -44,6 +50,13 @@ win="$(xdotool search --name "Xenia-canary" | tail -1)" echo "WINDOW=$win" xdotool windowactivate "$win" 2>/dev/null; xdotool windowfocus "$win" 2>/dev/null +# πŸ”΄ NOTAP=1 stops this script tapping A at all. The tap exists to skip the +# attract movie, but its trigger is "the screen changed a lot", which is ALSO +# true while a boot splash is fading. A run measuring the BOOT sequence must set +# it: with the tap on, a 2026-08-29 run classified the publisher splash as a +# movie at t=3 s, tapped through it, and the developer splash never appeared at +# all. The instrument was perturbing the thing it was measuring. +# # ARM=early presses F10 before the title exists, so a long window contains the # frame in which the screen is BUILT. Armed at the title instead, a capture only # ever sees the steady state β€” and on this title screen the steady state is 11 @@ -112,7 +125,7 @@ while [ $SECONDS -lt $deadline ]; do [ "${WAIT_DONE:-0}" = "1" ] || break fi echo "F10 pressed; window still open" - elif [ "$d" -gt 1500 ]; then + elif [ "$d" -gt 1500 ] && [ "${NOTAP:-0}" != "1" ]; then echo "movie (rmse $d) -> skip A"; python3 "$SD/pad.py" tap A 0.25; sleep 3 fi sleep 1