Files
d4r/tests/test_shim_config.py
T
ayo 162bbd77c8 shim: adaptive marker polling, opt-in GPU-side input wait/completion and RGBA8 output
- [Interop] MarkerPollUs = auto is the new default: the input-marker wait learns when
  each feature's inputs become ready, sleeps in <=200 us chunks, yields near the
  prediction and backs off when late (d4r_input_wait.h); a number keeps fixed pacing
  (0 yields). d4r_config.py no longer exports 200 when the key is unset

- D4R_SHIM_GPU_INPUT_WAIT=1 (experimental, split VRAM, age 0): NGX's null stream
  waits on a coherent Vulkan word imported into HIP (d4rStreamWaitValue32Eq) instead
  of CPU polling. The original readback marker stays the authority; a CPU watchdog
  releases discarded lists and timeouts. Denoiser, verify, capture and dilation
  frames keep the CPU wait

- D4R_SHIM_GPU_COMPLETION=1 (needs GPU_INPUT_WAIT): the bridge writes a completion
  word right after the native output kernel (d4rNgxWriteValue32), and the game's queue
  polls it with a bounded compute wait (kernels/support/vulkan_wait.comp), replacing
  the split list's CPU timeline signal. frame_retired writes the same word on every
  failure path

- D4R_SHIM_NATIVE_COMPLETION=1: a null-stream callback in the bridge signals the
  split timeline (d4rNgxTimeline*); D4rFrameCompletion::can_signal only releases the
  oldest outstanding frame

- RGBA8 output: tex_common.h adds an 'R4' redirect tag, so K's native output kernel
  stores packed RGBA8 directly (D4R_SHIM_OUTPUT_RGBA8=1, checked against the kernel
  manifest by d4rOutputKernelRgba8). Host packing handles RGBA8 too.
  patches/vkd3d-proton/0004 (VKD3D_D4R_LINEAR_RGBA8=WxH) makes such outputs linear and
  exportable so the result can be written in place

