3 月 19 日下午 2 点,锁定 NVIDIA AI 网络中文专场。立即注册观看
内容创建/渲染

通过着色器执行重新排序提高着色器性能和游戏内帧速率

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 的一个很好的候选者。

An image showing Grandma’s Kitchen by Richard Cowgill, path traced in Unreal Engine 5.
Figure 1. Grandma’s Kitchen 作者 Richard Cowgill ,在 Unreal Engine 5 中跟踪路径。对于这个视图,使用 SER 跟踪路径的速度要快 40% 。

应用 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函数替换为等效的NvTraceRayHitObjectNvReorderThread和[EZX29]集来实现此改进。一个关键的方面是,优化只是选择性地应用。此更改仅适用于 UE5 路径跟踪代码中的TraceTransparentRay函数,因为这是大多数实质性评估分歧的根源。其他射线的操作成本较低,重新订购的重要性较低,因此它们可能不值得尝试重新订购的额外成本。

当谈到路径跟踪代码的潜力时,这个例子只是冰山一角。更仔细的分析几乎肯定会带来额外的收益,包括可能消除使用多次通过压缩更长光线的需要。

不同寻常的情况:流明全局照明中的工作压缩

通常,人们会考虑重新排序,以处理点击着色所经历的执行分歧。虽然流明全局照明中使用的光线跟踪过程不会运行发散命中着色器,但它们仍然受益于 SER 提供的机制。

对于大型场景,如 UE5 City Sample ,轨迹被分割到近场和远场,作为单独的跟踪过程运行,并在其间进行压缩。多次传递和压缩可以由单个NVReorderThread调用代替。这避免了 GPU 上的闲置气泡,这些气泡用于压缩近场跟踪结果,然后发射远场射线。

A table with two columns, 'without SER" and 'with SER'

消除存储、压缩和重新启动工作的额外开销通常值得节省 20% 。由于原始代码中的假设(使用宏而不是参数排列行为的函数),着色器更改可能会更加密集。然而,逻辑更改相当于添加了两个重新排序调用,用一个布尔表达式表示跟踪是否命中。

复杂情况:流明反射

流明是 UE5 中包含的一个系统,用于实现全局照明和反射。它具有高度的复杂性,对它的深入讨论远远超出了本文的范围。下面的描述经过了严格的提炼,重点介绍了一种特定的配置:启用硬件光线跟踪( HWRT )命中照明的流明反射。请注意, Lumen 还可以通过带符号的距离场利用软件光线跟踪,这将在这里不再讨论。

要渲染反射, Lumen HWRT 命中照明路径使用多个过程:

  1. 近场追踪 – 提取材料 ID
  1. 紧凑型光线
  1. 远场跟踪(可选) – 提取材料 ID
  1. 紧凑型光线
  1. 附加远场射线(可选)
  1. 按材质对光线排序
  1. 使用命中照明重新跟踪

此外,以下关于流明工作原理的重要细节有助于解释 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 随着使用的发展发挥出更多的潜力。

其他资源

Download NVAPI to access SER

Read the Shader Execution Reordering whitepaper

Access the NVIDIA RTX Branch of Unreal Engine

Learn more about Vulkan

Ask a question on the NVIDIA Developer Forum

 

标签