proton: Copy DLLs provided by the NVIDIA driver into prefix

The upcoming NVIDIA 470 driver series will introduce a DLL (nvngx.dll)
for the support of NVIDIA DLSS in Proton. This change adds logic for
discovering the location of DLL files provided by the NVIDIA driver, and
copies them to C:\Windows\System32\

Reviewed-by: Adam Moss <amoss@nvidia.com>
This commit is contained in:
Liam Middlebrook 2021-05-10 21:53:15 -07:00 committed by Andrew Eikum
parent 1630811474
commit 0afcb19416

107
proton
View file

@ -15,6 +15,15 @@ import subprocess
import sys import sys
import tarfile import tarfile
from ctypes import CDLL
from ctypes import POINTER
from ctypes import Structure
from ctypes import addressof
from ctypes import cast
from ctypes import c_int
from ctypes import c_char_p
from ctypes import c_void_p
from filelock import FileLock from filelock import FileLock
from random import randrange from random import randrange
@ -99,7 +108,7 @@ def merge_user_dir(src, dst):
else: else:
extant_dirs += dst_dir extant_dirs += dst_dir
def try_copy(src, dst, add_write_perm=True, copy_metadata=True): def try_copy(src, dst, add_write_perm=True, copy_metadata=True, optional=False):
try: try:
if os.path.isdir(dst): if os.path.isdir(dst):
dstfile = dst + "/" + os.path.basename(src) dstfile = dst + "/" + os.path.basename(src)
@ -119,6 +128,12 @@ def try_copy(src, dst, add_write_perm=True, copy_metadata=True):
new_mode = os.lstat(dstfile).st_mode | stat.S_IWUSR | stat.S_IWGRP new_mode = os.lstat(dstfile).st_mode | stat.S_IWUSR | stat.S_IWGRP
os.chmod(dstfile, new_mode) os.chmod(dstfile, new_mode)
except FileNotFoundError as e:
if optional:
log('Error while copying to \"' + dst + '\": ' + e.strerror)
else:
raise
except PermissionError as e: except PermissionError as e:
if e.errno == errno.EPERM: if e.errno == errno.EPERM:
#be forgiving about permissions errors; if it's a real problem, things will explode later anyway #be forgiving about permissions errors; if it's a real problem, things will explode later anyway
@ -162,6 +177,87 @@ def try_get_game_library_dir():
return None return None
# Function to find the installed location of DLL files for use by Wine/Proton
# from the NVIDIA Linux driver
#
# See https://gitlab.steamos.cloud/steamrt/steam-runtime-tools/-/issues/71 for
# background on the chosen method of DLL discovery.
#
# On success, returns a str() of the absolute-path to the directory at which DLL
# files are stored
#
# On failure, returns None
def find_nvidia_wine_dll_dir():
try:
libdl = CDLL("libdl.so.2")
except (OSError):
return None
try:
libglx_nvidia = CDLL("libGLX_nvidia.so.0")
except OSError:
return None
# from dlinfo(3)
#
# struct link_map {
# ElfW(Addr) l_addr; /* Difference between the
# address in the ELF file and
# the address in memory */
# char *l_name; /* Absolute pathname where
# object was found */
# ElfW(Dyn) *l_ld; /* Dynamic section of the
# shared object */
# struct link_map *l_next, *l_prev;
# /* Chain of loaded objects */
#
# /* Plus additional fields private to the
# implementation */
# };
RTLD_DI_LINKMAP = 2
class link_map(Structure):
_fields_ = [("l_addr", c_void_p), ("l_name", c_char_p), ("l_ld", c_void_p)]
# from dlinfo(3)
#
# int dlinfo (void *restrict handle, int request, void *restrict info)
dlinfo_func = libdl.dlinfo
dlinfo_func.argtypes = c_void_p, c_int, c_void_p
dlinfo_func.restype = c_int
# Allocate a link_map object
glx_nvidia_info_ptr = POINTER(link_map)()
# Run dlinfo(3) on the handle to libGLX_nvidia.so.0, storing results at the
# address represented by glx_nvidia_info_ptr
if dlinfo_func(libglx_nvidia._handle,
RTLD_DI_LINKMAP,
addressof(glx_nvidia_info_ptr)) != 0:
return None
# Grab the contents our of our pointer
glx_nvidia_info = cast(glx_nvidia_info_ptr, POINTER(link_map)).contents
# Decode the path to our library to a str()
if glx_nvidia_info.l_name is None:
return None
try:
libglx_nvidia_path = os.fsdecode(glx_nvidia_info.l_name)
except UnicodeDecodeError:
return None
# Follow any symlinks to the actual file
libglx_nvidia_realpath = os.path.realpath(libglx_nvidia_path)
# Go to the relative path ./nvidia/wine from our library
nvidia_wine_dir = os.path.join(os.path.dirname(libglx_nvidia_realpath), "nvidia", "wine")
# Check that nvngx.dll exists here, or fail
if os.path.exists(os.path.join(nvidia_wine_dir, "nvngx.dll")):
return nvidia_wine_dir
return None
EXT2_IOC_GETFLAGS = 0x80086601 EXT2_IOC_GETFLAGS = 0x80086601
EXT2_IOC_SETFLAGS = 0x40086602 EXT2_IOC_SETFLAGS = 0x40086602
@ -705,6 +801,15 @@ class CompatData:
if os.path.exists(nvapi32_dll): if os.path.exists(nvapi32_dll):
os.unlink(nvapi32_dll) os.unlink(nvapi32_dll)
# Try to detect known DLLs that ship with the NVIDIA Linux Driver
# and add them into the prefix
nvidia_wine_dll_dir = find_nvidia_wine_dll_dir()
if nvidia_wine_dll_dir:
for dll in ["_nvngx.dll", "nvngx.dll"]:
try_copy(nvidia_wine_dll_dir + "/" + dll,
self.prefix_dir + "drive_c/windows/system32/" + dll,
optional=True)
try_copy(g_proton.lib64_dir + "wine/vkd3d-proton/d3d12.dll", try_copy(g_proton.lib64_dir + "wine/vkd3d-proton/d3d12.dll",
self.prefix_dir + "drive_c/windows/system32/d3d12.dll") self.prefix_dir + "drive_c/windows/system32/d3d12.dll")
try_copy(g_proton.lib_dir + "wine/vkd3d-proton/d3d12.dll", try_copy(g_proton.lib_dir + "wine/vkd3d-proton/d3d12.dll",