From a3aa3cc7d621c50ea3b4ac9c6074b1264a1c86f1 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Thu, 18 Jun 2026 15:28:49 +0200 Subject: [PATCH] [iterate-3Q] draw_capture: read vertex window via physical alias The UI geometry-capture read the vertex-fetch base at its bare low VA (~0x0adf_xxxx), which is unmapped in ours, so it copied all-zeros. The fetch constant's address:30 field is a guest *physical* dword address (canary reads it via Memory::TranslatePhysical, draw_util.cc:961). Ours only maps the cached-physical window at 0x4000_0000 (physical_to_backing). Rebase a low physical base onto that mapped alias when the raw VA is unmapped; window_base_dwords still carries the original base so the shader's rebase indexes the uploaded window. Decode itself was verified correct against canary (xe_gpu_vertex_fetch_t + GetVertexFetch + ucode.h vfetch const_index*3+const_index_sel): for the splash draws const_index_sel==0, so ours' stride-6 register offset lands on the exact same constant as canary's stride-2 offset; raw dwords match byte-for-byte. UI-only path (frame_captures is None in headless), so the deterministic --gpu-inline golden is byte-identical (verified) and 679 tests stay green. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/xenia-gpu/src/draw_capture.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/xenia-gpu/src/draw_capture.rs b/crates/xenia-gpu/src/draw_capture.rs index 2453e01..df4df75 100644 --- a/crates/xenia-gpu/src/draw_capture.rs +++ b/crates/xenia-gpu/src/draw_capture.rs @@ -115,18 +115,35 @@ fn resolve_vertex_window( let fc = fetch_const? as u32; let dword0 = rf.read(CONST_BASE_FETCH + fc * 6); let dword1 = rf.read(CONST_BASE_FETCH + fc * 6 + 1); - // address:30 at bits[31:2] of dword0 (in bytes once masked). + // address:30 at bits[31:2] of dword0 (in bytes once masked). The fetch + // constant carries a guest *physical* dword address — canary reads the + // vertex buffer via `Memory::TranslatePhysical(fetch.address * 4)` + // (`draw_util.cc:961`). On the Xbox 360 the physical range is mirrored at + // several virtual windows; ours only maps the cached-physical window at + // `0x4000_0000` (`gpu_system::physical_to_backing`). Reading the bare low + // address (`0x0adf_xxxx`) hits an unmapped VA and returns zeros, so rebase + // a low physical base onto the mapped `0x4000_0000` alias when the raw VA + // is not itself mapped. `window_base_dwords` keeps the *original* base so + // the shader's rebase against the (unmodified) fetch-constant address still + // indexes the uploaded window correctly. let base_bytes = dword0 & 0xFFFF_FFFC; if base_bytes == 0 { return None; } + let read_base = if mem.translate(base_bytes).is_some() { + base_bytes + } else if base_bytes < 0x2000_0000 && mem.translate(base_bytes | 0x4000_0000).is_some() { + base_bytes | 0x4000_0000 + } else { + base_bytes + }; // size:24 at bits[25:2] of dword1, in dwords. Clamp to our window cap. let size_dwords = ((dword1 >> 2) & 0x00FF_FFFF).clamp(1, MAX_WINDOW_DWORDS); let window_base_dwords = base_bytes >> 2; let mut dwords = Vec::with_capacity(size_dwords as usize); for i in 0..size_dwords { - let addr = base_bytes.wrapping_add(i * 4); - if addr < base_bytes { + let addr = read_base.wrapping_add(i * 4); + if addr < read_base { break; // wrap guard } // `read_u32` composes big-endian bytes into the u32 value; the WGSL's