#!/usr/bin/env bash # INTERACTIVE launcher for the NATIVE Linux Xenia-Canary build. # # Opens a REAL window on your desktop (display :0) and — unlike # run-canary-native-safe.sh — uses HARDWARE Vulkan (the AMD GPU) and leaves # audio ON. This is for hands-on play/testing, not headless tracer runs. # # ⚠ WARNING: this renders on the same AMD GPU that drives your VS Code / desktop. # The historical "crashes VS Code" issue was vkd3d-proton (D3D12->Vulkan under # Wine); the native build talks to Vulkan directly, so it MAY be fine — but if # the desktop glitches or VS Code dies, fall back to run-canary-native-safe.sh # (software Vulkan) or set CANARY_SOFTWARE=1 below. # # Usage: tools/run-canary-native.sh [seconds] (no arg => runs until you close it) # env: CANARY_BIN override binary (default = worktree Release build) # CANARY_SOFTWARE=1 force software Vulkan (lavapipe) even here # CANARY_MUTE=1 start muted / no audio device (--apu=nop) # CANARY_GPU= pick a specific Vulkan device by name (e.g. radv, 6800) # CANARY_EXTRA_ARGS space-separated extra cvars # DISPLAY target X display (default :0 = your screen) # Output: /tmp/canary_native_interactive.stdout (+ xenia.log in the binary dir). set -u # Paths derive from where this script sits — see the note in run-canary-safe.sh. HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKSPACE="$(cd "$HERE/../.." && pwd)" BIN_DEFAULT="$WORKSPACE/xenia-canary-native/build/bin/Linux/Release/xenia_canary" BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}" ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}" [ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; } SECS="${1:-0}" # 0 => no timeout (interactive) export DISPLAY="${DISPLAY:-:0}" [ -x "$BIN_EXE" ] || { echo "ABORT: native binary not found/executable: $BIN_EXE"; exit 4; } # --- Vulkan device selection --- if [ "${CANARY_SOFTWARE:-0}" = "1" ]; then export VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.json export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json export MESA_VK_DEVICE_SELECT=llvmpipe echo "GPU: forced software Vulkan (lavapipe)." else # Hardware Vulkan. Optionally pin a device by substring (RADV AMD is default [0]). [ -n "${CANARY_GPU:-}" ] && export MESA_VK_DEVICE_SELECT="$CANARY_GPU" echo "GPU: HARDWARE Vulkan${CANARY_GPU:+ (pinned: $CANARY_GPU)}." echo " Visible devices:"; vulkaninfo --summary 2>/dev/null | grep -i deviceName | sed 's/^/ /' fi # --- logging: keep it QUIET so kernel/APU debug spam doesn't starve the audio # worker thread. At --log_level=3 (Debug) the d>/A> firehose (piped through # tee) stalls the ALSA pipeline and mission audio dies permanently — the # exact "mission-audio silence" that commit f10484834 fixes in code; the # log flood defeats the keepalive. log_mask=13 = suppress Kernel|Cpu|Gpu. # (guest_audio_flags defaults to 0 = Digital Stereo, the Linux-safe path.) LOGLEVEL="${CANARY_LOGLEVEL:-1}" LOGMASK="${CANARY_LOGMASK:-13}" # --- audio --- args=(--log_level="$LOGLEVEL" --log_mask="$LOGMASK") if [ "${CANARY_MUTE:-0}" = "1" ]; then export SDL_AUDIODRIVER=dummy args+=(--mute=true --apu=nop) echo "AUDIO: muted (apu=nop)." else echo "AUDIO: on (ALSA)." fi # --- audio RE: capture true XMA per-stream params (channels/rate/head bytes) to # xenia.log as you play. Deduped (one line per unique sound); logs at Warning # so it shows at the audio-safe log level without spam. Off unless requested. if [ "${CANARY_XMA_PROBE:-0}" = "1" ]; then args+=(--xma_param_probe=true) echo "XMA-PARAM probe: ON (params captured to xenia.log)." fi # --- audio watchdog: when audio dies mid-mission and never returns, report # WHICH pipeline stage stopped (guest callback blocked / no XMA decode / # no frames submitted / host driver not writing). Near-silent while healthy. if [ "${CANARY_AUDIO_WD:-0}" = "1" ]; then args+=(--audio_watchdog=true) echo "AUDIO watchdog: ON (logs 'AUDIO-WD ...' when audio dies)." fi # --- hang watchdog: if the guest stops presenting frames for N seconds, the # emulator dumps every guest thread's registers + guest call stack to # xenia.log by itself. No debugger, no ptrace, no need to keep the window # open -- the freeze autopsy is already in the log. if [ -n "${CANARY_HANG_WD:-}" ]; then args+=(--hang_watchdog_secs="$CANARY_HANG_WD") echo "HANG watchdog: ON (${CANARY_HANG_WD}s without a frame => guest dump to xenia.log)." fi if [ -n "${CANARY_EXTRA_ARGS:-}" ]; then read -ra EXTRA <<< "$CANARY_EXTRA_ARGS" args+=("${EXTRA[@]}") fi # --- CPU stress: the mid-mission audio death only shows up under host CPU load # (the guest's audio callback misses its 5.33ms deadlines and the game tears # its own audio client down). CANARY_STRESS= spins n busy loops for the # duration of the run so the bug reproduces on demand instead of by luck. # They are killed when the run ends. Silent (no audio involvement). STRESS_PIDS=() stress_cleanup() { if [ ${#STRESS_PIDS[@]} -gt 0 ]; then kill "${STRESS_PIDS[@]}" 2>/dev/null echo "stress: stopped ${#STRESS_PIDS[@]} load generators." fi } trap stress_cleanup EXIT INT TERM if [ -n "${CANARY_STRESS:-}" ] && [ "${CANARY_STRESS}" -gt 0 ] 2>/dev/null; then for _i in $(seq 1 "$CANARY_STRESS"); do # Pure userspace spin; no I/O, no audio, no privileges. ( while :; do :; done ) & STRESS_PIDS+=($!) done echo "STRESS: ON -- ${CANARY_STRESS} busy loops competing for CPU (of $(nproc) cores)." fi pkill -x xenia_canary 2>/dev/null cd "$(dirname "$BIN_EXE")" || { echo "ABORT: bin dir missing"; exit 4; } rm -f xenia.log echo "launch (interactive, DISPLAY=$DISPLAY): $(basename "$BIN_EXE") ${args[*]}" if [ "$SECS" -gt 0 ] 2>/dev/null; then timeout -k 5 "$SECS" "$BIN_EXE" "$ISO" "${args[@]}" 2>&1 | tee /tmp/canary_native_interactive.stdout else "$BIN_EXE" "$ISO" "${args[@]}" 2>&1 | tee /tmp/canary_native_interactive.stdout fi rc=${PIPESTATUS[0]} echo "rc=$rc ADV.wmv=$(grep -ac 'ADV.wmv' xenia.log 2>/dev/null)"