Files
d4r/tests/test_asi_setup.py
T
ayo 34e6b0fe71 rr: add FSR Ray Regeneration, exact RDNA3 inference and motion experiments
Add an experimental FSR Ray Regeneration backend for D3D12 RR requests:
- Convert depth, motion, normals and albedos into AMD denoiser inputs.
- Split and recompose diffuse/specular signals, then run Super Sampling.
- Add stability/detail controls, demodulation guards and halo limiting.
- Handle AMD provider discovery under NVIDIA GPU spoofing.
- Add runtime probes, synthetic reference scenes and per-dispatch profiling.

Add exact gfx11 DLSS-RR inference using f16 cooperative matrices:
- Reproduce FP8 conversions, accumulation and camera reprojection arithmetic.
- Widen model weights and avoid requiring VK_EXT_shader_float8.
- Extend model building, replay tools and descriptor metadata.

Expand motion preprocessing and experimental AW2 native-motion support:
- Add tiled LDS dilation, padded allocation handling and thin-only sky fill.
- Order dilation with input waits and NGX on the selected stream.
- Add jitter-aligned native composition, hybrid routing and wire history repair.
- Add prepared masks, sparse repair and independent motion/repair controls.

Add optional fused R11G11B10 input/output conversion, sampled stage timings
and profile summarization. Extend configuration, packaging, documentation,
FidelityFX headers and CPU/GPU regression coverage.
2026-10-16 04:55:25 -04:00

368 lines
21 KiB
Python

