[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:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user