异步编程 (DirectX 和 C++)
本主题介绍在 DirectX 中使用异步编程和线程处理时要考虑的各种要点。
异步编程和 DirectX
如果只是了解 DirectX,或者即使遇到 DirectX,请考虑将所有图形处理管道放在一个线程上。 在游戏中的任何给定场景中,都有需要独占访问权限的常用资源,例如位图、着色器和其他资产。 这些相同的资源要求跨并行线程同步对这些资源的任何访问。 呈现是跨多个线程并行化的一个困难过程。
但是,如果你的游戏足够复杂,或者想要提高性能,则可以使用异步编程来并行化一些不特定于呈现管道的组件。 新式硬件具有多个核心和超线程 CPU,你的应用应利用这一点! 对于不需要直接访问 Direct3D 设备上下文的游戏的某些组件,可以使用异步编程来确保这一点,例如:
- 文件 I/O
- 物理特性
- AI
- networking
- audio
- controls
- 基于 XAML 的 UI 组件
应用可以在多个并发线程上处理这些组件。 文件 I/O(尤其是资产加载)从异步加载中获益很大,因为游戏或应用可以处于交互状态,同时加载或流式传输几百兆字节的资产。 创建和管理这些线程的最简单方法是使用并行模式库和任务模式,如 PPLTasks.h 中定义的并发命名空间中包含的。 使用并行模式库直接利用多个核心和超线程 CPU,并且可以改进从感知的加载时间到 CPU 计算或网络处理所附带的命中和滞后等一切。
请注意,在通用 Windows 平台(UWP)应用中,用户界面完全在单线程单元(STA)中运行。 如果要使用 XAML 互操作为 DirectX 游戏创建 UI,则只能使用 STA 访问控件。
使用 Direct3D 设备进行多线程处理
设备上下文的多线程仅在支持 Direct3D 功能级别 11_0 或更高版本的图形设备上可用。 但是,你可能希望最大程度地利用许多平台(例如专用游戏平台)的强大 GPU。 在最简单的情况下,你可能希望将头上显示(HUD)覆盖的呈现与 3D 场景呈现和投影分开,并且两个组件都使用单独的并行管道。 不过, 这两个线程都必须使用相同的 ID3D11DeviceContext 来创建和管理资源对象(纹理、网格、着色器和其他资产),不过,这是单线程的,这要求你实现某种同步机制(如关键部分)以安全地访问它。 而且,虽然可以在不同线程(延迟呈现)上为设备上下文创建单独的命令列表,但不能在同一 ID3D11DeviceContext 实例上同时播放这些命令列表。
现在,你的应用还可以使用 ID3D11Device(对于多线程安全)来创建资源对象。 那么,为什么不总是使用 ID3D11Device 而不是 ID3D11DeviceContext? 嗯,目前,对多线程的驱动程序支持可能不适用于某些图形接口。 你可以查询设备并找出它是否支持多线程处理,但如果希望访问最广泛的受众,则可以坚持单线程 ID3D11DeviceContext 进行资源对象管理。 也就是说,当图形设备驱动程序不支持多线程或命令列表时,Direct3D 11 会尝试在内部处理对设备上下文的同步访问;如果不支持命令列表,则提供软件实现。 因此,可以编写多线程代码,该代码将在具有图形接口的平台上运行,这些接口缺少对多线程设备上下文访问的驱动程序支持。
如果你的应用支持单独的线程来处理命令列表和显示帧,你可能希望使 GPU 保持活动状态,以及时的方式处理命令列表,同时以不知情的口号或滞后时间显示帧。 在这种情况下,可以对每个线程使用单独的 ID3D11DeviceContext,并通过使用 D3D11_RESOURCE_MISC_SHARED 标志创建资源来共享资源(如纹理)。 在此方案中, 必须在处理线程上调用 ID3D11DeviceContext::Flush 才能在显示显示线程中处理资源对象的结果之前完成命令列表的执行。
延迟呈现
延迟呈现在命令列表中记录图形命令,以便可以在其他时间播放图形命令,并且旨在支持在一个线程上呈现,同时录制用于在其他线程上呈现的命令。 完成这些命令后,可以在生成最终显示对象的线程上执行它们(帧缓冲区、纹理或其他图形输出)。
使用 ID3D11Device::CreateDeferredContext(而不是 D3D11CreateDevice 或 D3D11CreateDeviceAndSwapChain)创建延迟的上下文,以创建即时上下文。 有关详细信息,请参阅 “即时”和“延迟呈现”。
相关主题