NVIDIA 很高兴介绍下一代 GPU 中可用的新功能,称为着色器执行重新排序( SER )。 SER 是一种性能优化,可以释放光线跟踪着色器中更好的光线和内存一致性的潜力,从而提高着色效率。
背景和概述
着色发散是光线跟踪中的一个长期问题。随着渲染器实现的日益复杂,越来越多的工作负载受到着色器执行而不是光线跟踪的限制。缓解此问题的一种方法是在执行着色器代码时减少影响 GPU 的发散。
SER 有助于缓解两种类型的分歧:执行分歧和数据分歧。当不同的线程在一个着色器中执行不同的着色器或分支时,会发生 Execution divergence 。当不同的线程以难以缓存的模式访问内存资源时,会发生 Data divergence 。
SER 通过动态地重新排序 GPU 上的线程来缓解分歧,以便它们能够以更高的一致性继续执行。它还支持光线相交和着色的解耦。
有关 SER 的更全面概述,请参阅 Shader Execution Reordering 白皮书。
使用 NVIDIA API 集成
先决条件
要访问 SER 提供的功能集和优化,您需要以下各项:
- GPU 支持 DXR 1.0 或更高版本
- 支持 SER 、 R520 和更新版本的驱动程序
- HLSL 扩展头,可在最新的 NVIDIA API 中找到
- 针对 nvapi64.lib 的链接,包含在包含上述标题的包中
- DXC / dx 编译器的最新版本。支持模板的 dll (可选)。如果要从 Visual Studio 编译着色器,请确保将项目配置为使用此版本的编译器可执行文件。
Get started with SER and NVIDIA API .
初始化 NVAPI 并启用 SER API
首先,使用以下调用初始化/取消初始化 NVAPI :
NvAPI_Initialize();
NvAPI_Unload();
接下来,使用以下调用验证 SER API 是否受支持:
bool supported = false; NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(pDevice, NV_EXTN_OP_HIT_OBJECT_REORDER_THREAD, &supported); if (!supported) { /* Don't use SER */ }
主机端集成
在创建光线跟踪状态对象之前,设置一个假 UAV 插槽并注册它:
#define NV_SHADER_EXTN_SLOT 999999 // pick an arbitrary unused slot #define NV_SHADER_EXTN_REGISTER_SPACE 999999 // pick an arbitrary unused space NvAPI_D3D12_SetNvShaderExtnSlotSpace(pDevice, NV_SHADER_EXTN_SLOT, NV_SHADER_EXTN_REGISTER_SPACE);
如果需要线程本地变量,请使用相关函数:NvAPI_D3D12_SetNvShaderExtnSlotSpaceLocalThread
。
接下来,将伪 UAV 插槽添加到用于编译光线跟踪管道的全局根签名中。您不需要为此分配和/或绑定资源。下面是一个用粗体表示的假 UAV 插槽扩充 D3D12 示例代码的示例。应用程序中的根签名创建看起来可能会有很大不同。
// Global Root Signature // This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call. { CD3DX12_DESCRIPTOR_RANGE ranges[5]; // Perfomance TIP: Order from most frequent to least frequent. ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // output texture ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 0, 1); // static index buffers ranges[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 0, 2); // static vertex buffers ranges[3].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 3); // static vertex buffers // fake UAV for shader execution reordering ranges[4].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, NV_SHADER_EXTN_SLOT, NV_SHADER_EXTN_REGISTER_SPACE); CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignatureParams::Count]; rootParameters[GlobalRootSignatureParams::OutputViewSlot].InitAsDescriptorTable(1, &ranges[0]); rootParameters[GlobalRootSignatureParams::AccelerationStructureSlot].InitAsShaderResourceView(0); rootParameters[GlobalRootSignatureParams::SceneConstantSlot].InitAsConstantBufferView(0); rootParameters[GlobalRootSignatureParams::VertexBuffersSlot].InitAsDescriptorTable(3, &ranges[1]); rootParameters[GlobalRootSignatureParams::SerUavSlot].InitAsDescriptorTable(1, &ranges[4]); CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); SerializeAndCreateRaytracingRootSignature(globalRootSignatureDesc, &m_raytracingGlobalRootSignature); }
在着色器代码中使用 API
在着色器代码中,使用相同的值定义假 UAV 插槽并再次注册:
#define NV_SHADER_EXTN_SLOT u999999 // matches slot number in NvAPI_D3D12_SetNvShaderExtnSlotSpace #define NV_SHADER_EXTN_REGISTER_SPACE space999999 // matches space number in NvAPI_D3D12_SetNvShaderExtnSlotSpace #include "SER/nvHLSLExtns.h"
现在, SER API 可用于光线生成着色器:
NvHitObject hitObject = NvTraceRayHitObject(TLAS, RAY_FLAG_NONE, 0xff, 0, 1, 0, ray, payload); NvReorderThread(hitObject, 0, 0); NvInvokeHitObject(TLAS, hitObject, payload);
将 HLSL 编译为 DXIL 时,请执行以下操作之一:
1 ) 通过指定命令行参数-HV 2021
,确保在 DXC 中启用模板
或
2 ) 使用不需要模板的 API 宏版本。可以通过在#包括nvHLSLExtns.h
之前#定义NV_HITOBJECT_USE_MACRO_API
来启用宏版本。这适用于难以切换到 HLSL 2021 的传统代码库。如果代码库可以支持,建议使用模板。
Unreal Engine 5 Nv RTX 的集成
虚幻引擎开发人员可以利用虚幻引擎( NvRTX ) NVIDIA 分支中的 SER 。以下部分介绍 SER 如何在光线跟踪操作中提供性能提升,并提供针对特定用例的优化提示。
NVIDIA Unreal Engine 5 Nv RTX 5.0.3 版本将采用 SER 集成,以支持优化其许多光线跟踪路径。有了 SER , Nv RTX 开发人员将在 40 系列卡上看到额外的帧速率优化,光线跟踪操作速度提高 40% ,对质量或内容创作的影响为零。这提高了复杂光线跟踪计算的效率,并将在充分利用光线跟踪功能的场景中提供更大的增益。
SER 在 Unreal Engine 5 中的优势
Unreal Engine 5 ( UE5 )中的 SER 可以实现更好的脱机路径跟踪,可以说是 UE5 中最复杂的跟踪操作。同样,硬件光线跟踪反射和半透明(与材质和照明有复杂的交互作用)也将受益。
启用硬件光线跟踪时, SER 还可以提高流明性能。在某些情况下,实现这一点所需的更改(与初始系统复杂性无关)是微不足道的。在其他情况下,它增加了实质性的复杂性。下面将更详细地探讨三个不同的示例。
简单案例:路径跟踪
路径跟踪提供了一个高度分散的工作流,使其成为应用 SER 的一个很好的候选者。

