Add experimental native Vulkan DLSS frontend
Implement NGX's Vulkan API over the existing CUDA backend. Compute shaders pack the game's sampled images into buffers shared with CUDA (device-local memory exported through the Wine bridge), and a device event in the game's command buffer holds its queue until the result is written back, keeping frame age at zero. DLSS reads inputs through pitch-linear textures and its native output kernel writes the shared buffer directly; input and completion waits are adaptive. Make it work across engines: shared buffers are concurrent over every compute-capable queue family (Indiana Jones records DLSS on async compute), depth kept in an R32F colour image is accepted under a depth aspect, and the extension-requirement query allows a null list. Spare buffer sets are imported on the first frame and never while a frame is pending, since an import can block on GPU work that is waiting for us; host staging covers any shortfall. Fix two exit hazards. A game quitting without NGX shutdown left the GPU waiting on an event that only a killed thread would set, resetting the ring 10 s later: the bridge now runs a shim hook before teardown and skips all CUDA destroy/sync calls once Wine is shutting the process down. The shim's own entry point skips CRT static destructors at process termination, where locks held by killed threads made the exit spin forever. Add a Vulkan DLSS harness (dynamic, recreate, discard, invalid, exit, compute-queue and in-flight modes), staging/depth probes, ABI/config tests, and an OptiScaler settings overlay for Vulkan games.
This commit is contained in:
1 parent
18c694eecb
commit
694a7a0126
24 files changed
+2215
-20
No files matched your search
@@ -0,0 +1,49 @@
|
||||
"""Lock down the x64 NGX Vulkan resource ABI used by game-owned descriptors."""
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class VulkanAbiTests(unittest.TestCase):
|
||||
def test_x64_resource_layout_on_host_and_windows(self):
|
||||
code = r'''
|
||||
#include <cstddef>
|
||||
#include "d4r_vulkan_ngx.h"
|
||||
static_assert(sizeof(void*) == 8);
|
||||
// NVIDIA nvsdk_ngx_defs_vk.h x64 ABI: union + enum + bool.
|
||||
static_assert(sizeof(NgxVkImage) == 48);
|
||||
static_assert(offsetof(NgxVkImage, view) == 0);
|
||||
static_assert(offsetof(NgxVkImage, image) == 8);
|
||||
static_assert(offsetof(NgxVkImage, range) == 16);
|
||||
static_assert(offsetof(NgxVkImage, format) == 36);
|
||||
static_assert(offsetof(NgxVkImage, width) == 40);
|
||||
static_assert(offsetof(NgxVkImage, height) == 44);
|
||||
static_assert(sizeof(NgxVkResource) == 56);
|
||||
static_assert(offsetof(NgxVkResource, resource) == 0);
|
||||
static_assert(offsetof(NgxVkResource, type) == 48);
|
||||
static_assert(offsetof(NgxVkResource, readWrite) == 52);
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
directory = Path(temporary)
|
||||
source = directory / 'abi.cpp'
|
||||
source.write_text(code)
|
||||
include = directory / 'include'
|
||||
include.mkdir()
|
||||
(include / 'vulkan').symlink_to('/usr/include/vulkan', target_is_directory=True)
|
||||
(include / 'vk_video').symlink_to('/usr/include/vk_video', target_is_directory=True)
|
||||
for compiler in ('c++', 'x86_64-w64-mingw32-g++'):
|
||||
with self.subTest(compiler=compiler):
|
||||
if not shutil.which(compiler):
|
||||
continue
|
||||
subprocess.run([compiler, '-std=c++20', '-Wall', '-Wextra', '-Werror',
|
||||
'-I', str(ROOT / 'tools'), '-I', str(include),
|
||||
'-c', str(source), '-o', str(directory / 'abi.o')],
|
||||
check=True, capture_output=True, text=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,48 @@
|
||||
"""A native Vulkan install must leave d4r's CUDA core load outside OptiScaler's hook."""
|
||||
from pathlib import Path
|
||||
import configparser
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SETTINGS = ROOT / 'packaging/optiscaler-vulkan.settings'
|
||||
|
||||
|
||||
class VulkanConfigTests(unittest.TestCase):
|
||||
def test_migrate_broad_hook_preserving_game_specific_choices(self):
|
||||
sections = {}
|
||||
for line in SETTINGS.read_text().splitlines():
|
||||
if line and not line.startswith('#'):
|
||||
name, value = line.split('=', 1)
|
||||
section, key = name.split('.', 1)
|
||||
sections.setdefault(section, {})[key] = 'auto'
|
||||
sections['Hooks']['HookOriginalNvngxOnly'] = 'false'
|
||||
sections['Spoofing'] = {'Vulkan': 'false', 'VulkanExtensionSpoofing': 'false'}
|
||||
sections['Plugins'] = {'Path': r'd4r\plugins', 'LoadAsiPlugins': 'true'}
|
||||
source = '; existing game setup\r\n' + ''.join(
|
||||
f'[{name}]\r\n' + ''.join(f'{key}={value}\r\n' for key, value in values.items())
|
||||
for name, values in sections.items())
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
folder = Path(temporary)
|
||||
incoming, outgoing = folder / 'old.ini', folder / 'new.ini'
|
||||
incoming.write_bytes(source.encode())
|
||||
subprocess.run(['python3', str(ROOT / 'scripts/configure_optiscaler.py'),
|
||||
str(incoming), str(SETTINGS), str(outgoing)], check=True)
|
||||
data = outgoing.read_bytes()
|
||||
cfg = configparser.ConfigParser(interpolation=None)
|
||||
cfg.read_string(data.decode())
|
||||
self.assertEqual(cfg['Hooks']['HookOriginalNvngxOnly'], 'true')
|
||||
self.assertEqual(cfg['Libraries']['NvngxPath'], r'd4r\nvngx.dll')
|
||||
self.assertEqual(cfg['Upscalers']['VulkanUpscaler'], 'dlss')
|
||||
self.assertEqual(cfg['FrameGen']['Enabled'], 'false')
|
||||
self.assertEqual(cfg['Spoofing']['Vulkan'], 'false')
|
||||
self.assertEqual(cfg['Spoofing']['VulkanExtensionSpoofing'], 'false')
|
||||
self.assertEqual(cfg['Plugins']['Path'], r'd4r\plugins')
|
||||
self.assertEqual(cfg['Plugins']['LoadAsiPlugins'], 'true')
|
||||
self.assertEqual(data.count(b'\r\n'), data.count(b'\n'))
|
||||
self.assertTrue(data.startswith(b'; existing game setup\r\n'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user