FidelityFX 帧插值交换链 3.1.5
目录
引言
FrameInterpolationSwapChain 实现 IDXGISwapChain4 接口,以提供一种便捷的方式来处理帧插值和帧率显示所需的工作负载分派。
虽然此实现可能不适用于所有引擎或应用程序,但它旨在提供一种简便的方法来集成 FSR3 帧生成,使其(几乎)对底层应用程序透明。
描述
从应用程序的角度来看,FrameInterpolationSwapChain 可以替代 DXGI 交换链,并且行为应该类似。
禁用帧生成时,主要区别在于与直接使用交换链相比,present 操作的开销略有增加(多一次表面复制)。
在这种情况下,帧插值交换链仍然支持处理 UI 合成,因此在禁用帧插值时,应用程序无需区别处理其 UI。
在内部,FrameInterpolationSwapChain 将创建 2 个额外的 CPU 线程。
- 第一个线程用于避免在等待 GPU 完成插值时阻塞应用程序。然后,该线程将获取当前的 CPU 时间并计算帧率信息。
- 第二个线程负责分派 UI 合成的 GPU 工作负载(如果需要,则调用回调函数)以及进行非插值帧的 present 帧率控制。
集成
FrameInterpolationSwapChain 应使用 FidelityFX API 集成。本文档描述了 FrameInterpolationSwapChain 特定的 API 构造。
DirectX 12 的 FrameInterpolationSwapChain 实现 IDXGISwapChain4 接口,因此一旦创建,它的行为就与普通交换链一样。
创建方式有几种:
- 创建并填充
ffxCreateContextDescFrameGenerationSwapChainWrapDX12结构,然后调用ffxCreateContext。这将包装并替换现有的交换链。 - 创建并填充
ffxCreateContextDescFrameGenerationSwapChainNewDX12结构,然后调用ffxCreateContext。这将使用提供的dxgiFactory创建一个新的交换链。 - 创建并填充
ffxCreateContextDescFrameGenerationSwapChainForHwndDX12结构,然后调用ffxCreateContext。这将使用提供的hwnd、desc、fullscreenDesc和dxgiFactory创建一个新的交换链。
录制和分派帧插值工作负载
FrameInterpolationSwapchain 的设计独立于 FidelityFX 帧插值接口。为了实现这一点,它不直接与这些接口交互。帧插值工作负载可以通过两种方式提供给 FrameInterpolationSwapchain:
- 在
ffxConfigureDescFrameGeneration中提供回调函数frameGenerationCallback。当帧插值启用时,在游戏线程调用::Present时,FrameInterpolationSwapChain将调用此函数来记录包含帧插值工作负载的命令列表。 - 调用
ffxQueryDescFrameGenerationSwapChainInterpolationCommandListDX12从FrameInterpolationSwapChain获取命令列表,并将帧插值工作负载记录到其中。在这种情况下,命令列表将在调用 present 时执行。
命令列表可以在当前调用 present 的命令队列上执行,也可以在异步计算队列上执行。
- 如果应用程序调用 upscale 但随后决定不对某一帧调用 present,则同步执行更具弹性。
- 异步执行可能会带来更高的性能,具体取决于硬件以及与帧插值工作负载并行运行的工作负载。
无论哪种方式,UI 合成和 present 都将在第二个图形队列上执行,以避免将 UI 合成限制为计算,并允许驱动程序在准备下一帧时调度 present 调用。
注意:为确保 present 能在 FSR3 帧率控制逻辑预期的时间执行,避免微小卡顿并确保显示器的 VRR 响应良好,建议确保帧由多个命令列表组成。
UI 组成
在使用帧插值时,强烈建议特别注意 UI,因为游戏运动矢量引起的失真在 3D 场景中很难察觉,但会显著影响 UI 文本的可读性,并导致非常明显的伪影,尤其是在 UI 的任何直线、硬边上。
为了解决任何伪影并保持 UI 的美观和可读性,FrameInterpolationSwapChain 提供了 3 种处理 UI 合成的方式:
-
注册一个回调函数,该函数将在后缓冲之上渲染 UI。此函数将在每个显示的后缓冲(插值和真实)上调用,因此允许应用程序以显示速率渲染 UI 动画,或为发送到监视器的每一帧以不同方式应用诸如胶片颗粒等效果。但是,这种方法显然会对性能产生一定影响,因为 UI 将需要渲染两次,因此应注意仅在 UI 回调中记录小型工作负载。
-
将 UI 渲染到单独的表面,以便可以对其进行 alpha 混合并添加到最终的后缓冲中。这样,UI 就可以应用于插值和真实后缓冲,而不会产生任何失真。
-
除了最终后缓冲外,向
FrameInterpolationSwapChain提供一个包含无 HUD 场景的表面。在这种情况下,帧插值着色器将检测帧中的 UI 区域并抑制这些区域的失真。
内存使用情况
图表中的数据取自使用 DirectX 12 的 Radeon RX 9070 XTX,以最近的 MB 为单位,并且可能会发生变化。
| 输出分辨率 | UI 模式 | 内存使用量 (MB) |
|---|---|---|
| 3840x2160 | 单独的 UI 表面 + FFX_FRAMEGENERATION_UI_COMPOSITION_FLAG_ENABLE_INTERNAL_UI_DOUBLE_BUFFERING | 191 |
| 3840x2160 | 所有其他模式 | 159 |
| 2560x1440 | 单独的 UI 表面 + FFX_FRAMEGENERATION_UI_COMPOSITION_FLAG_ENABLE_INTERNAL_UI_DOUBLE_BUFFERING | 90 |
| 2560x1440 | 所有其他模式 | 75 |
| 1920x1080 | 单独的 UI 表面 + FFX_FRAMEGENERATION_UI_COMPOSITION_FLAG_ENABLE_INTERNAL_UI_DOUBLE_BUFFERING | 51 |
| 1920x1080 | 1920x1080 | 42 |
应用程序可以在创建上下文后,通过调用 ffxQuery 并传入有效的上下文和 ffxQueryFrameGenerationSwapChainGetGPUMemoryUsageDX12 来获取 FidelityFX 交换链上下文所需的 GPU 本地内存量。
应用程序可以在创建上下文之前,通过调用 ffxQuery 并传入 NULL 上下文,并填写 ffxQueryFrameGenerationSwapChainGetGPUMemoryUsageDX12V2 来获取默认 FidelityFX 交换链版本的 GPU 本地内存需求。要获取不同 FidelityFX 交换链版本的内存需求信息,还需要链接 ffxOverrideVersion。
有关如何调用 Query 的代码示例,请参阅。
可等待对象
建议游戏使用 GetFrameLatencyWaitableObject 获取一个可等待对象,然后使用该对象防止 CPU 过度领先于 GPU。当 VSync 开启且显示器刷新率较低时,这一点尤其重要,因为 GPU 的渲染速率可能会远低于 CPU 的提交速率。或者,应用程序可以使用设置为显示器刷新率一半的帧率限制器。
帧率和显示
FrameInterpolationSwapchain 会自动处理帧率。由于 Windows 不是实时操作系统,并且可变刷新率显示器对定时不精确很敏感,因此 FSR3 被设计为使用忙等待循环以实现最佳的定时行为。
启用帧生成后,帧的渲染时间可能差异很大。插值帧的工作负载可能远小于应用程序渲染的帧(“真实”帧)。因此,正确控制帧的显示速率以确保流畅的体验至关重要。目标是让每一帧显示相同的时间。
显示和帧率控制通过两个独立的 CPU 线程完成,独立于主渲染循环。一个高优先级帧率控制线程会跟踪平均帧时间(包括 UI 合成时间),并计算目标显示时间间隔。它还会等待 GPU 工作完成,以避免在 CPU 端调用 present 后出现长时间的 GPU 等待。
为防止任何帧时间峰值过度影响帧率,会使用多个帧的移动平均值来估算帧时间。
一个 present 线程分派生成帧的帧合成工作,等待自上次显示以来已过计算的 present 时间间隔,然后显示生成帧。它会为真实帧重复此操作。
应用程序应确保渲染帧率略低于所需输出帧率的一半。启用 VSync 时,渲染性能将自动限制为显示器最高刷新率的一半。
建议应用程序创建的 GPU 队列使用正常优先级,以便高优先级地调度插值工作。此外,开发人员应注意与插值和合成并行运行的命令列表要简短(执行时间方面),以便能够精确地调度显示。
预期行为
为了进一步说明帧率控制方法及其背后的原理,以下各节将根据插值后的帧率以及显示器是使用固定还是可变刷新率来概述预期行为。
固定刷新率
启用 VSync
在这种情况下,会禁用画面撕裂,并且每一帧都会显示至少一个同步间隔。显示是与显示器的垂直消隐周期(“vsync”)同步的。这可能会导致显示定时不均匀,并可能增加输入延迟(最多一个刷新周期)。
在图中,第一帧真实帧显示在垂直消隐间隔之后,导致前一帧插值帧显示两个刷新间隔,与立即显示相比延迟增加。
禁用 VSync
在这种情况下,可能会发生画面撕裂。显示不同步于显示器。这样做的好处是与较低的帧率相比,输入延迟会降低。
可变刷新率
本节适用于支持可变刷新率 (VRR) 技术(如 AMD FreeSync、NVIDIA G-SYNC® 和 VESA AdaptiveSync)的显示器和 GPU 组合。
显示刷新之间的时序由可变刷新率窗口决定。两次刷新之间的 delta 时间可以是窗口内的任何时间。例如,如果 VRR 窗口是 64-120Hz,则 delta 时间必须在 8.33 到 15.625 毫秒之间。如果 delta 超出此窗口,则很可能会发生画面撕裂。
如果在窗口内没有新的 present 发生,则会再次显示上一帧。
VRR 窗口内的插值帧率
可变刷新窗口通常不会超过显示器的报告原生刷新率,因此在这种情况下会禁用画面撕裂。
VRR 窗口外的插值帧率
如果帧率低于 VRR 窗口的下限,则预期行为与帧率低于固定刷新率显示器刷新率(见上文)的情况相同。
如果帧率高于 VRR 窗口的上限,则预期行为与帧率高于固定刷新率显示器刷新率(见上文)的情况相同。
附加信息
FrameInterpolationSwapChain 创建的资源列表
- 两个 CPU 工作线程。其中一个将在插值帧和真实帧的 present 之间进行部分忙碌等待,以精确计时 present。
- 一个异步计算队列 - 仅在创建 FSR3 上下文时设置了
FFX_FRAMEGENERATION_ENABLE_ASYNC_WORKLOAD_SUPPORT,并且ffxConfigureDescFrameGeneration中的allowAsyncWorkloads为 true 时使用。 - 一个异步 present 队列。此队列将用于执行 UI 合成工作负载和 present。
- 用于插值和 UI 合成工作负载的一组命令列表、分配器和栅栏。
- 将后缓冲 blit 到交换链并合成 UI 所需的 GPU 资源(如果未使用回调)。
- 附加到实际游戏窗口的交换链。
FrameInterpolationSwapchain 的设计旨在最大程度地减少运行时动态分配。
- 该类在交换链生命周期内的系统内存使用量是恒定的,不使用 STL。
- DirectX 资源在首次使用时创建,并保留以供重用。
构建示例
要构建FSR示例,请遵循以下说明:
-
下载并安装以下软件开发工具的最低版本:
- Visual Studio 2022 (在安装过程中安装
vcpkg包管理器) - Windows 10 SDK 10.0.18362.0
- Visual Studio 2022 (在安装过程中安装
-
打开Visual Studio解决方案
终端窗口 > <installation path>\Samples\FidelityFX_FSR\dx12\FidelityFX_FSR_2022.sln -
首次vcpkg安装
- 如果vcpkg尚未在Visual Studio中初始化,请执行以下操作:
- 从菜单中选择
工具,然后选择Visual Studio命令提示符以打开终端。 - 键入
vcpkg integrate install并按Enter键。 - 关闭并重新打开解决方案。
- 从菜单中选择
构建项目
Visual Studio解决方案文件(.sln)将包含构建效果示例所需的所有项目。要构建解决方案中的项目,您应该从Visual Studio顶部的菜单中点击生成,然后点击生成解决方案。这将构建示例的所有依赖项(例如FidelityFX Cauldron Framework),然后构建示例应用程序。
运行项目
从Visual Studio运行项目
-
如果尚未突出显示,请在解决方案资源管理器中选择示例项目。
-
右键单击项目,然后选择
设置为启动项目。 -
右键单击项目,然后选择
属性 -
在
配置属性下,单击调试条目。 -
为所有配置(
Debug和Release)将工作目录设置为$(TargetDir)。 -
单击
应用,然后单击确定关闭属性面板。 -
从工具栏中单击
生成菜单项,然后单击运行。
局限性
FSR 需要支持类型化 UAV 加载和 R16G16B16A16_UNORM 的 GPU。
版本历史
| 版本 | 日期 |
|---|---|
| 1.1.1 | 2023-11-28 |
| 1.1.2 | 2024-06-05 |
| 1.1.3 | 2025-05-08 |
| 3.1.5 | 2025-08-20 |
有关版本的更多详细信息,请参阅变更日志。