HLSL 入口函数

用法

在 HLSL 中,入口函数表示着色器执行的起点。它们必须在编译时已知。对于所有非库着色器,编译器假设默认入口函数名为 main,除非提供了 DXC /E 选项来指定备用入口点。对于库着色器,使用 [shader(...)] 属性来表示入口点。

入口函数的所有标量参数都必须具有语义注释,所有结构体参数都必须在结构体声明中的每个字段上具有语义注释。此外,如果入口函数具有返回类型,则也必须为返回类型提供语义注释。

HLSL 入口函数可以从着色器的其他部分调用,这会影响代码生成。

实现细节

在 Clang 中,DXC /E 选项被转换为 cc1 标志 -hlsl-entry,它反过来将 HLSLShader 属性应用于具有指定名称的函数。这允许入口函数的代码生成始终根据 HLSLShader 属性的存在来进行,无论您正在编译哪个着色器配置文件。

在代码生成中,会生成两个函数。一个是用户定义的函数,它被生成为具有内部链接的经过名称修饰的 C++ 函数,遵循正常的函数代码生成。

可以被 GPU 驱动程序调用的实际导出入口函数是一个未经名称修饰的 void(void) 函数。在代码生成中,我们生成未经名称修饰的入口函数作为实际的着色器入口。着色器入口函数用 hlsl.shader 函数属性进行注释,该属性标识入口的管道阶段。

未经名称修饰的入口函数的函数体首先包含对执行全局构造函数的调用,然后是用户定义的入口参数的实例,其中填充了它们的语义值,以及对用户定义的函数的调用。在调用指令之后,返回值(如果有)使用适用于存储输出的目标适当的内联函数进行保存(对于 DirectX,为 llvm.dx.store.output)。最后,任何存在的全局析构函数将在返回之前立即被调用。HLSL 不支持 C++ atexit 注册,而是对全局析构函数的调用是编译时生成的。

注意

Clang 中的 HLSL 支持目前侧重于计算着色器,它们不支持输出语义。在支持其他着色器配置文件之前,不会实现对输出语义的支持。

以下是表示计划实现的示例 IR,可能会随着 llvm.dx.store.outputllvm.dx.load.input 内联函数的实现而发生变化。

; Function Attrs: norecurse
define void @main() #1 {
   entry:
   %0 = call i32 @llvm.dx.load.input.i32(...)
   %1 = call i32 @"?main@@YAXII@Z"(i32 %0)
   call @llvm.dx.store.output.i32(%1, ...)
   ret void
}