应用 SER 可以减少路径跟踪器在材料评估中的分歧,而不仅仅是反弹次数。通过以下代码更改,性能提高了 20-50% :
#if !PATH_TRACER_USE_REORDERING // Trace the ray, including evaluation of hit data TraceRay( TLAS, RayFlags, RAY_TRACING_MASK_ALL, RAY_TRACING_SHADER_SLOT_MATERIAL, RAY_TRACING_NUM_SHADER_SLOTS, MissShaderIndex, PathState.Ray, PackedPayload); #else { NvHitObject Hit; // Trace ray to produce hit object NvTraceRayHitObject(TLAS, RayFlags, RAY_TRACING_MASK_ALL, RAY_TRACING_SHADER_SLOT_MATERIAL, RAY_TRACING_NUM_SHADER_SLOTS, MissShaderIndex, PathState.Ray, PackedPayload, Hit); // Reorder threads to have coherent hit evaluation NvReorderThread(Hit); // Evaluate hit data in the now coherent environment NvInvokeHitObject(TLAS, Hit, PackedPayload); } #endif
可以通过将 DXR TraceRay
函数替换为等效的NvTraceRayHitObject
、NvReorderThread
和[EZX29]集来实现此改进。一个关键的方面是,优化只是选择性地应用。此更改仅适用于 UE5 路径跟踪代码中的TraceTransparentRay
函数,因为这是大多数实质性评估分歧的根源。其他射线的操作成本较低,重新订购的重要性较低,因此它们可能不值得尝试重新订购的额外成本。
当谈到路径跟踪代码的潜力时,这个例子只是冰山一角。更仔细的分析几乎肯定会带来额外的收益,包括可能消除使用多次通过压缩更长光线的需要。
不同寻常的情况:流明全局照明中的工作压缩
通常,人们会考虑重新排序,以处理点击着色所经历的执行分歧。虽然流明全局照明中使用的光线跟踪过程不会运行发散命中着色器,但它们仍然受益于 SER 提供的机制。
对于大型场景,如 UE5 City Sample ,轨迹被分割到近场和远场,作为单独的跟踪过程运行,并在其间进行压缩。多次传递和压缩可以由单个NVReorderThread
调用代替。这避免了 GPU 上的闲置气泡,这些气泡用于压缩近场跟踪结果,然后发射远场射线。

