From 088fcce72405cc2d2c0e7ae230033e30f0f061f1 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Tue, 18 Aug 2026 20:15:48 +0000 Subject: [PATCH] tools: capture targets, and a Z-aware draw-order decoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ui_draw_capture.sh` grows three knobs the second iteration needed: * ARM=early presses F10 before the title exists, so a long window contains the frames in which a screen is BUILT (it turns out none are — the title screen submits the same 11 draws every frame and never rebuilds); * TARGET=menu taps A once on the title and arms on the main menu, skipping attract movies on the way — it does not get there, but the blocker it hits is documented rather than worked around; * EXTRA_FLAGS passes emulator cvars through (--create_profile_if_none, --mem_watch=false, --log_level). `ui_draw_order.py` follows the capture's new vertex format (x, y, z) and reports the Z it now has. --- tools/re-capture/ui_draw_capture.sh | 29 ++++++++++++++++++++++++++++- tools/re-capture/ui_draw_order.py | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tools/re-capture/ui_draw_capture.sh b/tools/re-capture/ui_draw_capture.sh index ea058590..d5d8bb5f 100755 --- a/tools/re-capture/ui_draw_capture.sh +++ b/tools/re-capture/ui_draw_capture.sh @@ -29,7 +29,7 @@ wide(){ [ "$(identify -format '%w' "$1" 2>/dev/null || echo 0)" -gt 1000 ]; } xsetroot -solid black 2>/dev/null || true # ATTACH=1 watches an emulator that is already up instead of launching one. if [ "${ATTACH:-0}" != "1" ]; then - ( cd "$OUT" && nohup run-canary --log_ui_draws=true --ui_draw_capture_frames="${FRAMES:-3}" --ui_draw_capture_max="${MAXDRAWS:-20000}" >"$OUT/canary.stdout" 2>"$OUT/canary.stderr" & ) + ( cd "$OUT" && nohup run-canary --log_ui_draws=true --ui_draw_capture_frames="${FRAMES:-3}" --ui_draw_capture_max="${MAXDRAWS:-20000}" ${EXTRA_FLAGS:-} >"$OUT/canary.stdout" 2>"$OUT/canary.stderr" & ) # 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. @@ -66,6 +66,33 @@ while [ $SECONDS -lt $deadline ]; do d=$(compare -metric RMSE /tmp/u1.png /tmp/u2.png null: 2>&1 | sed 's/ .*//' | cut -d. -f1); d=${d:-0} s=$(python3 "$SD/screen_id.py" /tmp/u2.png | awk '{print $1}') echo "t=${SECONDS}s screen=$s rmse=$d" + # TARGET=menu walks one step further: tap A ONCE on the title, then arm on the + # main menu. One tap, and only from a full-width grab — the run that tapped + # repeatedly (off a 10-pixel sliver it had classified) crashed the guest in the + # save-data probe behind that button. + if [ "${TARGET:-title}" = "menu" ]; then + if [ "$s" = "title" ] && [ "${tapped:-0}" = "0" ]; then + echo "TITLE at ${SECONDS}s -> one A"; python3 "$SD/pad.py" tap A 0.25; tapped=1 + elif [ "$s" = "menu" ]; then + cp /tmp/u2.png "$OUT/menu-before-f10.png" + xdotool windowactivate "$win" 2>/dev/null + xdotool key --window "$win" F10 + xdotool key F10 + sleep 3 + screenshot "$OUT/menu-after-f10.png" >/dev/null 2>&1 + if ls "$OUT"/xenia_re_ui_draws_*.log >/dev/null 2>&1; then + echo "MENU CAPTURED at ${SECONDS}s"; break + fi + echo "F10 pressed on the menu; no log yet" + elif [ "$d" -gt 1500 ]; then + # Still in the attract loop. Without this the menu target waits forever: + # the attract movie returns to the title and away again, and a run that + # only ever watches never gets there. + echo "movie (rmse $d) -> skip A"; python3 "$SD/pad.py" tap A 0.25; sleep 3 + fi + sleep 1 + continue + fi if [ "$s" = "title" ] && [ "${ARM:-title}" = "early" ]; then cp /tmp/u2.png "$OUT/title-reached.png" echo "TITLE REACHED at ${SECONDS}s (capture was armed early)" diff --git a/tools/re-capture/ui_draw_order.py b/tools/re-capture/ui_draw_order.py index b5c5958b..2261f115 100755 --- a/tools/re-capture/ui_draw_order.py +++ b/tools/re-capture/ui_draw_order.py @@ -19,7 +19,7 @@ import glob from collections import defaultdict W, H = 1280, 720 -VERT = re.compile(r"\[([0-9A-F]{8}),([0-9A-F]{8})=(-?\d+\.\d+),(-?\d+\.\d+)\]") +VERT = re.compile(r"\[(-?\d+\.\d+),(-?\d+\.\d+),z=(-?\d+\.\d+)\]") def sprite_index(texdir): @@ -78,7 +78,7 @@ def main(): continue if "vb=0x" in line and pending: if want is None or frame == want or frame is None: - verts = [(float(a), float(b)) for _, _, a, b in VERT.findall(line)] + verts = [(float(a), float(b)) for a, b, _z in VERT.findall(line)] if verts: # Two conventions in one log: the UI sprite shader emits NDC, # the full-screen pass emits pixels already (-0.5 .. 1279.5).