前几天,我在 Linux Graphics 知识星球中报道了《Vulkan-Docs 新增 VK_EXT_extended_dynamic_state》的消息,当时我对于该 extension 只知道大致的意思,却不了解其真正的含义及作用。直到几天后,我在 Ricardo García 的个人博客上读到这篇原文时,才对 VK_EXT_extended_dynamic_state
有了深刻的理解。
原文作者并不是一上来就讲解该 extension 是什么、怎么用,而是先介绍 vulkan pipeline 、shader、dynamic state 这些基本概念,然后通过问题来引出 VK_EXT_extended_dynamic_state 以及它的用法,最后还介绍了该 extension 产生的故事背景,整篇阅读起来十分顺畅。因此本人决定将其翻译成中文,分享给大家,方便大家阅读。
几天前,Vulkan-Docs 发布了 VK_EXT_extended_dynamic_state
扩展(extension),并首次出现在 Vulkan 1.2.145 版本中。这是一个非常有趣的扩展,它让 Vulkan pipeline 在许多使用场景中,变得更加灵活和实用。在 Igalia 公司,我作为该扩展的 VK-GL-CTS 测试作者参与了该扩展的开发工作,并且以个人绵薄之力 review 了 spec 文档,同时还对其做了一些小的修改。
此 Vulkan 扩展的目的,是为了让 Vulkan pipeline 在使用时可以动态设置某些参数,而不是那些一成不变的值(它们通常在创建 pipeline 的时候就固定了下来),从而让 Vulkan pipeline 变得更加灵活。对于那些不太熟悉 Vulkan 的人来说,Vulkan pipeline 可谓是 API 中体量最大的一个 object。Vulkan 通常包含 Compute Pipeline 和 Graphics Pipeline。对于这个扩展而言,我们只讨论 Graphics Pipeline。一个 pipeline object,当它被创建时,它包含了大量有关场景渲染时 GPU 需要执行哪些操作的信息。例如,如何从内存中读取三角形的顶点,有多少个 texture、buffer 和 image,颜色混合(color blending)参数,深度和模板测试(depth and stencil test)参数,多重采样抗锯齿(multi-sample anti-aliasing)参数,视口(viewport)参数,等等。
Vulkan 是一个低开销(low-overhead)的 API,它能帮助你尽可能地榨取 GPU 的性能。它希望你能提前指定所有信息,以便 Vulkan 底层实现(即 GPU 和驱动程序)在 pipeline 创建和运行时都有更多的机会来优化应用程序。每当你 “bind 一个 pipeline” 时(例如,将其作为后续 command 的 active pipeline),你都是在告诉底层 GPU 驱动应该如何工作。接着通常会有一堆 commands 紧随其后,用来告诉 GPU 使用前面的参数来绘制大量的几何图形。
创建 pipeline 可能还涉及到将 shader (着色器)编译为 GPU 本地可执行指令。shader 是一些可以直接运行在 GPU 上的小程序,当渲染流程进行到可编程阶段时,就会执行这些 shader 程序。当使用 GPU 绘制图形时,绘制过程将被拆分成多个阶段,每个阶段都会有许多输入数据,这些数据既可以来自于上一阶段,也可以来自于外部资源(如 buffer、texture 等),然后会生成许多输出,以供下一个阶段直接使用,或作为外部资源的后处理使用。其中一些阶段是固定不变的,而另一些则是可编程的(通过用户提供的 shader 程序来实现)。而当这些 shader 程序不那么小的时候,将它们编译并优化成 GPU 可执行指令将花费更多时间。通常算不上很长的时间,但如果使用毫秒计数的话,你就只有 16.6 ms 的时间来画下一帧,以便能达到 60fps 的效果。也正是这个问题催生了 ACO Shader 编译器的产生(用于 Mesa 中的 RADV 驱动,即 amdgpu vulkan 驱动),这也是为什么有些驱动程序会对 shader 的内容进行 hash 处理,并通过 shader cache 来检查之前是否编译过该 shader 程序。这同样也是为什么 Vulkan 希望你能尽可能提前创建 pipeline 的原因,否则当你在为动作游戏准备下一帧的过程中,突然发现需要一个新的 pipeline 时,可能会因为新的 pipeline 创建过程需要额外的处理时间而导致整个游戏变得卡顿。
Vulkan 为我们提供了几种缓解上述问题的可行方案。第一种,你可以事先创建好所有你认为需要的 pipeline。这的确是一种有效的方法,但由于我们可能会使用不同的 pipeline 参数进行组合,因而可能会创建大量的 pipeline。假设你想分别修改7个不同的参数,每个参数都有2个可能的值。这也就意味着,你必须在应用程序中创建128个(2 的 7 次方)不同的 pipeline 并对它们进行管理维护。另一种方案则是使用 pipeline cache,如果我们创建的是一个与之前相同或相似的 pipeline 的话,它将加快创建 pipeline 的速度,这样你就只需关注在给定时间点所需的 pipeline 变量就可以了。最后,Vulkan 为我们提供了动态修改 pipeline 参数的能力,而不是在创建 pipeline 时为它们设置固定的值,那就是 pipeline 中的 dynamic state。
dynamic state 可以帮助我们解决前面提到的所有问题。它使你的应用程序逻辑变得更加简单,因为你不再需要处理这么多不同的变量。同时它也减少了创建新 pipeline 的次数,这样就可以缩短初始化时间,降低 pipeline cache 的大小和访问次数,减少 state 修改以及游戏卡顿。VK_EXT_extended_dynamic_state
,当它被创建出来的时候,顾名思义,它扩展了 dynamic state 的 pipeline 元素的数量。它添加了如下 state:culling mode(剔除模式)、front face(正面)、primitive topology(图元拓扑)、带计数的 viewport、 带计数的 scissor(虽然以前的 viewport 和 scissor 也可以动态修改,但是我们无法修改它们的计数值)、顶点输入 binding stride、depth test(深度测试)激活与写入、depth comparison(深度比较)操作、depth bounds(深度边界)激活以及 stencil test(模板测试)激活等操作。这是一个相当大的新 dynamic 元素集合!
接下来的一个明显的问题是,使用这么多的 dynamic 元素是否会导致性能下降?因为 pipeline 的某些细节我们是无法提前预知的,从某种意义上讲这可能会减少 Vulkan 驱动实现的优化机会。我的回答是,这取决于 Vulkan 驱动的具体实现。例如,在某些 Vulkan 驱动的具体实现中,culling mode 或 front face 可以在绘图操作之前就设置到寄存器中,不管是在绑定 pipeline 的时候设置这些参数,还是在发送大量绘图命令之前动态的设置这些参数,就 Vulkan 驱动本身而言,这两种方式没有实际的差别。
我使用一个简单的 GPU-bound 的 Vulkan 程序测试了开启每个新的 dynamic state 对性能的影响,在屏幕上显示一个旋转的 3D 模型,结果我并没有发现它对 NVIDIA 专有驱动和 GTX 1070 显卡有任何性能上的影响,但你的测试结果可能会和我有所差异。和往常一样,在部署前请先进行测试。
当使用 Vukan 作为后端来实现其他更高级的 API 时,VK_EXT_extended_dynamic_state
也能派上用场。这些 API 不像 Vulkan 本身那样严格,其中一些绘图参数可以动态的修改,这全凭驱动程序来尽可能高效地完成这些修改。我说的其实是 OpenGL 或者 DirectX 11 之前的版本,正如你所猜想的,它对于 DXVK 这类项目而言是一个非常有意思的扩展接口,借助 Wine 和 Proton 它可以帮助改善 Linux 上的游戏现状。
关于这个扩展的背景故事也是十分有意思的,这一切都源于 Eric Lengyel 的一条“愤怒”的推特,他在推特中抱怨道,在渲染反射时,他不得不创建两条独立的 pipeline,仅仅只是为了改变 front face 或者三角形的环绕顺序。这促使 NVIDIA 工程师 Piers Daniell 在 Khronos 组织内部启动了一项 multi-vendor 的工作,并最终形成了 VK_EXT_extended_dynamic_state
这个扩展。正如你在该扩展的 summary 中所看到的,有许多公司参与了该扩展的制定,它们是:AMD、Arm、Broadcom、Google、Imagination、Intel、NVIDIA 和 Valve。
因此,这个扩展也是 Khronos 组织的众多成功案例之一。Khronos 组织就像是一个论坛,在这个论坛里,大大小小的硬件和软件厂商为实现图形行业跨平台解决方案,都纷纷参与到该方案的设计和标准的制定中来。在设计这些解决方案时,它们采纳了许多不同的观点和意见。如果你看一眼成员名单,你会看到许多来自硬件制造商和软件开发商的知名 logo,甚至还包括一些非常流行的游戏引擎公司。
在这个案例中,一条愤怒的推特就可以碰撞出大家共同努力的火花,但我们不应该它当作典范来模仿。其实你可以通过 Vulkan-Docs Github 仓库来提交改进 Vulkan Spec 的建议、新的 extension 以及好的想法。提交一个 Issue 其实就足够了!对于一个小的修改,使用 Pull Request 可能会更好!
全文完。
VK_EXT_extended_dynamic_state released for Vulkan