[iterate-4C] M3: default connected controller for user 0 (reach the Press A title loop)
Sylpheed's boot reaches its front-end TITLE shell (steady frame loop — 2219 swaps over 5B instr; XamResetInactivity/XamEnableInactivityProcessing/ XamUserGetSigninState front-end mgmt; XamInputGetCapabilities polled 8880x) but then STALLS: headless has no HID device, so GetCapabilities returns DEVICE_NOT_CONNECTED and the game loops forever waiting for a controller. It never advances to read input (XamInputGetState / XamInputGetKeystrokeEx = 0). Canary reaches the "Press A" menu because its default input drivers (xinput / winkey) present an always-connected controller. Match that: present a default always-connected, IDLE (no-buttons) virtual controller for user 0 (a console always exposes controller slots; a real gamepad under --ui still supplies actual button state — see xam_input_get_state, which prefers ui.snapshot_gamepad()). Set XENIA_NO_PAD=1 to present no controller (the raw headless no-HID trajectory). MEASURED (headless, this is not inference): with the controller connected the game advances into the interactive input-poll loop — XamInputGetKeystrokeEx 0 -> 2164 (once per frame) XamInputGetState 0 -> 2165 XamInputSetState (rumble) 0 -> 1 i.e. the "Press A" title-screen loop (attract video repeats while it polls for A), exactly the M3 milestone behavior. Visual confirmation of the on-screen "Press A" prompt is the remaining step (--ui + frame readback). GOLDEN RE-BASELINE (deliberate): a connected controller changes the guest trajectory WITHIN the 200M window (the title polls input before 200M), so both sylpheed_n200m.json and sylpheed_n2m.json are re-baselined. Change is proven ISOLATED: XENIA_NO_PAD=1 reproduces the OLD goldens byte-identical at both n. New n200m: instructions 200000203, imports 575647, draws 3208, swaps 910 (was 200000239 / 575447 / 3165 / 895 — small front-end-input increments; RTs/ shaders/textures unchanged). All 6 JIT configs match the new golden byte- identical (interp / JIT / JIT+CHAIN / +REGCACHE / REGION_MAX=1 / budget=1== interp+budget=1); 131 xenia-kernel + 21 xenia-jit tests green.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"instructions": 200000239,
|
||||
"imports": 575447,
|
||||
"instructions": 200000203,
|
||||
"imports": 575647,
|
||||
"unimpl": 0,
|
||||
"draws": 3165,
|
||||
"swaps": 895,
|
||||
"draws": 3208,
|
||||
"swaps": 910,
|
||||
"unique_render_targets": 2,
|
||||
"shader_blobs_live": 6,
|
||||
"texture_cache_entries": 1
|
||||
|
||||
@@ -125,6 +125,29 @@ fn gamepad_key(state: &xenia_hid::GamepadState) -> u128 {
|
||||
u128::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Whether user 0 has a default always-connected, idle (no-buttons) virtual
|
||||
/// controller. **Default ON** — matches canary, whose default input drivers
|
||||
/// (xinput / winkey) present a connected pad, so a title screen advances to its
|
||||
/// "Press A" input-poll state instead of stalling forever on
|
||||
/// DEVICE_NOT_CONNECTED (a console always exposes controller slots; a real
|
||||
/// gamepad under `--ui` supplies actual button state, otherwise the pad is idle).
|
||||
/// Set `XENIA_NO_PAD=1` to present NO controller (reproduces the raw headless
|
||||
/// no-HID trajectory).
|
||||
fn virtual_pad_enabled() -> bool {
|
||||
use std::sync::OnceLock;
|
||||
static V: OnceLock<bool> = OnceLock::new();
|
||||
*V.get_or_init(|| {
|
||||
!std::env::var("XENIA_NO_PAD")
|
||||
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
|
||||
/// True iff user 0 has a controller: a real host gamepad, or the default pad.
|
||||
fn user_connected(state: &KernelState, user: u32) -> bool {
|
||||
state.ui.as_ref().is_some_and(|ui| ui.is_connected(user)) || (user == 0 && virtual_pad_enabled())
|
||||
}
|
||||
|
||||
fn xam_input_get_capabilities(
|
||||
ctx: &mut PpcContext,
|
||||
mem: &GuestMemory,
|
||||
@@ -133,8 +156,7 @@ fn xam_input_get_capabilities(
|
||||
// r3 = user_index, r4 = flags, r5 = out X_INPUT_CAPABILITIES*
|
||||
let user = ctx.gpr[3] as u32;
|
||||
let out_ptr = ctx.gpr[5] as u32;
|
||||
let connected = state.ui.as_ref().is_some_and(|ui| ui.is_connected(user));
|
||||
if !connected {
|
||||
if !user_connected(state, user) {
|
||||
ctx.gpr[3] = xenia_hid::errors::DEVICE_NOT_CONNECTED as u64;
|
||||
return;
|
||||
}
|
||||
@@ -146,15 +168,16 @@ fn xam_input_get_state(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut Kern
|
||||
// r3 = user_index, r4 = flags, r5 = out X_INPUT_STATE*
|
||||
let user = ctx.gpr[3] as u32;
|
||||
let out_ptr = ctx.gpr[5] as u32;
|
||||
let Some(ui) = state.ui.as_ref() else {
|
||||
ctx.gpr[3] = xenia_hid::errors::DEVICE_NOT_CONNECTED as u64;
|
||||
return;
|
||||
};
|
||||
if !ui.is_connected(user) {
|
||||
if !user_connected(state, user) {
|
||||
ctx.gpr[3] = xenia_hid::errors::DEVICE_NOT_CONNECTED as u64;
|
||||
return;
|
||||
}
|
||||
let gamepad = ui.snapshot_gamepad();
|
||||
// Real host gamepad if present, else the idle virtual pad (XENIA_VIRTUAL_PAD).
|
||||
let gamepad = state
|
||||
.ui
|
||||
.as_ref()
|
||||
.map(|ui| ui.snapshot_gamepad())
|
||||
.unwrap_or_default();
|
||||
let key = gamepad_key(&gamepad);
|
||||
if key != state.last_input_bytes {
|
||||
state.input_packet_number = state.input_packet_number.wrapping_add(1);
|
||||
|
||||
Reference in New Issue
Block a user