Files
NX2navi/tests/gpa_check.py
chrg6 f321697aba Give the device address table's empty blocks back, so pool addresses that climb no longer run it out.
The game's allocator hands out pool addresses that climb through the 64 GiB alias region, and a block of the table, once given to a 16 MiB range, stayed with it for good: after 16 GiB of ranges ever mapped the table logged "out of L2 blocks", and every pool created from then on stayed out of it, so shaders read zeros through pointers into it (foliage, instance and light data) until the game was started again. A block whose last page is unmapped now leaves its range at once and serves another once the GPU is past the submissions that could still read the old entry, as a record does; a range mapped again while its block waits takes that block back. A range that finds no block no longer ends the mapping of the ranges after it, and the log line says how many blocks are in use and waiting.

tests\gpa_check.py builds vk_gpa.c against stand-ins for the allocator, the device and the completion queue and reads the table as a shader does. On the table as it was, its climbing pools read zeros.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 02:25:14 -05:00

48 lines
1.7 KiB
Python

"""Build and run tests/gpa_check.c: runtime/vk_gpa.c's device address table, with blocks given back and taken again.
Needs the Vulkan headers and vk_mem_alloc.h (docs/building.md), no GPU and no game files.
Usage: gpa_check.py
"""
import os
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MSYS_BIN = Path(r'C:\msys64\ucrt64\bin')
def compiler():
"""$CC, else MSYS2 UCRT64's clang, else clang on PATH (as tests/diff/common.py picks it, without its Unicorn)."""
if os.environ.get('CC'):
return os.environ['CC']
if os.name == 'nt' and (MSYS_BIN / 'clang.exe').exists():
return str(MSYS_BIN / 'clang.exe')
return shutil.which('clang') or 'clang'
def vma_include():
for d in (MSYS_BIN.parent / 'include' / 'vma', ROOT / 'toolchain' / 'vma', Path('/usr/include')):
if (d / 'vk_mem_alloc.h').is_file():
return d
sys.exit('gpa_check: vk_mem_alloc.h not found (docs/building.md)')
def main():
work = ROOT / 'build' / 'test' / 'gpa'
work.mkdir(parents=True, exist_ok=True)
exe = work / ('gpa_check.exe' if os.name == 'nt' else 'gpa_check')
env = dict(os.environ)
if os.name == 'nt' and MSYS_BIN.exists():
env['PATH'] = str(MSYS_BIN) + os.pathsep + env.get('PATH', '')
subprocess.check_call([compiler(), '-std=gnu11', '-O1', '-Wall', '-fno-strict-aliasing', '-I', str(ROOT / 'runtime'),
'-I', str(vma_include()), str(ROOT / 'tests' / 'gpa_check.c'),
str(ROOT / 'runtime' / 'vk_gpa.c'), '-o', str(exe)], env=env)
return subprocess.run([str(exe)], env=env).returncode
if __name__ == '__main__':
sys.exit(main())