D3D11 Immediate&DeferredContext与多线程渲染

最近在用 C++ DX11 写一些小玩具练手,其中不可避免地碰到了 Context 与多线程渲染内容,在此简单记录下。

D3D11 多线程渲染

D3D11 多线程渲染指的是 CPU 端使用多个线程并行生成 CommandList 并合并或提交,这是一个 CPU 端的概念。

单 GPU 执行 Command 时并没有多线程一说,它只是串行从 CommandList 取命令来执行。

所以,下文谈论的 D3D11 多线程渲染是为了优化或解决 CPU 瓶颈。

线程安全与不安全

D3D11 把线程安全与线程不安全的接口分成了两块,前者在D3D11Device中,后者在D3D11DeviceContext中。

D3D11Device的使用是线程安全的,而D3D11DeviceContext的使用是线程不安全的。

因此,多线程访问同一个 Context 或多个 Context 同时读写同一个 Resource,D3DRuntime 不负责保证线程安全。

由于 D3D11 图形 API 大多是阻塞执行的,在未做好线程同步的情况下执行非线程安全的行为,Crash 概率大大增加。

ImmediateContext 与 DeferredContext

二者都是 D3D11DeviceContext,只因作用不同而以命名区分二者。

在 D3D11 多线程渲染架构中,ImmediateContext用来生产及提交 CommandList,而DeferredContext仅用来生产 CommandList。

想要向硬件提交渲染命令,只能通过ImmediateContext

渲染时,DeferredContext生产的 CommandList 会通过ImmediateContext提交到设备的 CommandList 中。

图中虚线的“Optional Hardware Acceleration”指Driver Command Lists技术,它允许 Deferred Context 直接向 Driver 并行提交渲染命令,由于只有 NVIDIA 显卡完善支持,略过不表。

因此,一般情况下 D3D11 多线程渲染程序中会存在一个 RenderMain 线程及多个 RenderSub 线程:RenderMain 持有唯一的ImmediateContext,RenderSub 线程各自持有各自的DeferredContext

当 RenderSub 生产完 CommandList 后,我们会在 RenderMain 中调用ExecuteCommandList把 Sub 生产的 CommandList 提交到设备的 CommandList 中:

这些线程的同步需要开发者来维护。

我就是要多线程访问同一个 Context

原则上说,在 D3D11 环境中,同一个 Context 是可以被多个线程访问的,但 D3DRuntime 不负责维护线程安全,需要开发者自己维护。

如果线程同步没做好,轻则导致绘图混乱,重则程序崩溃。

同理,同一个 Resource 也可以在多个线程共享访问,但一样要做好线程同步。

所以,为了架构清晰与程序稳定,一般使用上文的单 immediate + 多 deferred 模式。

其他

DX12 的多线程渲染

DX12 虽然取消了 Context,但它处理 CommandList 的方式和 DX11 没有太大区别。

也就是说,DX12 还是需要开发者在一个 RenderMain 线程中拼接多个子线程生产的 CommandList,由开发者保证 CommandList 的组合与提交顺序。

DX12 的改进在于大部分 API 改成异步执行了,不再阻塞 CPU,在开发者水平较高的情况下,可以提高 CPU 的运行效率。

Unity 的多线程渲染

一般来说,Unity 的多线程渲染和上文的多线程渲染不是一个层级的概念。

在 DX12 之前,Unity 的多线程渲染大多是 UnityMain 单线程生产绘图中间层命令,UnityRender 线程把这些中间命令记录,并转换成底层图形 API 命令提交。

由于 D3D11 时期的绘图 API 大多是阻塞的,因此 Unity 的这种做法在 CPU 密集型的场景下会有一些优化效果(把 GPU 绘图阻塞放到了单独的 RenderThread 中,释放了 UnityMain 的 CPU 资源)。

但是,此时的 Unity 的多线程渲染仅仅是把绘图命令转换和提交的工作分配到单独线程上,并不是多个线程同时生产绘图命令。

不过,在 DX12 之后,如果平台支持且成功开启 Split Graphics Jobs,Unity 多线程渲染的工作模式会接近 DX 多线程:即多个 Worker 生产 CommandList,RenderThread 负责收集、拼装并提交。

梓喵出没博客(azimiao.com)版权所有,转载请注明链接:https://www.azimiao.com/10976.html
欢迎加入梓喵出没博客交流群:313732000

发表评论

*

*