VK_AMD_shader_early_and_late_fragment_tests
本文档描述了一个关于新的 SPIR-V 执行模式的提案,该模式允许片段着色器被早期片段操作丢弃,即使它们包含对存储资源的写入或其他副作用。
1. 问题陈述
大多数图形设备能够在片段着色器避免某些操作时利用早期片段操作 - 例如,写入深度或模板,或写入存储资源,在大多数情况下提供显著的性能优势。这在可能的情况下隐式启用,并且可以通过在 SPIR-V 中指定 EarlyFragmentTests
执行模式来显式启用。但是,EarlyFragmentTests
执行模式使得从着色器写入片段深度无效。
某些实现可以在片段着色之前和之后执行深度测试,允许保守的早期测试丢弃大多数片段,并允许晚期测试以更高的精度丢弃片段。GL_ARB_conservative_depth 添加了一种方法,即使深度是由片段着色器写入的,也可以启用此优化,从而允许在某些条件下实现进一步的优化。但是,如果着色器也写入存储资源,则由于规范的可预测性要求,无法进行此类优化。在应用程序不关心丢弃时是否执行片段着色器存储写入的情况下,可以在某些控制台平台上使用此功能来显著提高性能,但到目前为止,Vulkan 没有机制来做到这一点。对于某些应用程序,这可能意味着不必要的性能损失,而这种损失应该相对容易解决。
2. 解决方案空间
这个问题实际上只有一个解决方案,即以某种方式向应用程序公开此功能。主要问题是如何公开此功能,以什么粒度公开,以及我们是否应该提供任何保证。最终,它应该相对容易地打开/关闭,并且理想情况下应该以某种形式在每个片段着色器中设置。
表示开关的主要选项是
-
管线创建标志
-
SPIR-V 执行模式
-
非语义指令
如果它成为管线创建标志,则很容易在每个管线的基础上打开/关闭。但是,是否可以丢弃写入的知识通常是片段着色器代码本身中编写的任何算法的属性,这意味着此属性必须在两个位置指定。从这个角度来看,在着色器代码本身中包含一些东西是有意义的。
SPIR-V 执行模式是表达此功能的一种直接方式,并且与 SPIR-V 中表达保守深度的方式一致(例如,DepthGreater
执行模式)。使用本质上是优化提示的执行模式的缺点是,驱动程序必须实现该扩展才能使 SPIR-V 有效;而实际上在所有情况下都可以安全地忽略它。
非语义指令似乎提供了一种指定行为的方式,而无需驱动程序实现任何新东西。不幸的是,由于诱导的行为违反了当前的 Vulkan 规范,因此它不适合此用例,因为它不能安全地添加到所有着色器。
如果没有像非语义扩展那样更广泛的“可以忽略但可以改变行为”的扩展,SPIR-V 执行模式可能是最合适的选择。应用程序、面向应用程序的库或 Vulkan 软件层可以用来在不支持时自动删除执行模式。如果实现不具备所需的功能,也应该最终能够支持该执行模式作为空操作。
3. 提案
3.1. 新的 Vulkan 功能
typedef struct VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD {
VkStructureType sType;
void* pNext;
VkBool32 shaderEarlyAndLateFragmentTests;
} VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD;
此功能允许在实现使用的 SPIR-V 着色器中使用新的执行模式。
3.2. 新的 SPIR-V 执行模式
引入了一种新的执行模式,允许在执行深度和模板写入时,结合深度优化,同时执行提前和延迟的深度和模板测试。为了允许使用此新执行模式进行模板参考写入,提供了类似的模板参考写入优化。
执行模式 | 额外操作数 | 启用功能 | |
---|---|---|---|
5017 |
EarlyAndLateFragmentTestsAMD |
着色器 |
|
5079 |
StencilRefUnchangedFrontAMD |
StencilExportEXT |
|
5080 |
StencilRefGreaterFrontAMD |
StencilExportEXT |
|
5081 |
StencilRefLessFrontAMD |
StencilExportEXT |
|
5082 |
StencilRefUnchangedBackAMD |
StencilExportEXT |
|
5083 |
StencilRefGreaterBackAMD |
StencilExportEXT |
|
5084 |
StencilRefLessBackAMD |
StencilExportEXT |
这允许实现显式执行提前和延迟测试。
3.3. 新的 GLSL 布局限定符
以下新的布局限定符被添加到 GLSL 中
片段着色器允许以下独立的声明
__early_and_late_fragment_testsAMD
请求在片段着色器执行之前和之后执行某些片段测试,如 Vulkan 1.2 规范的“片段操作”章节中所述。此声明必须单独出现在一行中。
可以指定以下附加的独立声明
layout-qualifier-id:
__stencil_ref_unchanged_frontAMD
__stencil_ref_less_frontAMD
__stencil_ref_greater_frontAMD
__stencil_ref_unchanged_backAMD
__stencil_ref_less_backAMD
__stencil_ref_greater_backAMD
这些声明必须各自单独出现在一行中。只能指定一个 stencil_ref_*frontAMD 和一个 stencil_ref*_backAMD 声明。每个声明都约束任何着色器调用写入的 gl_FragStencilRefARB
的最终值的意图。如果与声明一致的所有 gl_FragStencilRefARB
值都将失败(或通过),则允许实现执行优化,假设给定片段的模板测试失败(或通过)。这可能包括如果片段被遮挡并且着色器没有副作用而导致片段被丢弃时跳过着色器执行。如果 gl_FragStencilRefARB
的最终值与着色多边形的朝向的声明不一致,则相应片段的模板测试结果是未定义的。如果模板测试通过且启用了模板写入,则写入模板缓冲区的值始终是 gl_FragStencilRefARB
的值,无论它是否与布局限定符一致。
以上每个限定符都直接映射到等效命名的 spir-v 执行模式。
3.4. 新的 HLSL 属性
添加了以下新的 Vulkan 特定属性
-
early_and_late_tests
:将入口点标记为启用提前和延迟深度测试。如果通过SV_Depth
写入深度,则还必须指定depth_unchanged
(可以自由写入 SV_DepthLess 和 SV_DepthGreater)。如果通过SV_StencilRef
写入模板参考值,则必须指定stencil_ref_unchanged_front
、stencil_ref_greater_equal_front
或stencil_ref_less_equal_front
中的一个,以及stencil_ref_unchanged_back
、stencil_ref_greater_equal_back
或stencil_ref_less_equal_back
中的一个。 -
depth_unchanged
:指定写入SV_Depth
的任何深度都不会使早期深度测试的结果无效。在 SPIR-V 中设置DepthUnchanged
执行模式。 -
stencil_ref_unchanged_front
:指定当片段是正面时,写入SV_StencilRef
的任何模板引用都不会使早期模板测试的结果无效。在 SPIR-V 中设置StencilRefUnchangedFrontAMD
执行模式。 -
stencil_ref_greater_equal_front
:指定当片段是正面时,写入SV_StencilRef
的任何模板引用值将大于或等于 API 设置的模板参考值。在 SPIR-V 中设置StencilRefGreaterFrontAMD
执行模式。 -
stencil_ref_less_equal_front
:指定当片段是正面时,写入SV_StencilRef
的任何模板引用值将小于或等于 API 设置的模板参考值。在 SPIR-V 中设置StencilRefLessFrontAMD
执行模式。 -
stencil_ref_unchanged_back
:指定当片段是背面时,写入SV_StencilRef
的任何模板引用值不会使早期模板测试的结果无效。在 SPIR-V 中设置StencilRefUnchangedBackAMD
执行模式。 -
stencil_ref_greater_equal_back
:指定当片段是背面时,写入SV_StencilRef
的任何模板引用值将大于或等于 API 设置的模板参考值。在 SPIR-V 中设置StencilRefGreaterBackAMD
执行模式。 -
stencil_ref_less_equal_back
:指定当片段是背面时,写入SV_StencilRef
的任何模板引用值将小于或等于 API 设置的模板参考值。在 SPIR-V 中设置StencilRefLessBackAMD
执行模式。
着色器不能指定多个 stencil_ref_unchanged_front
、stencil_ref_greater_equal_front
和 stencil_ref_less_equal_front
中的一个。着色器不能指定多个 stencil_ref_unchanged_back
、stencil_ref_greater_equal_back
和 stencil_ref_less_equal_back
中的一个。