Fix OptiScaler NGX routing and align marker polling defaults
- Explicitly route OptiScaler to d4r\nvngx.dll in release packages. - Extract INI generation into a helper that preserves line endings and rejects missing or duplicate settings. - Check NGX routing separately from file presence and return failure when the installation is incomplete or misconfigured. - Add an optional OptiScaler patch for external DLSS backends while preserving AMD identity for FSR4, with matching launch guidance. - Align portable and launcher marker polling defaults at 200 us. - Log when d4r's model override replaces game or OptiScaler presets. - Add configuration, package, routing and coexistence regression tests. - Update installation documentation, add FSR/DLSS comparison videos and ignore local .tools artifacts. Refs: #7, #14
This commit is contained in:
1 parent
f66cccaf28
commit
cb08e1c313
17 files changed
+738
-31
No files matched your search
@@ -0,0 +1,80 @@
|
||||
"""CPU regression checks for the upstream OptiScaler coexistence patch."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PATCH = ROOT / 'patches/optiscaler/0001-proton-external-dlss-fsr4-coexistence.patch'
|
||||
|
||||
|
||||
class OptiScalerCoexistenceTests(unittest.TestCase):
|
||||
def test_backend_identity_policy(self):
|
||||
patch = PATCH.read_text()
|
||||
start = patch.index('diff --git a/OptiScaler/spoofing/BackendIdentity.h ')
|
||||
section = patch[start:].split('\ndiff --git ', 1)[0]
|
||||
# Compile the exact added header that an upstream build will consume.
|
||||
header = '\n'.join(line[1:] for line in section.splitlines()
|
||||
if line.startswith('+') and not line.startswith('+++'))
|
||||
code = r'''
|
||||
#include "BackendIdentity.h"
|
||||
#include <cassert>
|
||||
int main() {
|
||||
for (bool nvidia : {false, true})
|
||||
for (bool optedIn : {false, true})
|
||||
for (bool file : {false, true})
|
||||
assert(BackendIdentity::AllowDlss(nvidia, optedIn, file) ==
|
||||
(nvidia || (optedIn && file)));
|
||||
const char* amd[] = {"amdxc64.dll", "AMDXCFFX64.DLL", "amd_fidelityfx_dx12.dll",
|
||||
"amd_fidelityfx_loader_dx12.dll", "amd_fidelityfx_upscaler_dx12.dll",
|
||||
"amd_fidelityfx_framegeneration_dx12.dll", "amd_fidelityfx_denoiser_dx12.dll",
|
||||
"amd_fidelityfx_radiancecache_dx12.dll"};
|
||||
for (auto caller : amd) assert(BackendIdentity::IsAmdFsrCaller(caller));
|
||||
const char* game[] = {"Game.exe", "Game-Win64-Shipping.exe", "nvngx.dll",
|
||||
"nvngx_dlss.dll", "notamdxc64.dll", "amdxc64.dll.exe", "", "AMDXC64"};
|
||||
for (auto caller : game) assert(!BackendIdentity::IsAmdFsrCaller(caller));
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
directory = Path(temporary)
|
||||
(directory / 'BackendIdentity.h').write_text(header)
|
||||
runner = directory / 'identity'
|
||||
subprocess.run(['c++', '-std=c++17', '-Wall', '-Wextra', '-Werror', '-I', temporary,
|
||||
'-x', 'c++', '-', '-o', str(runner)], input=code,
|
||||
text=True, capture_output=True, check=True)
|
||||
subprocess.run([str(runner)], check=True)
|
||||
|
||||
def test_upstream_integration_preserves_hardware_identity(self):
|
||||
additions = '\n'.join(line[1:] for line in PATCH.read_text().splitlines()
|
||||
if line.startswith('+') and not line.startswith('+++'))
|
||||
self.assertEqual(additions.count('BackendIdentity::IsAmdFsrCaller(caller)'), 4)
|
||||
self.assertIn('DLSSAllowExternalBackend { false }', additions)
|
||||
self.assertIn('readBool("DLSS", "AllowExternalBackend")', additions)
|
||||
self.assertIn('ini.SetValue("DLSS", "AllowExternalBackend"', additions)
|
||||
self.assertIn('std::filesystem::is_regular_file(backendPath, error)', additions)
|
||||
self.assertIn('backendPath = Util::ExePath().parent_path() / backendPath', additions)
|
||||
self.assertIn('NvngxPath.set_volatile_value(backendPath.wstring())', additions)
|
||||
self.assertIn('if (State::Instance().isRunningOnNvidia && !Config::Instance()->DxgiSpoofing.has_value())', additions)
|
||||
self.assertNotIn('isRunningOnNvidia = true', additions)
|
||||
self.assertNotIn('isRunningOnRDNA4 = true', additions)
|
||||
|
||||
def test_checker_launch_options_follow_external_backend_config(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
game = Path(temporary)
|
||||
(game / 'd4r').mkdir()
|
||||
for flag in ('true', 'false', 'auto'):
|
||||
(game / 'OptiScaler.ini').write_bytes(
|
||||
(f'[DLSS]\r\nAllowExternalBackend={flag}\r\n'
|
||||
'[Libraries]\r\nNvngxPath=d4r\\nvngx.dll\r\n').encode())
|
||||
result = subprocess.run(['sh', str(ROOT / 'packaging/d4r-check.sh'), temporary],
|
||||
text=True, capture_output=True)
|
||||
if flag == 'true':
|
||||
self.assertIn('WINE_HIDE_AMD_GPU=0 %command%', result.stdout)
|
||||
self.assertNotIn('PROTON_FORCE_NVAPI=1', result.stdout)
|
||||
self.assertNotIn('requires the game\'s working directory', result.stdout)
|
||||
else:
|
||||
self.assertIn('PROTON_FORCE_NVAPI=1', result.stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,228 @@
|
||||
"""Release NGX routing: real INI generation, full/clean ZIP staging, install diagnostics.
|
||||
|
||||
Package tests use disposable runtime files and stand-ins for GPU kernel compilation.
|
||||
Actual OptiScaler/Proton loading additionally needs the D3D12 harness.
|
||||
"""
|
||||
import configparser
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SETTINGS = ROOT / 'packaging/optiscaler.settings'
|
||||
GENERATOR = ROOT / 'scripts/configure_optiscaler.py'
|
||||
|
||||
|
||||
def upstream_ini():
|
||||
sections = {}
|
||||
for line in SETTINGS.read_text().splitlines():
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
path, _ = line.split('=', 1)
|
||||
section, key = path.split('.', 1)
|
||||
sections.setdefault(section, []).append(key)
|
||||
# Keep NvngxPath independent of the settings file so removing the release
|
||||
# override reproduces the original bug rather than removing the fixture key.
|
||||
if 'NvngxPath' not in sections['Libraries']:
|
||||
sections['Libraries'].append('NvngxPath')
|
||||
return '; upstream comment\n' + ''.join(
|
||||
f'[{section}]\n' + ''.join(f'{key}=auto\n' for key in keys)
|
||||
for section, keys in sections.items()) + '[Menu]\nShowFps=true\n'
|
||||
|
||||
|
||||
class OptiScalerConfigTests(unittest.TestCase):
|
||||
def generate(self, directory, source):
|
||||
source_path = directory / 'upstream.ini'
|
||||
output = directory / 'OptiScaler.ini'
|
||||
source_path.write_bytes(source.encode())
|
||||
result = subprocess.run(['python3', str(GENERATOR), str(source_path), str(SETTINGS), str(output)],
|
||||
capture_output=True, text=True)
|
||||
return result, output
|
||||
|
||||
def assert_routing(self, data):
|
||||
ini = configparser.ConfigParser()
|
||||
ini.read_string(data.decode())
|
||||
self.assertEqual(ini['Libraries']['NvngxPath'], r'd4r\nvngx.dll')
|
||||
self.assertEqual(ini['Libraries']['OptiDllPath'], 'd4r')
|
||||
self.assertEqual(ini['Upscalers']['Dx12Upscaler'], 'dlss')
|
||||
self.assertEqual(ini['DLSS']['Enabled'], 'true')
|
||||
self.assertEqual(ini['Hooks']['HookOriginalNvngxOnly'], 'true')
|
||||
self.assertEqual(ini['Menu']['ShowFps'], 'true')
|
||||
|
||||
def test_generated_config_and_line_endings(self):
|
||||
for newline in ('\n', '\r\n'):
|
||||
with self.subTest(newline=repr(newline)), tempfile.TemporaryDirectory() as temporary:
|
||||
source = upstream_ini().replace('\n', newline)
|
||||
result, output = self.generate(Path(temporary), source)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
data = output.read_bytes()
|
||||
self.assert_routing(data)
|
||||
self.assertTrue(data.startswith(b'; upstream comment' + newline.encode()))
|
||||
self.assertEqual(data.count(b'\r\n'), data.count(b'\n') if newline == '\r\n' else 0)
|
||||
self.assertEqual(source.count(newline), data.count(newline.encode()))
|
||||
|
||||
def test_missing_or_duplicate_override_fails_without_output(self):
|
||||
for replacement in ('', 'NvngxPath=auto\nnvngxpath=d4r\\ngx\\_nvngx.dll\n'):
|
||||
with self.subTest(replacement=replacement), tempfile.TemporaryDirectory() as temporary:
|
||||
result, output = self.generate(Path(temporary), upstream_ini().replace('NvngxPath=auto\n', replacement))
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn('exactly one NvngxPath', result.stderr)
|
||||
self.assertFalse(output.exists())
|
||||
|
||||
def test_missing_or_duplicate_libraries_section_fails(self):
|
||||
for source in (upstream_ini().replace('[Libraries]', '[Other]'),
|
||||
upstream_ini() + '[libraries]\nNvngxPath=auto\n'):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
result, output = self.generate(Path(temporary), source)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn('exactly one [Libraries]', result.stderr)
|
||||
self.assertFalse(output.exists())
|
||||
|
||||
def test_full_and_clean_release_archives(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
repo = Path(temporary) / 'repo'
|
||||
inputs = Path(temporary) / 'inputs'
|
||||
inputs.mkdir()
|
||||
(repo / 'scripts').mkdir(parents=True)
|
||||
for name in ('package_release.sh', 'configure_optiscaler.py', 'check_glibc_compat.sh'):
|
||||
shutil.copy2(ROOT / 'scripts' / name, repo / 'scripts' / name)
|
||||
shutil.copytree(ROOT / 'packaging', repo / 'packaging')
|
||||
for name in ('LICENSE', 'NOTICE'):
|
||||
shutil.copy2(ROOT / name, repo / name)
|
||||
for component in ('zluda', 'vkd3d-proton'):
|
||||
(repo / 'patches' / component).mkdir(parents=True)
|
||||
(repo / 'patches' / component / 'fixture.patch').write_text('fixture\n')
|
||||
# All shell staging, INI generation, ABI checks and archiving are real;
|
||||
# replace only expensive GPU builds/manifests with fixture artifacts.
|
||||
(repo / 'kernels/tools').mkdir(parents=True)
|
||||
kernel_build = repo / 'kernels/build.sh'
|
||||
kernel_build.write_text('#!/bin/sh\nmkdir -p "$2"\nprintf fixture > "$2/layer.hsaco"\n')
|
||||
kernel_build.chmod(0o755)
|
||||
(repo / 'kernels/tools/kernel_manifest.py').write_text(
|
||||
'import pathlib, sys\npathlib.Path(sys.argv[1], "d4r-kernels.txt").write_text("layer 1234\\n")\n')
|
||||
|
||||
def fixture(path, data=b'fixture'):
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(data)
|
||||
return path
|
||||
|
||||
# A real minimal ELF lets the package's existing GLIBC checker run.
|
||||
elf = inputs / 'library.so'
|
||||
subprocess.run(['cc', '-shared', '-x', 'c', '-o', str(elf), '-'],
|
||||
input='int fixture(void) { return 0; }\n', text=True, check=True)
|
||||
fixture(repo / 'build/d4r_nvngx.dll')
|
||||
fixture(repo / 'build/wine-nvcuda/x86_64-unix/nvcuda.dll.so', elf.read_bytes())
|
||||
fixture(inputs / 'zluda/libnvcuda.so', elf.read_bytes())
|
||||
fixture(inputs / 'rocm/lib/libamdhip64.so.7', elf.read_bytes())
|
||||
fixture(inputs / 'rocm/licenses/runtime.txt')
|
||||
for name in ('d3d12.dll', 'd3d12core.dll'):
|
||||
fixture(inputs / 'vkd3d' / name)
|
||||
fixture(inputs / 'opti/OptiScaler.dll')
|
||||
fixture(inputs / 'opti/OptiScaler.ini', upstream_ini().replace('\n', '\r\n').encode())
|
||||
dlss = fixture(inputs / 'nvngx_dlss.dll')
|
||||
core = fixture(inputs / '_nvngx.dll')
|
||||
license_path = fixture(inputs / 'license.txt')
|
||||
for name in ('LICENSE-APACHE', 'LICENSE-MIT', 'ext/llvm-project/llvm/LICENSE.TXT', 'LICENSE', 'COPYING'):
|
||||
fixture(inputs / 'source' / name)
|
||||
# Prebuilt L texture names keep the full flow from calling an emitter.
|
||||
names = ['rrlite_dec0_4x4']
|
||||
for mv in ('mvhi', 'mvlo'):
|
||||
for dynamic_range in ('hdr', 'ldr'):
|
||||
names.append(f'rrlite_enc0_4x4_{mv}_{dynamic_range}')
|
||||
names.extend(f'rrlite_post_{v}_{mv}_{dynamic_range}' for v in ('3_1', '3_2'))
|
||||
for prefix in ('gfx1101', 'accuracy/gfx1101'):
|
||||
for name in names:
|
||||
fixture(inputs / 'tex' / prefix / (name + '.hsaco'))
|
||||
fixture(inputs / 'tex' / prefix / 'd4r-accuracy.txt', b'1\n')
|
||||
env = {k: v for k, v in os.environ.items() if not k.startswith('D4R_')}
|
||||
env.update(D4R_SKIP_BUILD='1', D4R_GPU_ARCHS='gfx1101', SOURCE_DATE_EPOCH='1700000000',
|
||||
D4R_OPTISCALER=str(inputs / 'opti'), D4R_DLSS_DLLS=str(dlss),
|
||||
D4R_ZLUDA_DIR=str(inputs / 'zluda'), D4R_VKD3D_DIR=str(inputs / 'vkd3d'),
|
||||
D4R_ROCM_RUNTIME=str(inputs / 'rocm'), D4R_OPTISCALER_LICENSE=str(license_path),
|
||||
D4R_ZLUDA_SRC=str(inputs / 'source'), D4R_VKD3D_SRC=str(inputs / 'source'),
|
||||
D4R_BUNDLE_DLSS=str(dlss), D4R_BUNDLE_NGX=str(core), D4R_BUNDLE_TEX=str(inputs / 'tex'))
|
||||
version = (repo / 'packaging/VERSION').read_text().strip()
|
||||
configs = []
|
||||
for bundled in ('1', '0'):
|
||||
with self.subTest(bundled=bundled):
|
||||
env['D4R_BUNDLE_NVIDIA'] = bundled
|
||||
result = subprocess.run(['bash', str(repo / 'scripts/package_release.sh'), str(repo / 'dist')],
|
||||
env=env, text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
name = f'd4r-{version}' + ('-nonvidia' if bundled == '0' else '')
|
||||
with zipfile.ZipFile(repo / 'dist' / (name + '.zip')) as archive:
|
||||
self.assertIsNone(archive.testzip())
|
||||
data = archive.read('OptiScaler.ini')
|
||||
self.assert_routing(data)
|
||||
configs.append(data)
|
||||
self.assertEqual(data, (repo / 'dist' / name / 'OptiScaler.ini').read_bytes())
|
||||
self.assertIn('d4r/nvngx.dll', archive.namelist())
|
||||
self.assertNotIn('nvngx.dll', archive.namelist())
|
||||
self.assertNotIn('d4r/_nvngx.dll', archive.namelist())
|
||||
self.assertEqual('d4r/ngx/_nvngx.dll' in archive.namelist(), bundled == '1')
|
||||
self.assertEqual('d4r/nvngx_dlss.dll' in archive.namelist(), bundled == '1')
|
||||
self.assertEqual(archive.read('d4r/d4r-check.sh'), (ROOT / 'packaging/d4r-check.sh').read_bytes())
|
||||
self.assertEqual(configs[0], configs[1])
|
||||
|
||||
|
||||
class InstallCheckTests(unittest.TestCase):
|
||||
def test_routing_separate_from_file_presence(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
game = Path(temporary) / 'game with spaces'
|
||||
for name in ('Game.exe', 'dxgi.dll', 'd3d12.dll', 'd3d12core.dll', 'd4r/nvngx.dll',
|
||||
'd4r/nvcuda.dll', 'd4r/zluda/libcuda.so', 'd4r/d4r.ini', 'd4r/nvngx_dlss.dll',
|
||||
'd4r/ngx/_nvngx.dll', 'd4r/rocm/lib/libamdhip64.so.7'):
|
||||
path = game / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.touch()
|
||||
for value, good in ((r'd4r\nvngx.dll', True), ('./d4r/nvngx.dll', True),
|
||||
('Z:' + str(game / 'd4r/nvngx.dll').replace('/', '\\'), True),
|
||||
('auto', False), ('d4r', False), (r'd4r\ngx\_nvngx.dll', False),
|
||||
(r'C:\windows\system32\_nvngx.dll', False), ('', False)):
|
||||
with self.subTest(value=value):
|
||||
setting = f'NvngxPath={value}\n' if value else ''
|
||||
(game / 'OptiScaler.ini').write_bytes(
|
||||
('[Libraries]\nOptiDllPath=d4r\n' + setting).replace('\n', '\r\n').encode())
|
||||
result = subprocess.run(['sh', str(ROOT / 'packaging/d4r-check.sh'), str(game)],
|
||||
text=True, capture_output=True)
|
||||
self.assertIn('ok d4r/nvngx.dll', result.stdout)
|
||||
if good:
|
||||
self.assertIn('OptiScaler NGX routing: NvngxPath=', result.stdout)
|
||||
self.assertNotIn('CONFIG', result.stdout)
|
||||
else:
|
||||
self.assertIn('CONFIG', result.stdout)
|
||||
self.assertIn('OptiDllPath alone can load system _nvngx.dll', result.stdout)
|
||||
self.assertNotIn('checks passed', result.stdout)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
|
||||
def test_section_and_duplicate_keys(self):
|
||||
for ini in ('[Other]\nNvngxPath=d4r\\nvngx.dll\n[Libraries]\nOptiDllPath=d4r\n',
|
||||
'[Libraries]\nNvngxPath=d4r\\nvngx.dll\nnvngxpath=auto\n'):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
game = Path(temporary)
|
||||
(game / 'd4r').mkdir()
|
||||
(game / 'OptiScaler.ini').write_text(ini)
|
||||
result = subprocess.run(['sh', str(ROOT / 'packaging/d4r-check.sh'), temporary],
|
||||
capture_output=True, text=True)
|
||||
self.assertIn('CONFIG', result.stdout)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
|
||||
def test_correct_routing_does_not_hide_missing_shim(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
game = Path(temporary)
|
||||
(game / 'd4r').mkdir()
|
||||
(game / 'OptiScaler.ini').write_text('[Libraries]\nNvngxPath=d4r\\nvngx.dll\n')
|
||||
result = subprocess.run(['sh', str(ROOT / 'packaging/d4r-check.sh'), temporary],
|
||||
capture_output=True, text=True)
|
||||
self.assertIn('MISSING d4r/nvngx.dll', result.stdout)
|
||||
self.assertIn('OptiScaler NGX routing: NvngxPath=', result.stdout)
|
||||
self.assertNotIn('checks passed', result.stdout)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,92 @@
|
||||
"""Marker polling defaults and the shim's model-override diagnostics."""
|
||||
import configparser
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SHIM = ROOT / 'tools/d4r_nvngx_shim.cpp'
|
||||
|
||||
|
||||
class ShimConfigTests(unittest.TestCase):
|
||||
def test_marker_poll_defaults_agree(self):
|
||||
for name in ('packaging/d4r.ini', 'config/d4r.ini.default'):
|
||||
ini = configparser.ConfigParser()
|
||||
ini.read(ROOT / name)
|
||||
self.assertEqual(ini.getint('Interop', 'MarkerPollUs'), 200, name)
|
||||
source = SHIM.read_text()
|
||||
portable = re.search(r'portable_set\("D4R_SHIM_MARKER_POLL_US", poll.empty\(\) \? "(\d+)" : poll\);', source)
|
||||
runtime = re.search(r'env_uint\("D4R_SHIM_MARKER_POLL_US", (\d+)\)', source)
|
||||
self.assertIsNotNone(portable, 'portable marker polling fallback missing')
|
||||
self.assertIsNotNone(runtime, 'runtime marker polling fallback missing')
|
||||
self.assertEqual(int(portable[1]), 200)
|
||||
self.assertEqual(int(runtime[1]), 200)
|
||||
|
||||
def test_launcher_marker_poll_defaults_overrides_and_validation(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
ini = Path(temporary) / 'd4r.ini'
|
||||
env = {key: value for key, value in os.environ.items() if not key.startswith('D4R_')}
|
||||
cmd = ['python3', str(ROOT / 'scripts/d4r_config.py'), '--config', str(ini)]
|
||||
for value, expected in ((None, '200'), ('auto', '200'), ('200', '200'), ('0', '0'), ('75', '75')):
|
||||
ini.write_text('[Interop]\n' + (f'MarkerPollUs = {value}\n' if value is not None else ''))
|
||||
result = subprocess.run(cmd, env=env, text=True, capture_output=True, check=True)
|
||||
self.assertIn(f'export D4R_SHIM_MARKER_POLL_US={expected}\n', result.stdout)
|
||||
# An explicit environment variable keeps its existing precedence.
|
||||
result = subprocess.run(cmd, env=dict(env, D4R_SHIM_MARKER_POLL_US='35'),
|
||||
text=True, capture_output=True, check=True)
|
||||
self.assertNotIn('export D4R_SHIM_MARKER_POLL_US=', result.stdout)
|
||||
for value in ('-1', '0.5', 'invalid'):
|
||||
ini.write_text(f'[Interop]\nMarkerPollUs = {value}\n')
|
||||
result = subprocess.run(cmd, env=env, text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 2)
|
||||
self.assertIn('MarkerPollUs must be a number of microseconds', result.stderr)
|
||||
|
||||
def test_production_model_override_logs_and_presets(self):
|
||||
# Compile the actual feature-creation preset block with fake NGX inputs
|
||||
# and a captured logger, so the test exercises the production message
|
||||
# and override behavior without requiring Windows, NGX or a GPU.
|
||||
source = SHIM.read_text()
|
||||
start = source.index(' const char* presetNames[] =')
|
||||
end = source.index(' feature->preset = presets[1];', start)
|
||||
block = source[start:end]
|
||||
runner_source = r'''
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
static unsigned nextPreset = 21;
|
||||
unsigned get_uint_or(void*, const char*, unsigned) { return nextPreset++; }
|
||||
std::string env_string(const char* name) { const char* value = getenv(name); return value ? value : ""; }
|
||||
void logf(const char* format, ...) {
|
||||
va_list args; va_start(args, format); vprintf(format, args); va_end(args); puts("");
|
||||
}
|
||||
int main() {
|
||||
void* parameters = nullptr;
|
||||
''' + block + r'''
|
||||
printf("presets:"); for (unsigned preset : presets) printf(" %u", preset); puts("");
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
runner = Path(temporary) / 'model-override'
|
||||
subprocess.run(['c++', '-std=c++17', '-Wall', '-Wextra', '-Werror', '-x', 'c++', '-', '-o', str(runner)],
|
||||
input=runner_source, text=True, capture_output=True, check=True)
|
||||
env = {key: value for key, value in os.environ.items() if key != 'D4R_DLSS_PRESET'}
|
||||
for preset, model in ((5, 'E'), (11, 'K'), (12, 'L'), (13, 'M')):
|
||||
with self.subTest(model=model):
|
||||
output = subprocess.check_output([str(runner)], env=dict(env, D4R_DLSS_PRESET=str(preset)), text=True)
|
||||
self.assertIn(f'D4R_DLSS_PRESET={preset} (model {model}) overrides game/OptiScaler presets', output)
|
||||
self.assertIn('[DLAA=21 Quality=22 Balanced=23 Performance=24 UltraPerformance=25 UltraQuality=26]', output)
|
||||
self.assertIn(f'using preset={preset} for all quality modes', output)
|
||||
self.assertIn('presets:' + f' {preset}' * 6, output)
|
||||
for value in (None, ''):
|
||||
current = env if value is None else dict(env, D4R_DLSS_PRESET=value)
|
||||
output = subprocess.check_output([str(runner)], env=current, text=True)
|
||||
self.assertNotIn('model override', output)
|
||||
self.assertEqual(output.strip(), 'presets: 21 22 23 24 25 26')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user