THREE THINGS FROM THE DECODER, one of which I am not taking. REFUTED: "the developer splash is one composited quad, the bounding box of the three logos". The observed quad is 525x259 at (378,155). The three logos' bounding box is 500x421 at (390,164) -- a 259-tall quad CANNOT contain them, and palogo_anima alone starts at y=449, thirty-five pixels below that quad's bottom edge. The observed quad matches the union of gamearts_eff and seta_eff, 521x261 at (379,154), to about four pixels in every dimension -- and both of those are TRANSIENTS my own census flagged, dark by t=45, so a frame containing that quad is a build-in frame rather than the settled screen. I cannot see their draw stream, so I sent the arithmetic rather than a verdict, and the port keeps drawing three: I will not stop drawing an element on a claim whose stated identification excludes that element from its own bounding box. THE BLACK HOLD IS 9 UNITS, NOT 12. I authored 12 from Q7's luminance plateau of 0.17-0.23 s, supported by the menus' transition quad. The Decoder counted SUBMITTED QUADS instead -- luminance cannot separate the outgoing fade's tail from true black. Four frames with no sprite quad at all, at 2.284 units/frame derived from the disc as its own clock, gives 9.1 units = 0.152 s (6.9-11.4). That overlaps the luminance figure only at the top, and the true black is SHORTER still since both boundary frames carry picture. My 12 was supported by analogy -- a different screen's quad on a different path -- and a number that fits by analogy loses to one measured in place. verify-dwell's bound moved with it; both screens still agree. THE TITLE'S SWEEPS LOOP. The oracle shows the quad oscillating over its whole x range and resetting hard, one reset in the first title dwell and two in the second. The loop-length field could NOT have settled it, correcting a hope I had stated: both records declare exactly their last keyframe time, slack zero, and "loops at 600" and "runs once for 600 and stops" write the identical header. Verified on the two sweeps' LCM, since their periods differ: 600 and 720 realign at 3600 units, mean diff 0, against 0.438 at half that. Scoped to the title. The menus declare the same lengths but the oracle measurement is of the title, and my own weak evidence points the other way there -- best match with the sweeps off-screen, three times worse mid-screen, against a 73% on-screen duty cycle if they looped. Two weak signals in opposite directions is a reason to scope, not to pick. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
111 lines
4.8 KiB
Bash
Executable File
111 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Check the port's boot pacing against captures of the real game.
|
|
#
|
|
# tools/port/verify-dwell
|
|
#
|
|
# WHY THIS IS A TOOL AND NOT A ONE-OFF. Doing it by hand once already refuted a
|
|
# 🔴 I had filed myself: `docs/port/BLOCKED.md` said `rest.t` was the wrong settle
|
|
# landmark AND that "everything the sequencer paces off it is therefore late".
|
|
# The first half is true; the second was wrong, and I nearly re-paced screens
|
|
# that already matched the game to 0.05 s.
|
|
#
|
|
# ⚠️ A PORT'S TRANSITION INTERVAL IS NOT THE ORACLE'S VISIBLE SPAN. They differ
|
|
# by the black hold between screens, and confusing the two cost this corpus 0.6 s
|
|
# once and 0.48 s on the plate delay. So the comparison here is explicit: the
|
|
# port's interval is checked against the oracle's span PLUS the measured hold.
|
|
#
|
|
# 🔴 AND THE VERDICT DOES NOT COME FROM THE FILMSTRIP ANY MORE. It used to
|
|
# measure ink spans from `--film` frames. The boot's black hold is 0.17-0.23 s
|
|
# (HANDOFF Q7) -- shorter than the 0.25 s cadence meant to observe it -- so when
|
|
# the black frame fell between samples two screens merged into one span and this
|
|
# tool reported `developer logos` as 93 s against an oracle of 3.5 s. Filming at
|
|
# 0.1 s made it WORSE: 2.5x the screenshots slows the run enough that the capture
|
|
# catches up in bursts, and the publisher span came back as 7.80 s.
|
|
#
|
|
# The sequencer already knows exactly when it changed screens and prints it.
|
|
# Sampling a picture to rediscover a number the program can state is how this
|
|
# went wrong. The filmstrip is kept, and marked advisory.
|
|
#
|
|
# THE EXPECTED NUMBERS ARE THE ORACLE'S, NOT THE PORT'S: three cold boots from
|
|
# `docs/re/boot-order-and-splash-dwell.md`, quoted as a test fixture. Nothing in
|
|
# the port derives them and nothing may.
|
|
set -euo pipefail
|
|
cd "${PROJECT_DIR:-/work}"
|
|
export DISPLAY="${DISPLAY:-:97}"
|
|
OUT="${OUT:-$(mktemp -d)}"
|
|
mkdir -p "$OUT"
|
|
INTERVAL="${INTERVAL:-0.25}"
|
|
|
|
echo "running the boot (the intro is skipped -- the splashes are what this measures)"
|
|
timeout "${TIMEOUT:-300}" godot --path port --resolution 1280x720 -- \
|
|
--boot --skip-at=1 "--film-interval=$INTERVAL" "--film=$OUT/f" \
|
|
>"$OUT/boot.log" 2>&1 || true
|
|
|
|
INTERVAL="$INTERVAL" python3 - "$OUT" <<'PYEOF'
|
|
import glob, os, re, subprocess, sys
|
|
out = sys.argv[1]
|
|
INTERVAL = float(os.environ.get("INTERVAL", "0.25"))
|
|
# The boot's black gap, measured in the DRAW STREAM (4 presented frames at
|
|
# 2.284 units/frame = 9.1 units), not from luminance -- luminance cannot separate
|
|
# the outgoing fade's tail from true black. The +/-1 frame range is 6.9-11.4
|
|
# units = 0.114-0.190 s. HANDOFF Q7's luminance figure of 0.17-0.23 s overlaps
|
|
# only at the top, and the draw-stream number is the one to use.
|
|
HOLD_LO, HOLD_HI = 0.114, 0.190
|
|
|
|
marks = []
|
|
for line in open(os.path.join(out, "boot.log"), errors="replace"):
|
|
m = re.match(r"\s+-> (\S+) at ([0-9.]+) s", line)
|
|
if m:
|
|
marks.append((m.group(1), float(m.group(2))))
|
|
if not marks:
|
|
print("no transitions in the boot log -- see", os.path.join(out, "boot.log"))
|
|
raise SystemExit(2)
|
|
|
|
ORACLE = [
|
|
("publisher wordmark", [4.297, 4.604, 4.370]),
|
|
("developer logos", [3.508, 3.503, 3.366]),
|
|
]
|
|
starts = [0.0] + [t for _, t in marks]
|
|
print()
|
|
print("%-20s %-14s %-26s %s" % ("screen", "port interval", "oracle span (3 boots)", "verdict"))
|
|
bad = 0
|
|
for k, (name, runs) in enumerate(ORACLE):
|
|
if k + 1 >= len(starts):
|
|
print("%-20s %-14s %s" % (name, "-", "no such transition this run")); continue
|
|
d = starts[k + 1] - starts[k]
|
|
lo, hi = min(runs) + HOLD_LO, max(runs) + HOLD_HI
|
|
ok = lo - 0.15 <= d <= hi + 0.15
|
|
bad += 0 if ok else 1
|
|
print("%-20s %-14s %-26s %s"
|
|
% (name, "%.2f s" % d, "%.3f / %.3f / %.3f" % tuple(runs),
|
|
"agrees" if ok else "DIFFERS"))
|
|
print()
|
|
print(" An interval is the oracle's SPAN plus the black hold (%.2f-%.2f s)," % (HOLD_LO, HOLD_HI))
|
|
print(" compared against span+hold with 0.15 s of slack for wall-clock jitter.")
|
|
print(" transitions:", ", ".join("%s@%.2f" % m for m in marks[:4]))
|
|
|
|
frames = sorted(glob.glob(os.path.join(out, "f_*.png")))[:120]
|
|
if frames:
|
|
means = [float(subprocess.run(["convert", f, "-colorspace", "Gray", "-format",
|
|
"%[fx:mean*255]", "info:"], capture_output=True, text=True).stdout or 0)
|
|
for f in frames]
|
|
ink = [m > 0.0 for m in means]
|
|
spans, i = [], 0
|
|
while i < len(ink):
|
|
if ink[i]:
|
|
j = i
|
|
while j < len(ink) and ink[j]: j += 1
|
|
spans.append((i * INTERVAL, j * INTERVAL)); i = j
|
|
else:
|
|
i += 1
|
|
print()
|
|
print(" advisory -- filmstrip ink spans at %.2f s, which CANNOT resolve a" % INTERVAL)
|
|
print(" %.2f-%.2f s hold and merges screens whenever it misses one:" % (HOLD_LO, HOLD_HI))
|
|
for a, b in spans[:4]:
|
|
print(" %6.2f - %6.2f s (%.2f s)" % (a, b, b - a))
|
|
raise SystemExit(1 if bad else 0)
|
|
PYEOF
|
|
rc=$?
|
|
echo "artifacts in $OUT"
|
|
exit $rc
|