Import the scene graph, and find the worst gap between two branch points is zero

ROADMAP G1, scheduled early because it is a measurement input, and it paid for
that immediately. 51.3 established that a ring's lookahead is accumulated out of
pipe - wire and that a seek spends all of it, so what a branch point costs is
set by the time since the last one. 55.5 rehearsed a seek on the machine and
said out loud that it could not ask the question, because nothing here knew
where the branch points are.

They are everywhere, and 5.4% of them are free of charge to the player and not
to us. Over 612 distinct transitions into a seek, taking the earliest instant
each input window opens: worst 0.000 s, p10 0.950, median 3.473, best 82.497.
33 open on the first frame of a clip the disc SEEKED to -- press right as
flaming_ropes.enter_room appears and you are in fall_to_death, two seeks with no
play between them. 51.2's slack rule can therefore be answered NO by the
content rather than by the buffer, and no amount of ring is a defence.

It does not break the design. A branch on an empty ring costs the 2-record
prefill, 149.7 ms at 488 KB/s, not the climb. What it removes is margin: at that
rate in a 256 KB ring, 76% of this game's branch points arrive before the ring
has refilled, and a 512 KB ring makes that 90%, because doubling the ceiling
does not touch the surplus. The ring is not the lever; the surplus is.

CORRECTION to FINDINGS 16: there is only one transcription. The SNES chapter
set says in its own README that it is derived from DirkSimple, so the planned
diff of two independent sources catches conversion errors only. Run anyway:
durations agree 388/505 within a frame, branch structure 470/505, and of the 35
differences 16 are renames and 18 of the other 19 are that port dropping the
arcade's diagonals. Zero transcription discrepancies, and none were findable.

Two constraints on the input layer come free: the arcade needs eight directions,
and the shortest input window is 98 ms against 54.4's 72.13/90.16 ms frame slot,
so input cannot be polled on the frame tick.

The coupling to outside source is contained to one file (USER DECISION).
tools/import/scenegraph.py is the only code here that knows those projects
exist -- their paths, table names, timing formulas, constants -- and it writes
DLXSCENE1, this project's own schema, into gitignored tmp/ with the sources'
licences inside it. tools/analysis/25_scene_graph.py reads only that. Nothing is
vendored and nothing outside-derived is committed. The split was made after the
measurement and the whole output was re-run byte for byte to show it moved no
number.

Both import gates are negative-tested: deleting one sequence upstream fails the
516/906 count, and closing the table early fails the constructor-end check,
which replaced one that was vacuous.