"""Native ASI configuration: backups, existing plugins, loader contract and automatic dispatch."""
import configparser
import hashlib
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import time
import unittest
ROOT = Path(__file__).resolve().parents[1]
@unittest.skipUnless(shutil.which('wine') and shutil.which('x86_64-w64-mingw32-g++'), 'Wine/MinGW required')
class AsiSetupTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.temporary = tempfile.TemporaryDirectory(prefix='d4r-asi-tests-')
cls.directory = Path(cls.temporary.name)
cls.helper = cls.directory / 'd4r-setup.exe'
cls.probe = cls.directory / 'probe.exe'
for source, output in (('d4r_asi_setup.cpp', cls.helper), ('d4r_asi_probe.cpp', cls.probe)):
subprocess.run(['x86_64-w64-mingw32-g++', '-std=c++20', '-O2', '-Wall', '-Wextra',
'-Werror', '-static', '-municode', str(ROOT / 'tools' / source), '-o', str(output)],
check=True, capture_output=True)
# Hold the second destination open without delete sharing. The helper
# must restore the first DLL when the second replacement fails.
lock_source = cls.directory / 'locked-dll.cpp'
lock_source.write_text(r'''
#include <windows.h>
#include <string>
int wmain(int argc, wchar_t** argv) {
if (argc != 5) return 2;
HANDLE locked = CreateFileW(argv[2], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (locked == INVALID_HANDLE_VALUE) return 3;
std::wstring command = L"\"" + std::wstring(argv[1]) + L"\" \"" + argv[3] + L"\" \"" + argv[4] + L"\"";
STARTUPINFOW startup = {}; startup.cb = sizeof(startup);
PROCESS_INFORMATION process = {};
if (!CreateProcessW(argv[1], command.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup, &process)) return 4;
WaitForSingleObject(process.hProcess, INFINITE);
DWORD code = 0; GetExitCodeProcess(process.hProcess, &code);
CloseHandle(process.hThread); CloseHandle(process.hProcess); CloseHandle(locked);
return code == 1 ? 0 : 5;
}
''')
cls.lock_probe = cls.directory / 'locked-dll.exe'
subprocess.run(['x86_64-w64-mingw32-g++', '-std=c++20', '-O2', '-Wall', '-Wextra',
'-Werror', '-static', '-municode', str(lock_source), '-o', str(cls.lock_probe)],
check=True, capture_output=True)
cls.env = {k: v for k, v in os.environ.items() if not k.startswith('D4R_')}
cls.env.update(WINEPREFIX=str(cls.directory / 'prefix'), WINEDEBUG='-all', WINEARCH='win64',
WINEDLLOVERRIDES='winemenubuilder.exe=d', D4R_ASI_SETUP_QUIET='1')
@classmethod
def tearDownClass(cls):
cls.temporary.cleanup()
@staticmethod
def winpath(path):
return 'Z:' + str(path).replace('/', '\\')
def fixture(self, game, source):
runtime = game / 'custom plugins/d4r'
runtime.mkdir(parents=True)
(game / 'Game.exe').touch()
(game / 'winmm.dll').write_bytes(b'existing OptiScaler loader')
(game / 'OptiScaler.ini').write_bytes(source)
plugin = game / 'custom plugins/d4r.asi'
plugin.write_bytes(b'ASI fixture')
(plugin.parent / 'Other.asi').write_bytes(b'other mod')
for name in ('nvcuda.dll', 'nvngx_dlss.dll', 'ngx/_nvngx.dll',
'zluda/libcuda.so', 'rocm/lib/libamdhip64.so.7'):
path = runtime / name
path.parent.mkdir(parents=True, exist_ok=True)
path.touch()
(runtime / 'd4r.ini').write_text('[DLSS]\nModel=K\n')
shutil.copy2(self.helper, runtime / 'd4r-setup.exe')
for name in ('d3d12.dll', 'd4r12core.dll'):
(runtime / name).write_bytes(('new ' + name).encode())
(game / name).write_bytes(('old ' + name).encode())
(game / 'D3D12Core.dll').write_bytes(b'original Microsoft SDK core')
return plugin
def setup(self, game, plugin):
return subprocess.run(['wine', str(self.helper), self.winpath(game), self.winpath(plugin)],
env=self.env, cwd=self.directory, text=True, capture_output=True, timeout=60)
def test_custom_plugin_folder_preservation_backup_and_retry(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp) / 'Game with spaces'
original = (b'; keep this comment\r\n[Plugins]\r\nPath=custom plugins\r\n'
b'LoadAsiPlugins=true\r\nLoadReShade=true\r\n'
b'[FrameGen]\r\nFGOutput=fsr31\r\n[Menu]\r\nShortcutKey=45\r\n')
plugin = self.fixture(game, original)
result = self.setup(game, plugin)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(next(game.glob('OptiScaler.ini.d4r-backup.*')).read_bytes(), original)
configured = (game / 'OptiScaler.ini').read_bytes()
self.assertEqual(configured.count(b'\n'), configured.count(b'\r\n'))
self.assertIn(b'LoadReShade=true\r\n', configured)
self.assertIn(b'[FrameGen]\r\nFGOutput=fsr31\r\n', configured)
self.assertIn(b'; keep this comment\r\n', configured)
ini = configparser.ConfigParser()
ini.read_string(configured.decode())
self.assertEqual(ini['Libraries']['NvngxPath'], self.winpath(plugin))
self.assertEqual(ini['Plugins']['Path'], self.winpath(plugin.parent))
self.assertEqual((game / 'winmm.dll').read_bytes(), b'existing OptiScaler loader')
self.assertEqual((plugin.parent / 'Other.asi').read_bytes(), b'other mod')
self.assertEqual((game / 'D3D12Core.dll').read_bytes(), b'original Microsoft SDK core')
for name in ('d3d12.dll', 'd4r12core.dll'):
self.assertEqual((game / name).read_bytes(), ('new ' + name).encode())
self.assertEqual(next(game.glob(name + '.d4r-backup.*')).read_bytes(), ('old ' + name).encode())
self.assertEqual(self.setup(game, plugin).returncode, 0)
self.assertEqual(len(list(game.glob('*.d4r-backup.*'))), 3)
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), configured)
check = subprocess.run(['sh', str(ROOT / 'packaging/d4r-check.sh'), str(game)],
text=True, capture_output=True)
self.assertEqual(check.returncode, 0, check.stdout + check.stderr)
self.assertIn('(d4r ASI)', check.stdout)
for old, new, message in ((b'LoadAsiPlugins=true', b'LoadAsiPlugins=false', 'LoadAsiPlugins=true'),
(b'HookOriginalNvngxOnly=true', b'HookOriginalNvngxOnly=false',
'HookOriginalNvngxOnly=true')):
(game / 'OptiScaler.ini').write_bytes(configured.replace(old, new))
check = subprocess.run(['sh', str(ROOT / 'packaging/d4r-check.sh'), str(game)],
text=True, capture_output=True)
self.assertNotEqual(check.returncode, 0)
self.assertIn(message, check.stdout)
def test_duplicate_settings_and_missing_nvidia_leave_config_untouched(self):
for source in (b'[Libraries]\nNvngxPath=auto\nnvngxpath=other\n',
b'[Plugins]\nPath=plugins\n[plugins]\nPath=elsewhere\n',
b'[Plugins]\nLoadAsiPlugins=true\n'):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp)
plugin = self.fixture(game, source)
if source == b'[Plugins]\nLoadAsiPlugins=true\n':
(plugin.parent / 'd4r/nvngx_dlss.dll').unlink()
result = self.setup(game, plugin)
self.assertNotEqual(result.returncode, 0)
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), source)
self.assertFalse(list(game.glob('*.d4r-backup.*')))
self.assertFalse(list(game.glob('.d4r-setup.*')))
@unittest.skipUnless((ROOT / 'build/d4r.asi').exists(), 'Build the ASI first')
def test_actual_asi_load_and_automatic_configuration(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp) / 'Game with spaces'
original = b'[Plugins]\nLoadAsiPlugins=true\nPath=custom plugins\n'
plugin = self.fixture(game, original)
shutil.copy2(ROOT / 'build/d4r.asi', plugin)
shutil.copy2(self.probe, game / 'probe.exe')
result = subprocess.run(['wine', str(game / 'probe.exe'), self.winpath(plugin),
self.winpath(plugin.parent / 'd4r')], env=self.env, cwd=self.directory,
text=True, capture_output=True, timeout=60)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn('ASI loader contract passed', result.stdout)
deadline = time.monotonic() + 15
status = plugin.parent / 'd4r/installation-status.txt'
while (not status.exists() or 'setup complete' not in status.read_text()) and time.monotonic() < deadline:
time.sleep(0.05)
self.assertIn(self.winpath(plugin), (game / 'OptiScaler.ini').read_text())
log = (plugin.parent / 'd4r/d4r_nvngx.log').read_text()
self.assertEqual(log.count('InitializeASI:'), 1)
self.assertFalse((plugin.parent / 'd4r_nvngx.log').exists())
def test_invalid_second_dll_destination_preserves_first_dll_and_config(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp)
source = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, source)
(game / 'd4r12core.dll').unlink()
(game / 'd4r12core.dll').mkdir()
result = self.setup(game, plugin)
self.assertNotEqual(result.returncode, 0)
self.assertEqual((game / 'd3d12.dll').read_bytes(), b'old d3d12.dll')
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), source)
self.assertFalse(list(game.glob('.d4r-setup.*')))
def test_nvidia_installer_accepts_both_runtime_layouts(self):
for nested in (False, True):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
base = Path(tmp)
runtime = base / 'd4r'
(runtime / 'ngx').mkdir(parents=True)
for name in ('nvngx.dll', 'd4r.ini'):
(runtime / name).touch()
dlss, dlssd, ngx = b'verified DLSS fixture', b'verified DLSS RR fixture', b'verified NGX fixture'
(runtime / 'nvngx_dlss.dll').write_bytes(dlss)
(runtime / 'nvngx_dlssd.dll').write_bytes(dlssd)
(runtime / 'ngx/_nvngx.dll').write_bytes(ngx)
script = (ROOT / 'install.sh').read_text()
replacements = {'DLSS_SHA256': hashlib.sha256(dlss).hexdigest(),
'DLSSD_SHA256': hashlib.sha256(dlssd).hexdigest(),
'NGX_SHA256': hashlib.sha256(ngx).hexdigest()}
script = '\n'.join(key + '=' + replacements[key] if key in replacements else line
for line in script.splitlines() for key in [line.split('=', 1)[0]]) + '\n'
installer = (runtime if nested else base) / 'install.sh'
installer.write_text(script)
result = subprocess.run(['bash', str(installer)], cwd=self.directory,
text=True, capture_output=True)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn('already installed and verified', result.stdout)
self.assertEqual((runtime / 'nvngx_dlss.dll').read_bytes(), dlss)
def test_locked_second_dll_rolls_back_first_dll(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp)
original = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, original)
result = subprocess.run(['wine', str(self.lock_probe), self.winpath(self.helper),
self.winpath(game / 'd4r12core.dll'), self.winpath(game), self.winpath(plugin)],
env=self.env, cwd=self.directory, text=True, capture_output=True, timeout=30)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
for name in ('d3d12.dll', 'd4r12core.dll'):
self.assertEqual((game / name).read_bytes(), ('old ' + name).encode())
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), original)
self.assertIn('earlier changes were restored', (plugin.parent / 'd4r/installation-status.txt').read_text())
def test_d3d12_optiscaler_loader_is_preserved(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp)
original = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, original)
loader = b'MZ fixture OptiScaler.dll\0'
(game / 'd3d12.dll').write_bytes(loader)
result = self.setup(game, plugin)
self.assertNotEqual(result.returncode, 0)
self.assertEqual((game / 'd3d12.dll').read_bytes(), loader)
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), original)
self.assertFalse(list(game.glob('*.d4r-backup.*')))
def test_prefix_config_detected_without_game_local_ini(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
base = Path(tmp)
game = base / 'game'
original = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, original)
umu = base / 'prefix/drive_c/windows/system32/umu'
umu.mkdir(parents=True)
shutil.move(str(plugin.parent), umu / 'plugins')
plugin = umu / 'plugins/d4r.asi'
config = umu / 'OptiScaler.ini'
shutil.move(str(game / 'OptiScaler.ini'), config)
result = self.setup(game, plugin)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn(self.winpath(plugin), config.read_text())
self.assertFalse((game / 'OptiScaler.ini').exists())
self.assertEqual(next(umu.glob('OptiScaler.ini.d4r-backup.*')).read_bytes(), original)
# The real GE-Proton loader writes C: paths and lives in the prefix,
# while the game EXE lives outside that prefix on Z:.
dosdevices = base / 'prefix/dosdevices'
dosdevices.mkdir()
(dosdevices / 'c:').symlink_to('../drive_c')
(umu / 'OptiScaler.dll').touch()
win_plugins = r'C:\windows\system32\umu\plugins'
data = config.read_text().replace(self.winpath(plugin.parent), win_plugins)
config.write_text(data)
checker = plugin.parent / 'd4r/d4r-check.sh'
shutil.copy2(ROOT / 'packaging/d4r-check.sh', checker)
(game / 'winmm.dll').unlink()
result = subprocess.run(['sh', str(checker), str(game)], text=True, capture_output=True)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn('(d4r ASI)', result.stdout)
@unittest.skipUnless((ROOT / 'build/d4r.asi').exists(), 'Build the ASI first')
def test_active_proxy_ini_selected_over_unrelated_game_ini(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
base = Path(tmp)
game = base / 'game'
original = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, original)
shutil.copy2(ROOT / 'build/d4r.asi', plugin)
umu = base / 'prefix/drive_c/windows/system32/umu'
umu.mkdir(parents=True)
config = umu / 'OptiScaler.ini'
config.write_bytes(b'; active proxy config\n[Plugins]\nLoadAsiPlugins=true\n')
proxy_source = base / 'proxy.cpp'
proxy_source.write_text('extern "C" __declspec(dllexport) void DummyOpti() {}\n')
definition = base / 'proxy.def'
definition.write_text('LIBRARY OptiScaler.dll\nEXPORTS\nDummyOpti\n')
proxy = umu / 'winmm.dll'
subprocess.run(['x86_64-w64-mingw32-g++', '-shared', '-static', str(proxy_source),
str(definition), '-o', str(proxy)], check=True, capture_output=True)
source = base / 'active-proxy.cpp'
source.write_text(r'''
#include <windows.h>
int wmain(int argc, wchar_t** argv) {
if (argc != 3 || !LoadLibraryW(argv[1])) return 2;
HMODULE asi = LoadLibraryW(argv[2]);
if (!asi) return 3;
auto init = reinterpret_cast<void (*)()>(GetProcAddress(asi, "InitializeASI"));
if (!init) return 4;
init();
return 0;
}
''')
probe = game / 'active-proxy.exe'
subprocess.run(['x86_64-w64-mingw32-g++', '-static', '-municode', str(source), '-o', str(probe)],
check=True, capture_output=True)
result = subprocess.run(['wine', str(probe), self.winpath(proxy), self.winpath(plugin)],
env=self.env, cwd=self.directory, text=True, capture_output=True, timeout=60)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
status = plugin.parent / 'd4r/installation-status.txt'
deadline = time.monotonic() + 15
while (not status.exists() or 'setup complete' not in status.read_text()) and time.monotonic() < deadline:
time.sleep(0.05)
self.assertTrue(status.exists())
self.assertIn('setup complete', status.read_text())
self.assertIn(self.winpath(plugin), config.read_text())
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), original)
@unittest.skipUnless((ROOT / 'build/d4r.asi').exists(), 'Build the ASI first')
def test_xalia_does_not_start_setup_or_modify_settings(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp)
original = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, original)
shutil.copy2(ROOT / 'build/d4r.asi', plugin)
utility = game / 'xalia.exe'
shutil.copy2(self.probe, utility)
result = subprocess.run(['wine', str(utility), self.winpath(plugin),
self.winpath(plugin.parent / 'd4r')], env=self.env, cwd=self.directory,
text=True, capture_output=True, timeout=30)
# The probe's environment assertions intentionally fail because
# InitializeASI must leave this auxiliary process untouched.
self.assertEqual(result.returncode, 5, result.stdout + result.stderr)
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), original)
self.assertFalse((plugin.parent / 'd4r/installation-status.txt').exists())
self.assertFalse((plugin.parent / 'd4r/d4r_nvngx.log').exists())
@unittest.skipUnless((ROOT / 'build/d4r.asi').exists(), 'Build the ASI first')
def test_installation_waits_for_game_exit(self):
with tempfile.TemporaryDirectory(dir=self.directory) as tmp:
game = Path(tmp)
original = b'[Plugins]\nLoadAsiPlugins=true\n'
plugin = self.fixture(game, original)
shutil.copy2(ROOT / 'build/d4r.asi', plugin)
shutil.copy2(self.probe, game / 'probe.exe')
process = subprocess.Popen(['wine', str(game / 'probe.exe'), self.winpath(plugin),
self.winpath(plugin.parent / 'd4r')],
env=dict(self.env, D4R_ASI_PROBE_PAUSE_MS='5000'), cwd=self.directory,
text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
status = plugin.parent / 'd4r/installation-status.txt'
deadline = time.monotonic() + 15
while not status.exists() and process.poll() is None and time.monotonic() < deadline:
time.sleep(0.05)
self.assertTrue(status.exists())
self.assertIn('Close the game', status.read_text())
self.assertEqual((game / 'd3d12.dll').read_bytes(), b'old d3d12.dll')
self.assertEqual((game / 'OptiScaler.ini').read_bytes(), original)
output, errors = process.communicate(timeout=20)
self.assertEqual(process.returncode, 0, output + errors)
deadline = time.monotonic() + 10
while 'setup complete' not in status.read_text() and time.monotonic() < deadline:
time.sleep(0.05)
self.assertIn('setup complete', status.read_text())
self.assertEqual((game / 'd3d12.dll').read_bytes(), b'new d3d12.dll')
finally:
if process.poll() is None:
process.kill()
process.communicate()
if __name__ == '__main__':
unittest.main()