NVIDIA RTX Remix Bridge
NVIDIA RTX Remix 项目允许将高质量的路径追踪渲染、照明、阴影等引入经典游戏。该仓库包含 NVIDIA RTX Remix Bridge 客户端和服务器组件,这些组件是允许 32 位游戏与 64 位 Remix Runtime dll 交互所必需的。
注意: 要体验 NVIDIA RTX Remix 的完整功能集,需要将从此仓库编译的二进制文件与 GitHub 上
dxvk-remix
仓库中的二进制文件结合使用。更多详情和说明请参见下文!
先决条件
- Microsoft Windows 10 或 11
- Microsoft Visual Studio
- 已测试 Visual Studio 2019。
- Visual Studio 2022 安装 MSVC v142 构建工具后也应该可以工作。
- Visual Studio 的免费社区版也可以正常使用。
注意: 请确保在 Visual Studio 安装程序界面的"单个组件"列表中安装
MSVC v142 - VS 2019 C++ x64/x86 构建工具
。NVIDIA RTX Remix 仅在 Visual Studio 2019 构建工具上进行了测试,较新的编译器版本可能不兼容或导致意外结果/问题。 - Meson - 已测试 v0.61.4,最新版本也应该可以正常工作。
- 按照说明安装,并在继续之前重启电脑(Meson 会提示这一点)
- Python - 3.9 版本或更新版本
如何构建
要生成初始 Visual Studio 解决方案并构建,请运行 build_bridge_all.bat
,它将为 32 位和 64 位平台的调试/发布配置生成构建目录。
您还可以在根目录下新增的 _vs
子目录中,打开并重新构建不同配置的单个项目/解决方案。
注意: 要获得完整的 Remix Bridge 构建,您需要同时编译 x86 和 x64 解决方案,因为 bridge 客户端组件由 x86 解决方案构建,而 bridge 服务器组件由 x64 解决方案构建。
x86 解决方案的构建输出会进入仓库根目录中名为 _output
的文件夹(如果不存在则会创建)。
x64 解决方案的构建输出会进入仓库根目录的 _output
目录内名为 .trex
的文件夹。
注意: 从技术上讲,Remix Bridge 可以单独用于游戏,而不需要
dxvk-remix
仓库中的 Remix Runtime 组件,但这种情况下它将回退使用 Windows 中安装的 x64 系统 DirectX9 运行时,且无法执行任何光线追踪或资产替换。
注意: 在构建 bridge 之前,建议删除根目录下之前创建的任何以
_comp..
为前缀的构建目录和_vs
目录,特别是如果这是您第一次使用 ninja 后端构建系统构建 bridge。
如何运行
放置即用
运行 Remix Bridge 所需做的就是将构建输出复制到游戏目录中,使得游戏启动时能够加载 Remix Bridge 的 d3d9.dll
,然后它会加载所有其他组件。
注意: 根据游戏不同,Remix Runtime 文件的正确位置可能有所不同,可能在游戏的根目录或名为
bin
的子文件夹中。可能需要一些尝试才能找到特定游戏的正确位置!
使用 Remix Bridge 启动器
加载 Remix Bridge d3d9.dll
到游戏中的另一种方法是使用名为 NvRemixLauncher32.exe
的可执行文件,该文件由 Visual Studio x86 解决方案生成。某些游戏无法识别 Remix Bridge d3d9.dll
,需要在运行时注入。启动器可执行文件需要与其他 Remix Bridge 文件放在一起,然后可以从命令行这样使用:
> NvRemixLauncher32.exe <游戏可执行文件> <其他启动参数>
不带任何参数运行启动器可以查看可用选项列表。
将构建的二进制文件部署到游戏中
-
仅首次操作:将 gametargets.example.conf 复制为项目根目录中的 gametargets.conf
-
在 gametargets.conf 中更新您游戏的路径。参照 gametargets.example.conf 中的示例。确保删除所有三行开头的 "#"。可以同时添加多个游戏的配置。
-
打开并重新保存顶层 meson.build 文件(例如通过记事本)以更新其时间戳,然后重新运行构建。这将触发完整的 meson 脚本运行,在 Visual Studio 解决方案文件中生成一个项目,并将构建的二进制文件部署到 gametargets.conf 中指定的游戏目录中
Visual Studio 调试
使用这些组件的预期方式是使用 x86 解决方案中客户端 d3d9
项目编译的 32 位 d3d9.dll
输出,以及 x64 解决方案中服务器 NvRemixBridge
项目编译的 64 位服务器 dll。如果 .trex
目录中存在该文件,服务器 bridge 组件将加载 64 位 Remix Runtime(dxvk-remix
)d3d9.dll
,以将渲染命令和结果传递给 Vulkan 路径追踪渲染器并返回,但默认情况下,它会加载安装在默认 Windows 系统目录中的常规系统 DirectX9 dll。
注意: x86 解决方案同时包含
client
和server
项目,但不包含server
项目的复制任务,因为server
输出来自 x64 解决方案,后者只包含server
项目及其复制任务。server
项目保留在 x86 解决方案中只是为了使跨进程调试更容易,这样 Visual Studio 在进行子进程调试时能正确识别源文件。
如果您在 d3d9
项目设置的 调试
属性页中的 命令
字段输入游戏可执行文件,并在 工作目录
属性中输入游戏路径,那么直接从 Visual Studio 启动游戏并调试 Remix Bridge 会更加容易。
注意: 建议下载 VS 扩展以帮助调试/附加到子进程(如服务器 NvRemixBridge 组件)
Visual Studio 2015、2017、2019 版本:https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool
Visual Studio 2022 版本:https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool2022
将 RTX Remix Bridge 用于零售游戏
Remix Bridge 应该可以与许多零售游戏直接兼容,但根据游戏不同,可能需要额外的配置步骤以确保游戏以正确的 DirectX 设置运行。例如,Portal
需要使用 -dxlevel 70
参数启动才能正常工作。
此外,如前所述,如果您想利用 RTX Remix 的路径追踪功能,则需要将 Remix Bridge 二进制文件与 GitHub 上 dxvk-remix
仓库中的 Remix Runtime dll 结合使用。
获取完整二进制包的最简单方法是从GitHub上的RTX Remix
仓库下载发布版本。
Remix桥接架构与实现细节
- 客户端
d3d9
项目负责拦截32位游戏/应用的所有DirectX9 API调用,而服务器端NvRemixBridge.exe
接收这些调用并在64位进程和内存空间中执行,将它们传递给x64系统DirectX9运行时或其他渲染中间层(如果存在)。 - 服务器直接在客户端窗口中进行渲染。任何客户端d3d9运行时调用都是在不调用系统运行时的情况下实现的,因此只有服务器端桥接组件为游戏进程创建实际的d3d9设备,这有助于提高游戏兼容性,尤其是在独占全屏模式下运行时。
- 桥接服务器使用客户端打开的窗口句柄,并直接在其中呈现,这样就无需进行任何共享表面复制等操作。
- 客户端和服务器之间的主要通信渠道是通过环形队列实现的,这些队列组合形成一个
IpcChannel
。- 一个
IpcChannel
用于所有从客户端发送到服务器的命令,另一个通道将响应发送回客户端。同样,在每个IpcChannel
内,一个队列处理被调用的命令,另一个队列包含命令所需的参数和参数数据。 - 队列是全局静态变量,队列大小在
util_common.h
中定义。 - 服务器和客户端进程各自有自己的队列实例,但它们都指向相同的共享内存,因此从同一内存读写数据,避免任何额外的复制。
- 读写访问遵循简化的生产者/消费者模式,通过原子计数器(
AtomicCircularQueue
)或在客户端/服务器完成写入/读取时触发的信号量(BlockingCircularQueue
)来调节,当队列为空时,服务器将阻塞并等待新命令到来。如果队列被写入者填满,它将阻塞直到读取者消耗队列中的项目。 - 大多数情况下,客户端是队列的写入者,服务器是读取者,除了应用程序启动期间客户端和服务器交换握手信息,服务器通过向客户端发送响应来确认,以及执行需要服务器响应的命令时,例如将表面数据复制到数据队列中以便在客户端读取。
- 通过
push()
添加新命令/数据,通过pull()
调用读取。定义了一些PUSH
和PULL
宏,以便更容易快速发送命令或读取特定数据类型的数据。 - 数据队列支持发送任意大小的数据,通过传递大小和指向被推送数据的指针来实现。数据队列的底层基本类型是
uint32_t
,这意味着数据以4字节块发送。也支持发送nullptr
(即无数据但强类型)。
- 一个
- 有一些辅助函数支持开发和故障排除:
LogMissingFunctionCall()
用于标记客户端和服务器尚未完全实现的D3D9 API函数。在Debug
配置或日志级别设置为Debug
时执行期间,客户端将在Visual Studio的调试输出窗口中打印对缺失函数的调用,并将其写入日志文件,便于发现特定游戏或用例还需要实现的函数。LogFunctionCall()
默认不执行任何操作,但与LOG_ALL_CALLS
定义结合使用时,可用于记录所有函数的调用,无论其实现状态如何。这有助于理解可能导致特定崩溃/错误或需要调试的其他情况的调用流程。
- 关于代码组织的一些注释:
- 在客户端,实现代码分散在与D3D9 API接口名称匹配的单独文件中,但所有头文件都合并到单个文件
d3d9_lss.h
中。 - 在服务器端,目前几乎所有代码都在
main.cpp
中,可能会在某个时候重构为类似客户端的每个接口的单独文件。这是一个很长的文件,有一个非常长的switch
语句,但由于许多接口方法非常相似,这也使得在相关函数之间来回导航更容易,而不必在多个文件之间跳转。可能在达到一定的完整性和稳定性后重新组织最有意义,这样重构就不会导致与其他并行进行的更改产生大量合并冲突。
- 在客户端,实现代码分散在与D3D9 API接口名称匹配的单独文件中,但所有头文件都合并到单个文件
注意: 每次会话后,客户端日志的末尾将包含先前从客户端和服务器端接收和处理的d3d9命令。如果客户端发生崩溃,我们可以在服务器日志中找到这些信息。