Files
NX2navi/tests/build_gate_check.py
chrg6 39fa0be78c Make every pipeline built ahead of its first use pass a memory gate that counts the builds already running.
Only the large compute shaders' background builder looked at the system's commit before it started a build, and it
saw nothing of what the builds beside it were about to take. The boot's recorded pipelines (up to 64 builders), the
pool's background builds during play, the boot's compute pipelines and stage libraries and the library warm asked
nobody.

Each of them now asks the gate before a build. A build that starts reserves an estimate of the driver compiler's
working memory until it ends (build_reserve_mb, 64 MB; 4096 MB for a large compute shader), and the gate admits a
build only while the commit headroom holds the part kept free (build_free_mb, 4096 MB), the reservations and its own.
A refused boot builder ends and leaves its pipelines to the pool; the pool leaves a background build queued and asks
again every two seconds; the stage library prebuild and the library warm wait up to 30 seconds and then end, and the
warm goes on at the next start. A pipeline a draw or a dispatch waits for is never refused. The preparation of the
pipeline list runs without the gate: no game runs beside it.

tests/build_gate_check.py checks the gate's arithmetic and, through --probe-buildgate, its answers with the machine's
own commit. What the builders do when refused is not exercised by a test.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 04:42:07 -05:00

74 lines
3.4 KiB
Python

"""Build and run tests/build_gate_check.c: runtime/vk_build_gate.h, the memory gate of the background pipeline builds:
what it admits and refuses by the system's commit, and that the builds under way count through their reservations.
With the game executable's path it also runs the executable's --probe-buildgate (runtime/vk_pipeline.c
vk_pipeline_probe_buildgate: the gate as the builders call it, with this machine's commit; no device, no window, no
build) three times: with the settings in effect, with build_free_mb = 0, where every build must be admitted, and
with build_free_mb at a value no machine has free, where every build must be refused.
Usage: build_gate_check.py [PATH_TO_GAME_EXE]
"""
import os
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'tests' / 'diff'))
try:
from common import MSYS_BIN, compiler # noqa: E402
except ImportError: # a Python without the differential tests' packages: the same compiler choice without them
MSYS_BIN = Path(r'C:\msys64\ucrt64\bin')
def compiler():
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 main():
work = ROOT / 'build' / 'test' / 'build_gate'
work.mkdir(parents=True, exist_ok=True)
exe = work / ('build_gate_check.exe' if os.name == 'nt' else 'build_gate_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(), '-O1', '-Wall', '-Werror', '-I', str(ROOT / 'runtime'),
str(ROOT / 'tests' / 'build_gate_check.c'), '-o', str(exe)], env=env)
rc = subprocess.run([str(exe)], env=env).returncode
if len(sys.argv) > 1:
rc |= probe(sys.argv[1])
return rc
def probe(game):
"""The executable's --probe-buildgate under three settings; 0 when each run passes and says what it must."""
fails = 0
for free_mb, must in ((None, None), ('0', 'an ordinary build is admitted now'),
('1048576', 'an ordinary build is refused now')):
env = dict(os.environ)
env.pop('NX2NAVI_BUILD_FREE_MB', None)
if free_mb is not None:
env['NX2NAVI_BUILD_FREE_MB'] = free_mb
r = subprocess.run([game, '--probe-buildgate'], env=env, capture_output=True, text=True, errors='replace')
lines = [ln for ln in r.stdout.splitlines() if ln.startswith('probe-buildgate:')]
print('\n'.join(lines))
ok = r.returncode == 0 and lines and lines[-1].endswith('passed') and (must is None or must in lines[-1])
if free_mb == '0':
ok = ok and any('16 of 16 ordinary builds admitted' in ln and 'admitted' in ln.split(';')[-1] for ln in lines)
if free_mb == '1048576':
ok = ok and any('0 of 16 ordinary builds admitted' in ln and 'refused' in ln.split(';')[-1] for ln in lines)
if not ok:
fails += 1
print('build_gate_check: FAIL: --probe-buildgate with build_free_mb %s (exit code %d)'
% (free_mb or 'as set', r.returncode))
print('build_gate_check: the executable\'s gate %s' % ('FAILED' if fails else 'passed'))
return 1 if fails else 0
if __name__ == '__main__':
sys.exit(main())