Files
Cesarus85--mjolnir-vr/tools/test_android_mirror_uploads.py
T
chestahh 3975480a3b Fix Android Custom Edition texture residency and draw state
Keep synchronous cached CE pixels and hardware headers at stable addresses
outside the fixed Xbox arena, bounded to 128 MiB and 8192 entries. Only
older-frame textures can be evicted; unregister bindings, GPU copies and
channel annotations before freeing, including map/bitmap teardown and flush.
Preserve the original readers, conversion, normal GPU caching and HUD
replacement behavior. Refresh Android GL shadow state before each draw.

Keep the player-tested Android streaming geometry bypass and its regression
tests, since current upstream main does not contain that fix yet. Desktop
and web preserve their cache, upload and draw-state behavior. No fullscreen,
simulation, networking or map-format changes are included.

The engine-owned bitmap getter and teardown require small source hooks,
each marked port:. The renderer admits heap pixels only through a registered
header and validates the resource extent against its allocation. Diagnostics
are limited to unload summaries and the first allocation warning per lifetime.

Device evidence: the tester reports correct rendering and stable FPS, with
zero allocation failures, fallback substitutions or evictions across four CE
visits on Mali-G615 and 87.5 MiB peak residency. Local tests cover stable
uploads, bounds, OOM, frame locks, eviction, cleanup, HUD residency, reduced
diagnostics, normal write tracking, Android-only state refresh and geometry
streaming. All 30 upstream-focused checks passed before final CI validation.
Full Android/web builds require CI because local targets are unavailable.
No fork packaging, branding, map adapters or unrelated changes are included.

Player-Note: Fix Android Custom Edition texture corruption and repeated cache upload hitches.
Player-Note: Prevent stretched or missing geometry on Android by streaming geometry directly.

(cherry picked from commit 4ac4685b96c39b20491095579ce469f8afcd2d3c)
2026-10-21 01:27:08 +01:00

78 lines
4.0 KiB
Python

"""Check Android geometry streaming and preserved desktop/web caching.
Compile the production mirror and frame rotation. Model driver storage
renaming, not a particular phone; this does not establish the device cause.
"""
import os
from pathlib import Path
import shutil
import subprocess
import pytest
from harness import function, mutated
ROOT = Path(__file__).resolve().parents[1]
@pytest.fixture(scope="module")
def mirror_uploads(tmp_path_factory):
compiler = shutil.which(os.environ.get("CC", "clang"))
if not compiler:
pytest.skip("clang required")
renderer = (ROOT / "port/linux/src/d3d8_gl.c").read_text()
mirror = renderer[renderer.index("#define MIRROR_SEGMENT_SIZE"):
renderer.index("/* the smallest and largest index of an index range")]
present = function(renderer, "D3DDevice_Present")
rotation = present[present.index("host_gl_fence_frame("):
present.index("if (xgpu_capabilities.atomic_counters && device.ring_test_count")]
directory = tmp_path_factory.mktemp("android-mirror-regression")
target = ["--target=i686-pc-windows-msvc", "-fuse-ld=lld"] if os.name == "nt" else ["-m32"]
binaries = {}
for variant, defines in (("android", ["-DHALO_ANDROID=1"]), ("android-cached", ["-DHALO_ANDROID=1"]),
("gles", []), ("web", ["-DHALO_WEB=1"]),
("desktop", ["-DTEST_DESKTOP=1"]), ("unguarded", [])):
work = directory / variant
work.mkdir()
selected = mutated(mirror,
"unused && device.frame - mirror.subdata_frame[segment] >= STREAM_BUFFER_RING",
"unused") if variant == "unguarded" else mirror
if variant == "android-cached":
selected = mutated(selected,
"/* Persistent geometry caching causes corruption on affected Android\n"
"\tdevices. Use current vertex/index bytes through the streaming path. */\n"
"\treturn FALSE;",
"/* Control: leave the cache enabled. */")
(work / "mirror.inc").write_text(selected)
(work / "rotation.inc").write_text(rotation)
executable = work / ("test.exe" if os.name == "nt" else "test")
result = subprocess.run(
[compiler, *target, *defines, "-std=gnu89", "-Wall", "-Wextra", "-Werror",
"-Wno-unused-function", "-O2", "-I", str(work),
str(ROOT / "port/android/tests/mirror_upload_test.c"), "-o", str(executable)],
capture_output=True, text=True)
assert result.returncode == 0, result.stdout + result.stderr
binaries[variant] = executable
return binaries
@pytest.mark.parametrize("case", ["reuse", "rollover"])
@pytest.mark.parametrize("variant", ["android", "android-cached", "gles", "web", "desktop", "unguarded"])
def test_mirror_upload_ordering(mirror_uploads, case, variant):
result = subprocess.run([str(mirror_uploads[variant]), case], capture_output=True, text=True, timeout=15)
if variant == "android-cached":
assert result.returncode == 1, result.stdout + result.stderr
assert result.stderr.rstrip().endswith(
": !mirror_range(PLATFORM_CONTIGUOUS_BASE, 300 * 4096, &buffer, &offset, &generation)"), result.stderr
elif variant == "unguarded":
assert result.returncode == 1, result.stdout + result.stderr
assert "repair_uploads=0" in result.stderr, result.stderr
assert result.stderr.rstrip().endswith(": overlaps == 0 && mismatches == 0"), result.stderr
assert "overlaps=0 " not in result.stderr and "bad_draws=0 " not in result.stderr, result.stderr
else:
assert result.returncode == 0, result.stdout + result.stderr
if variant == "android":
assert "bypassed=4800 allocations=0 uploads=0 watch_reads=0 protections=0" in result.stderr, result.stderr
elif variant == "desktop":
assert "mapped=0 " in result.stderr, result.stderr