#!/usr/bin/env bash # Diff Godot's drawing of an exported screen against `sylpheed-cli screen # render` of the same build -- the P1 gate. # # tools/verify-screen # every screen in the manifest # tools/verify-screen main_menu title # named screens # # Writes .godot.png, .ref.png and .diff.png into # $OUT (default: a directory under /tmp) and prints, per screen, the largest # per-channel difference anywhere in the frame. # # The two renderers are held to the same inputs on purpose: # # * the REFERENCE CLI is the one built by `build-reference-cli`, from the same # `sylpheed-formats` revision the exporter is pinned to. /reborn's own # target/ is a live mount of the other agent's checkout and moves mid-run; a # pixel disagreement against a moving decoder proves nothing. # * `--black` because Godot clears to black and the screen carries its own # background. The CLI's default dim slate stands in for a 3D scene behind an # in-mission screen, which is not this screen. # * `--primitives --animated` because those are what make the CLI draw the same # element set. `--focus` is NOT passed: nothing is focused at rest (HANDOFF # Q5 measured initial focus as unstable boot to boot, so choosing one is # P5's decision). # * `--pose=rest` on the Godot side. Since P2 the port's DEFAULT is to play the # timeline, and the settled timeline is deliberately NOT what `rest` says -- # the export's `rest` misses `ptframe1`/`ptframe2` on the main menu, and the # running game shows them (docs/DECISIONS.md). Both renderers read `rest` # through the same decoder, so asking for it here keeps this a test of the # PORT against the reference. It is not the test of whether `rest` is right; # that one is the oracle capture, and the port already departs from it. # # A difference here is not automatically the port's fault. Say which renderer is # wrong and why -- do not tune until they match. set -euo pipefail cd "${PROJECT_DIR:-/work}" # `reference-cli/`, not `release/`: the reference binary is built per pinned # revision so a pin change cannot silently reuse the previous revision's build. # See docker/bin/build-reference-cli. CLI="${SYLPHEED_CLI:-${CARGO_TARGET_DIR:-/sylph-home/port/target-container}/reference-cli/sylpheed-cli}" DISC="${SYLPHEED_DISC:-/disc}" OUT="${OUT:-${TMPDIR:-/tmp}/verify-screen}" export DISPLAY="${DISPLAY:-:97}" [ -x "$CLI" ] || { echo "no reference CLI at $CLI -- run build-reference-cli" >&2; exit 2; } [ -f export/manifest.json ] || { echo "no export/manifest.json -- run build-export --run" >&2; exit 2; } mkdir -p "$OUT" # Godot needs one scan to register the `class_name` globals; without it every # script fails to parse and the run dies with no frame drawn. [ -d port/.godot ] || godot --headless --path port --import >/dev/null 2>&1 screens=("$@") if [ ${#screens[@]} -eq 0 ]; then mapfile -t screens < <(python3 -c ' import json; print("\n".join(s["name"] for s in json.load(open("export/manifest.json"))["screens"]))') fi status=0 for name in "${screens[@]}"; do build=$(python3 -c ' import json,sys m=json.load(open("export/manifest.json")) f=next(s["file"] for s in m["screens"] if s["name"]==sys.argv[1]) print(json.load(open("export/"+f))["source"]["build"])' "$name") "$CLI" screen render "$DISC/dat/GP_TITLE.pak" "$OUT/$name.ref.png" \ --build "$build" --black --primitives --animated >/dev/null godot --path port --resolution 1280x720 -- \ "--screen=$name" --pose=rest "--capture=$OUT/$name.godot.png" >"$OUT/$name.log" 2>&1 convert "$OUT/$name.godot.png" "$OUT/$name.ref.png" \ -compose difference -composite -colorspace Gray -auto-level "$OUT/$name.diff.png" read -r max mean <<<"$(convert "$OUT/$name.godot.png" "$OUT/$name.ref.png" \ -compose difference -composite -format "%[fx:maxima*255] %[fx:mean*255]" info:)" # 3/255 is what integer-truncating compositing in the CLI and float rounding # in a GPU differ by. Anything above that is a placement, order or colour # disagreement and needs a reason, not a threshold. verdict=OK awk "BEGIN{exit !($max > 3)}" && { verdict=DIFFERS; status=1; } printf '%-16s build %-3s max %-5s mean %-8s %s\n' "$name" "$build" "$max" "${mean:0:6}" "$verdict" done echo "artifacts in $OUT" exit $status