From da7c29b6d27b9cc2fa06d18908e80c24c3c8e4a4 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Thu, 18 Jun 2026 18:07:00 +0200 Subject: [PATCH] [iterate-3V] Fix logo texture: map texture-fetch physical base onto backing window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The publisher-logo texture (K8888 1280x768 linear, `E59B2B3D`'s tfetch surface) rendered flat/transparent because the GPU texture decode read the wrong host bytes — NOT because the asset was never decompressed. First-divergence (vs canary, measured both engines): - Ours DOES read game:\hidden\Resource3D\*.xpr in full, builds a byte-identical cache, decompresses the logo, and CPU-writes the real artwork (~839K nonzero bytes) into the texture buffer — at the guest physical-aperture VA 0x4dbee000 (writer sub_823C3E70 @ 0x823c3f8c). This REFUTES the iterate-3U verdict that the texture was never filled. - BUT the GPU decode used the raw fetch-constant base 0x0dbee000 as a virtual address. In ours' flat 4GB memory, virtual 0x0dbee000 and the physical alias 0x4dbee000 are DIFFERENT host bytes (no aliasing in the read/write path), so the decode read all-zeros. The Xenos texture fetch constant carries a guest *physical* base; the CPU writes texels through its cached-physical aperture, which ours backs at the committed 0x4000_0000 window. Map the base via the existing `physical_to_backing` helper before reading — exactly as the vertex fetch path (draw_capture.rs, iterate-3Q) and as canary reads textures through its GPU shared memory (= physical). Measured after fix (env-gated probe, removed): the logo decode reads base=0x4dbee000 and produces 839068/3932160 nonzero bytes (21.3%) — a centered logo on a transparent field, matching canary's ~21% exactly. Determinism: GPU-side pure read; no CPU/guest-memory state changes. The n50m --gpu-inline --stable-digest golden is byte-identical (verified 2x, texture_cache_entries unchanged). cargo test --workspace green. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/xenia-gpu/src/gpu_system.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/xenia-gpu/src/gpu_system.rs b/crates/xenia-gpu/src/gpu_system.rs index 86a7f2c..03952b3 100644 --- a/crates/xenia-gpu/src/gpu_system.rs +++ b/crates/xenia-gpu/src/gpu_system.rs @@ -1385,9 +1385,24 @@ impl GpuSystem { .register_file .read(CONST_BASE_FETCH + slot as u32 * 6 + k as u32); } - let Some(key) = crate::texture_cache::decode_fetch_constant(fetch6) else { + let Some(mut key) = crate::texture_cache::decode_fetch_constant(fetch6) else { continue; }; + // The Xenos texture fetch constant carries a guest + // *physical* base address (`base >> 12`). On the Xbox + // 360 the GPU reads the unified physical memory; the + // CPU writes the (decompressed) texels through its + // cached-physical aperture, which ours backs at the + // committed `0x4000_0000` window. Map the physical + // base onto that backing window so the GPU samples the + // bytes the guest actually wrote — exactly as the + // vertex-fetch path does (`draw_capture.rs`) and as + // canary reads textures through its GPU shared memory + // (= physical). Without this the decode reads the + // low VA `0x0dbee000` (always zero) instead of the + // filled `0x4dbee000`, flattening every disk-asset + // texture (e.g. the publisher logo `E59B2B3D`). + key.base_address = physical_to_backing(key.base_address); let bi = key.format.block_info(); let span_bytes = (key.pitch_texels as u32) * (key.height as u32)