[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) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-18 15:28:49 +02:00
parent 6ff184694d
commit a3aa3cc7d6

View File

@@ -115,18 +115,35 @@ fn resolve_vertex_window(
let fc = fetch_const? as u32; let fc = fetch_const? as u32;
let dword0 = rf.read(CONST_BASE_FETCH + fc * 6); let dword0 = rf.read(CONST_BASE_FETCH + fc * 6);
let dword1 = rf.read(CONST_BASE_FETCH + fc * 6 + 1); 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; let base_bytes = dword0 & 0xFFFF_FFFC;
if base_bytes == 0 { if base_bytes == 0 {
return None; 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. // 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 size_dwords = ((dword1 >> 2) & 0x00FF_FFFF).clamp(1, MAX_WINDOW_DWORDS);
let window_base_dwords = base_bytes >> 2; let window_base_dwords = base_bytes >> 2;
let mut dwords = Vec::with_capacity(size_dwords as usize); let mut dwords = Vec::with_capacity(size_dwords as usize);
for i in 0..size_dwords { for i in 0..size_dwords {
let addr = base_bytes.wrapping_add(i * 4); let addr = read_base.wrapping_add(i * 4);
if addr < base_bytes { if addr < read_base {
break; // wrap guard break; // wrap guard
} }
// `read_u32` composes big-endian bytes into the u32 value; the WGSL's // `read_u32` composes big-endian bytes into the u32 value; the WGSL's