No 68000 code ran or changed; decode.bin is still 1,296 B at the same MD5.
check.sh gains an import stage that skips when there is no checkout. ALL GREEN
before and after.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-24 22:11:04 -07:00
parent 2676f3b835
commit 00232bb22b
7 changed files with 1187 additions and 13 deletions
+91
View File
@@ -1,3 +1,94 @@
# Status & next-session handoff — end of session 24 (2026-08-24)
## Session 24: the scene graph is in, and the worst gap between two decision points is zero
**Green light first and last: `./tools/bench/check.sh` was ALL GREEN before any
of this and ALL GREEN after**, plus a new import stage.
**ROADMAP G1 is DONE. FINDINGS 56.** G1 was scheduled early because it is a
*measurement input*, and it paid for that immediately.
**1. THE ANSWER: the worst gap is zero.** Over 612 distinct transitions into a
seek, taking the earliest instant each input window opens and chaining play
across sequences the disc plays through without seeking:
| | worst | p10 | median | p75 | p90 | best |
|---|---:|---:|---:|---:|---:|---:|
| seconds of play between two seeks | **0.000** | 0.950 | 3.473 | 5.800 | 9.548 | 82.497 |
**33 of the 612 (5.4%) are zero** — an input window that opens at t=0 of a clip
the disc seeked to, so two seeks can fall back to back with no play at all
(`flaming_ropes.enter_room -> fall_to_death`: press right on the first frame and
you die). **51.2's slack rule can be answered NO by the content, not by the
buffer**, and no amount of ring is a defence. 56.3.
**2. Most branch points arrive before the ring has refilled, at every rate this
tree has considered.** 51.3's climb against the game's own gaps, gate container:
| ring KB | pipe KB/s | ceiling | climb s | branch points under the climb |
|---:|---:|---:|---:|---:|
| 256 | 451.4 | 3 | 20.83 | 601/612 (98%) |
| 256 | 488.0 | 7 | 6.11 | **468/612 (76%)** |
| 256 | 513.2 | 7 | 3.81 | 370/612 (60%) |
| 256 | 600.0 | 7 | 1.66 | 129/612 (21%) |
| 512 | 488.0 | 11 | 9.60 | **551/612 (90%)** |
**A bigger ring makes this worse and now content says so too**: same rate, 76%
at 256 KB and 90% at 512 KB, because doubling the ceiling does not touch
`pipe - wire`. **The ring is not the lever; the surplus is.** 56.4.
**3. It does not break — it removes margin.** A branch on an empty ring costs
the prefill, not the climb: **149.7 ms (1.80 frame slots) at 488 KB/s**, 162.0 ms
if it is a scene change carrying the 6,164-byte header. So the finding is not
"this fails", it is **"this design runs permanently at minimum lookahead, and
the arcade content is what puts it there"**. The mechanical seek is still
unmodelled (B1) and is charged on top. 56.5.
**4. CORRECTION to FINDINGS 16: there is only one transcription.** 16 cleared
two permissively licensed sources and planned to diff them "to catch
transcription errors". The SNES project's own `data/events/README.md` says its
chapters are "derived from DirkSimple game data" — a second *copy*, not a second
transcription. The diff runs anyway and catches conversion errors: durations
agree 388/505 within one frame, branch structure 470/505, and of the 35
differences **16 are renames and 18 of the other 19 are the SNES conversion
dropping the arcade's diagonals**. Zero transcription discrepancies were found
and none could have been. 56.2, 56.6.
**5. Two constraints on the input layer, free with the import.** The arcade uses
**eight directions plus action and start** (diagonals are 21 of 906 windows —
droppable by a port that must, not by one aiming at the arcade), and the
**shortest input window is 98 ms** against a frame slot of 72.13 or 90.16 ms
(54.4). **Input cannot be polled on the frame tick**; `clock.i`'s V-DISP
interrupt already runs 8.6x faster at 0.1% of the budget. 56.7.
**6. The coupling to outside source is contained to one file (USER DECISION).**
`tools/import/scenegraph.py` is the only file in this tree that knows those
projects exist — their paths, table names, timing formulas, constants — and it
writes **`DLXSCENE1`**, our own schema, into gitignored `tmp/`.
`tools/analysis/25_scene_graph.py` reads only that. Nothing is vendored, nothing
outside-derived is committed, and the generated table carries its own `sources`
attribution block. The split was made after the measurement and changed no
number in it.
**New in the tree:** `tools/import/` (new directory, one file),
`tools/analysis/25_scene_graph.py`, and a `check.sh` stage that imports, gates
on 516 sequences / 906 input windows, and runs the analysis — skipped when there
is no checkout, like the px68k and IPL ROM stages.
**No 68000 code ran and none changed.** `decode.bin` is still 1,296 B at the
same MD5.
**Next:** **P4** (drive the MB89352, settle `W`) still decides the project and
still needs hardware or a MAME that models the SPC. What 56 changes about it:
the transport now has a *content* requirement as well as a rate one — it has to
survive a branch with an empty ring at zero notice, 5.4% of the time. **P2's
remaining half** (reserve index 0 as black) is unchanged and still bundled with
the two other re-encode-class questions from 55: the delivered-rate rate point
and 54.4's short slot. All three are still one re-encode plus one
re-measurement, and still want deciding together.
---
# Status & next-session handoff — end of session 23 (2026-08-24)
## Session 23: the 68000 fills its own ring, and the player's request loop turns out to cost more than the medium does