- D4R_SHIM_FUSE_RGBA8_INPUT=1: one Vulkan pass converts and copies RGBA8 color input
  (vulkan_read.comp mode 5 matches the blit's toward-zero halves, plus a row stride)

- profiling: thread CPU time from the Unix bridge (Wine's GetThreadTimes ticks at
  10 ms); game-queue D3D12 timestamps (vk_input/vk_dlss_wait/vk_output/vk_total);
  gpu_eval split into input_gpu_wait and dlss_gpu; D4R_PROFILE_GPU_EVENTS,
  _TIMESTAMPS, _LOG and _CPU_TIME=0 switch off individual parts for bisection

- bridge: D4R_NGX_STREAM_PRIORITY=high|normal runs NGX on a priority stream
  (off by default; no gain measured)

- release_feature: a feature already marked retiring by a failed drain can still
  be released normally

- harness: RGBA8 with motion and jitter scenes (re-quantized each frame),
  D4R_HARNESS_RGBA8_RAMP, D4R_HARNESS_THRASH_MB to evict L2/Infinity Cache before
  each evaluation

- kernels/native: in_hoist.py and split_output_paths.py (opt-in scheduling
  experiments); scripts/build_k_ldr_output.sh builds K's LDR output with an
  optional D4R_LDR_OUTPUT_MAX_VGPR cap

- tests: test_input_wait.py and test_latency_optimizations.py; MarkerPollUs and
  frame-completion tests updated
2026-10-13 22:30:41 -04:00

130 lines
6.1 KiB
Python

"""Engine opt-in, preset-selection behavior and polling configuration precedence."""
import configparser
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class ShimConfigTests(unittest.TestCase):
def test_engine_is_opt_in_and_configured_by_ini(self):
for name in ('packaging/d4r.ini', 'config/d4r.ini.default'):
ini = configparser.ConfigParser(inline_comment_prefixes=(';',))
ini.read(ROOT / name)
self.assertFalse(ini.getboolean('Engine', 'Enabled'), name)
self.assertEqual(ini.get('Engine', 'ModelDir'), 'auto', name)
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 text, enabled, model in (('', '0', None), ('[Engine]\nEnabled = true\n', '1', None),
('[Engine]\nEnabled = true\nModelDir = /models/k\n', '1', '/models/k')):
ini.write_text(text)
result = subprocess.run(cmd, env=env, text=True, capture_output=True, check=True)
self.assertIn(f'export D4R_ENGINE={enabled}\n', result.stdout)
self.assertEqual('export D4R_ENGINE_MODEL_DIR=/models/k\n' in result.stdout, model is not None)
ini.write_text('[Engine]\nEnabled = maybe\n')
result = subprocess.run(cmd, env=env, text=True, capture_output=True)
self.assertEqual(result.returncode, 2)
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, None), ('auto', None), ('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)
if expected is None:
self.assertNotIn('export D4R_SHIM_MARKER_POLL_US=', result.stdout)
else:
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)
class RenderPresetPolicyTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.directory = tempfile.TemporaryDirectory()
cls.addClassCleanup(cls.directory.cleanup)
cls.runner = Path(cls.directory.name) / 'render-presets'
source = r'''
#include "d4r_render_presets.h"
#include <cstdio>
int main(int argc, char** argv) {
if (argc != 2) return 2;
const bool rr = argv[1][0] == '1';
unsigned int presets[6] = {21, 22, 23, 24, 25, 26};
if (rr) {
for (unsigned int& preset : presets) preset += 10;
}
const auto overrideValue = d4r_parse_render_preset_override(
std::getenv(d4r_render_preset_variable(rr)));
overrideValue.apply(presets);
for (unsigned int preset : presets) std::printf("%u ", preset);
std::puts("");
}
'''
subprocess.run(
['c++', '-std=c++17', '-Wall', '-Wextra', '-Werror',
'-I', str(ROOT / 'tools'), '-x', 'c++', '-', '-o', str(cls.runner)],
input=source, text=True, capture_output=True, check=True)
def select(self, rr=False, **overrides):
env = dict(os.environ)
for name in ('D4R_DLSS_PRESET', 'D4R_RR_PRESET'):
env.pop(name, None)
env.update(overrides)
result = subprocess.run(
[str(self.runner), '1' if rr else '0'], env=env,
text=True, capture_output=True, check=True, timeout=5)
return [int(value) for value in result.stdout.split()]
def test_sr_models_override_every_quality_mode(self):
for value in (5, 11, 12, 13):
with self.subTest(preset=value):
self.assertEqual(
self.select(D4R_DLSS_PRESET=str(value), D4R_RR_PRESET='4'),
[value] * 6)
def test_rr_models_do_not_use_the_sr_override(self):
for value in (4, 5):
with self.subTest(preset=value):
self.assertEqual(
self.select(rr=True, D4R_RR_PRESET=str(value), D4R_DLSS_PRESET='13'),
[value] * 6)
def test_unset_override_preserves_quality_specific_game_presets(self):
for rr, foreign in ((False, 'D4R_RR_PRESET'), (True, 'D4R_DLSS_PRESET')):
with self.subTest(rr=rr):
expected = list(range(31, 37) if rr else range(21, 27))
self.assertEqual(self.select(rr=rr), expected)
self.assertEqual(self.select(rr=rr, **{foreign: '13'}), expected)
def test_empty_override_does_not_fall_back_to_the_other_feature(self):
self.assertEqual(
self.select(D4R_DLSS_PRESET='', D4R_RR_PRESET='5'),
list(range(21, 27)))
self.assertEqual(
self.select(rr=True, D4R_RR_PRESET='', D4R_DLSS_PRESET='13'),
list(range(31, 37)))
def test_explicit_default_preset_overrides_game_presets(self):
self.assertEqual(self.select(D4R_DLSS_PRESET='0'), [0] * 6)
self.assertEqual(self.select(rr=True, D4R_RR_PRESET='0'), [0] * 6)
if __name__ == '__main__':
unittest.main()