消除存储、压缩和重新启动工作的额外开销通常值得节省 20% 。由于原始代码中的假设(使用宏而不是参数排列行为的函数),着色器更改可能会更加密集。然而,逻辑更改相当于添加了两个重新排序调用,用一个布尔表达式表示跟踪是否命中。
复杂情况:流明反射
流明是 UE5 中包含的一个系统,用于实现全局照明和反射。它具有高度的复杂性,对它的深入讨论远远超出了本文的范围。下面的描述经过了严格的提炼,重点介绍了一种特定的配置:启用硬件光线跟踪( HWRT )命中照明的流明反射。请注意, Lumen 还可以通过带符号的距离场利用软件光线跟踪,这将在这里不再讨论。
要渲染反射, Lumen HWRT 命中照明路径使用多个过程:
- 近场追踪 – 提取材料 ID
- 紧凑型光线
- 远场跟踪(可选) – 提取材料 ID
- 紧凑型光线
- 附加远场射线(可选)
- 按材质对光线排序
- 使用命中照明重新跟踪
此外,以下关于流明工作原理的重要细节有助于解释 SER 和非 SER 方法的差异。
- 内腔中的近场和远场分别对应于 TLAS 中靠近相机的物体和远离相机的物体的不同部分。近场和远场都包含在同一 TLAS 中。
- 在上述过程中使用了两条不同的光线跟踪管道。近场和远场都使用简化的(快速)跟踪路径,而命中照明具有完全的材质支持。这就是使用命中照明单独重新跟踪路径的原因。
有关这些焊道的更多技术细节,请参见 Lumen Technical Details 。启用 SER 后,可以组合传递,因为不再需要单独的压缩和排序阶段。该过程大致成为轨迹近场,如果不是命中轨迹远场,如果其中一个命中,则使用命中对象评估材质并执行照明。由于跟踪和着色的解耦,这是可能的。
着色器的相关部分如下所示:
NvHitObject SERHitObject; // Near field NvTraceRayHitObject(..., SERHitObject); NvReorderThread(SERHitObject); Result.bIsHit = SERHitObject.IsHit(); // Far field if (!Result.bIsHit) { // Transform ray into far field space of TLAS ... NvTraceRayHitObject(..., SERHitObject); NvReorderThread(SERHitObject); Result.bIsHit = SERHitObject.IsHit(); } // Compute result if (Result.bIsHit) { NvInvokeHitObject(Context.TLAS, SERHitObject, Payload); Result.Radiance = CalculateRayTracedLighting(); } // Handle miss
这是 SER 可用性的一个例子,它对渲染体系结构产生了更高层次的影响,而不仅仅是用相应的 NVAPI 等效物替换 TraceRay 。上述实施导致 GPU 上流明反射速度增加 20-30% ,这是在 UE5 城市样本中分析典型工作负载时测得的。
结论
在同时考虑数据和执行时,着色差异可能会带来性能问题。 Shader Execution Reordering API 为开发人员提供了一个功能强大的工具来减轻这些惩罚,只需相对较少的工作即可开始。上面讨论的优化只是将 SER 提供的可能性引入大型代码库(如 Unreal Engine 5 )的初始阶段。我们期待看到 SER 随着使用的发展发挥出更多的潜力。
其他资源
Read the Shader Execution Reordering whitepaper
Access the NVIDIA RTX Branch of Unreal Engine
Ask a question on the NVIDIA Developer Forum