DXIL 之着色器参数和签名
DXIL 之着色器参数和签名
本节将规范了在DXIL中如何表达HLSL着色器的输入和输出参数。HLSL的签名和语义
在HLSL中,着色器入口函数的形式参数指定了着色器如何与图形管线进行交互。输入参数作为输入签名指定了着色器接收的值。输出参数作为输出签名指定了着色器产生的值。着色器编译器将HLSL的输入和输出签名映射到符合Direct3D功能规范中硬件约束的DXIL规范中。DXIL规范也被称为签名。签名映射是一个复杂的过程,因为有许多限制条件。所有签名参数必须适应有限的N个4x32位寄存器空间。出于效率的考虑,参数被组合在一起,以符合规范的约束条件,这个过程称为签名打包(signature packing)。大多数签名都是紧密打包的;然而,顶点着色器(VS)的输入签名并不是打包的,因为其值来自于输入装配器(IA)阶段而非图形管线。相反地,像素着色器(PS)的输出签名则会分配对齐,以使得SV_Target语义索引与输出寄存器索引对齐。
每个HLSL签名参数都通过类似C语言类型、插值模式和语义名称和索引进行定义。类型定义了参数的形状,可能非常复杂。插值模式增加了打包的约束条件,即打包在一起的参数必须具有兼容的插值模式。语义是与参数关联的额外名称,用于以下目的:(1)指定一个参数是否为特殊的系统值(SV),(2)将参数链接到IA或StreamOut API流,以及(3)帮助调试。语义索引用于消除使用相同语义名称的参数之间的歧义,或跨越多个寄存器空间的行的参数之间的歧义。
SV(System value)语义为相关参数添加特定的含义和约束。参数可以由硬件提供,这时被称为系统生成值(SGV)。另外,参数也可以由硬件进行解释,这时被称为系统解释值(SIV)。SGV和SIV是与管线阶段相关的;此外,有些参与签名打包,而有些则不参与。非SV语义始终参与签名打包。
大多数系统生成值(SGV)使用特殊的Dxil内部函数加载,而不是从签名中加载输入。它们通常在签名中根本不存在。可以通过声明和使用特殊的内部函数来检测它们的存在。只有少数例外情况。其中一种情况是,它们存在并且从签名中加载,而不是从特殊内部函数加载,因为它们必须是可能从前一个阶段传递的打包签名的一部分,允许前一个阶段覆盖这些值,例如用于几何着色器中的SV_PrimitiveID和SV_IsFrontFace。在另一种情况下,它们标识仍然为DXBC签名做出贡献以提供信息的签名元素,但仅使用特殊的内部函数来读取值,例如GS输入中的SV_PrimitiveID和PS输入中的SampleIndex。
对于各种签名位置中的不同系统值,其行为分类通过SemanticKind和SigPointKind组织的表进行描述。SigPointKind是一种新的分类,唯一地标识每个入口点可能的输入或输出参数集合。对于每个SemanticKind和SigPointKind的组合,都有一个SemanticInterpretationKind定义了该位置的处理类别。
在SigPointKind中,签名点(Signature Points)被枚举如下:
ID | SigPoint | Related | ShaderKind | PackingKind | SignatureKind | Description |
---|---|---|---|---|---|---|
0 | VSIn | Invalid | Vertex | InputAssembler | Input | Ordinary Vertex Shader input from Input Assembler |
1 | VSOut | Invalid | Vertex | Vertex | Output | Ordinary Vertex Shader output that may feed Rasterizer |
2 | PCIn | HSCPIn | Hull | None | Invalid | Patch Constant function non-patch inputs |
3 | HSIn | HSCPIn | Hull | None | Invalid | Hull Shader function non-patch inputs |
4 | HSCPIn | Invalid | Hull | Vertex | Input | Hull Shader patch inputs - Control Points |
5 | HSCPOut | Invalid | Hull | Vertex | Output | Hull Shader function output - Control Point |
6 | PCOut | Invalid | Hull | PatchConstant | PatchConstOrPrim | Patch Constant function output - Patch Constant data passed to Domain Shader |
7 | DSIn | Invalid | Domain | PatchConstant | PatchConstOrPrim | Domain Shader regular input - Patch Constant data plus system values |
8 | DSCPIn | Invalid | Domain | Vertex | Input | Domain Shader patch input - Control Points |
9 | DSOut | Invalid | Domain | Vertex | Output | Domain Shader output - vertex data that may feed Rasterizer |
10 | GSVIn | Invalid | Geometry | Vertex | Input | Geometry Shader vertex input - qualified with primitive type |
11 | GSIn | GSVIn | Geometry | None | Invalid | Geometry Shader non-vertex inputs (system values) |
12 | GSOut | Invalid | Geometry | Vertex | Output | Geometry Shader output - vertex data that may feed Rasterizer |
13 | PSIn | Invalid | Pixel | Vertex | Input | Pixel Shader input |
14 | PSOut | Invalid | Pixel | Target | Output | Pixel Shader output |
15 | CSIn | Invalid | Compute | None | Invalid | Compute Shader input |
16 | MSIn | Invalid | Mesh | None | Invalid | Mesh Shader input |
17 | MSOut | Invalid | Mesh | Vertex | Output | Mesh Shader vertices output |
18 | MSPOut | Invalid | Mesh | Vertex | PatchConstOrPrim | Mesh Shader primitives output |
19 | ASIn | Invalid | Amplification | None | Invalid | Amplification Shader input |
语义解释的分类如下(SemanticInterpretationKind):
ID | Name | Description |
---|---|---|
0 | NA | Not Available |
1 | SV | Normal System Value |
2 | SGV | System Generated Value (sorted last) |
3 | Arb | Treated as Arbitrary |
4 | NotInSig | Not included in signature (intrinsic access) |
5 | NotPacked | Included in signature, but does not contribute to packing |
6 | Target | Special handling for SV_Target |
7 | TessFactor | Special handling for tessellation factors |
8 | Shadow | Shadow element must be added to a signature for compatibility |
8 | ClipCull | Special packing rules for SV_ClipDistance or SV_CullDistance |
在每个SigPointKind下,每个SemanticKind的语义解释如下:
Semantic | VSIn | VSOut | PCIn | HSIn | HSCPIn | HSCPOut | PCOut | DSIn | DSCPIn | DSOut | GSVIn | GSIn | GSOut | PSIn | PSOut | CSIn | MSIn | MSOut | MSPOut | ASIn |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Arbitrary | Arb | Arb | NA | NA | Arb | Arb | Arb | Arb | Arb | Arb | Arb | NA | Arb | Arb | NA | NA | NA | Arb | Arb | NA |
VertexID | SV | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
InstanceID | SV | Arb | NA | NA | Arb | Arb | NA | NA | Arb | Arb | Arb | NA | Arb | Arb | NA | NA | NA | NA | NA | NA |
Position | Arb | SV | NA | NA | SV | SV | Arb | Arb | SV | SV | SV | NA | SV | SV | NA | NA | NA | SV | NA | NA |
RenderTargetArrayIndex | Arb | SV | NA | NA | SV | SV | Arb | Arb | SV | SV | SV | NA | SV | SV | NA | NA | NA | NA | SV | NA |
ViewPortArrayIndex | Arb | SV | NA | NA | SV | SV | Arb | Arb | SV | SV | SV | NA | SV | SV | NA | NA | NA | NA | SV | NA |
ClipDistance | Arb | ClipCull | NA | NA | ClipCull | ClipCull | Arb | Arb | ClipCull | ClipCull | ClipCull | NA | ClipCull | ClipCull | NA | NA | NA | ClipCull | NA | NA |
CullDistance | Arb | ClipCull | NA | NA | ClipCull | ClipCull | Arb | Arb | ClipCull | ClipCull | ClipCull | NA | ClipCull | ClipCull | NA | NA | NA | ClipCull | NA | NA |
OutputControlPointID | NA | NA | NA | NotInSig | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
DomainLocation | NA | NA | NA | NA | NA | NA | NA | NotInSig | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
PrimitiveID | NA | NA | NotInSig | NotInSig | NA | NA | NA | NotInSig | NA | NA | NA | Shadow | SGV | SGV | NA | NA | NA | NA | SV | NA |
GSInstanceID | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig | NA | NA | NA | NA | NA | NA | NA | NA |
SampleIndex | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | Shadow _41 | NA | NA | NA | NA | NA | NA |
IsFrontFace | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | SGV | SGV | NA | NA | NA | NA | NA | NA |
Coverage | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig _50 | NotPacked _41 | NA | NA | NA | NA | NA |
InnerCoverage | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig _50 | NA | NA | NA | NA | NA | NA |
Target | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | Target | NA | NA | NA | NA | NA |
Depth | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotPacked | NA | NA | NA | NA | NA |
DepthLessEqual | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotPacked _50 | NA | NA | NA | NA | NA |
DepthGreaterEqual | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotPacked _50 | NA | NA | NA | NA | NA |
StencilRef | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotPacked _50 | NA | NA | NA | NA | NA |
DispatchThreadID | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig | NotInSig | NA | NA | NotInSig |
GroupID | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig | NotInSig | NA | NA | NotInSig |
GroupIndex | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig | NotInSig | NA | NA | NotInSig |
GroupThreadID | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig | NotInSig | NA | NA | NotInSig |
TessFactor | NA | NA | NA | NA | NA | NA | TessFactor | TessFactor | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
InsideTessFactor | NA | NA | NA | NA | NA | NA | TessFactor | TessFactor | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
ViewID | NotInSig _61 | NA | NotInSig _61 | NotInSig _61 | NA | NA | NA | NotInSig _61 | NA | NA | NA | NotInSig _61 | NA | NotInSig _61 | NA | NA | NotInSig | NA | NA | NA |
Barycentrics | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotPacked _61 | NA | NA | NA | NA | NA | NA |
ShadingRate | NA | SV _64 | NA | NA | SV _64 | SV _64 | NA | NA | SV _64 | SV _64 | SV _64 | NA | SV _64 | SV _64 | NA | NA | NA | NA | SV | NA |
CullPrimitive | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NotInSig | NA | NA | NA | NA | NotPacked | NA |
以下是在本节中用于说明的一个顶点着色器示例:
struct Foo { float a; float b[2]; }; struct VSIn { uint vid : SV_VertexID; float3 pos : Position; Foo foo[3] : SemIn1; float f : SemIn10; }; struct VSOut { float f : SemOut1; Foo foo[3] : SemOut2; float4 pos : SV_Position; }; void main(in VSIn In, // input signature out VSOut Out) // output signature { ... }
签名打包必须高效。它应该尽量使用较少的寄存器,而且打包算法应该在合理的时间内运行。复杂之处在于该问题是NP完全问题,算法需要采用启发式方法来解决。
虽然当前不需要详细了解打包算法的细节,但重要的是概述一些与在DXIL中表示打包签名相关的概念。由于C/C++类型系统引发的参数形状复杂性,打包变得更加复杂。在上面的示例中,Out.foo数组字段的各个字段实际上也是数组,在内存中跨度排序。有效地分配这种跨度形状是困难的。为了简化打包过程,第一步是将用户自定义的(结构体)参数拆分为组成部分,并使跨度数组连续。这个准备步骤使算法能够操作稠密的矩形形状,我们称之为签名元素。在上述示例中,输出签名具有以下元素:float Out_f、float Out_foo_a[3]、float Out_foo_b[2][3]和float4 pos。每个元素都由行数和列数来描述。它们分别是1x1、3x1、6x1和1x4。打包算法的目标是将这些元素适应Nx4的寄存器空间,并满足所有打包兼容性约束。
签名元素记录
在DXIL中,每个签名元素都被表示为元数据记录。对于上面的示例输出签名,元素记录如下所示:
; element ID, semantic name, etype, sv, s.idx, interp, rows, cols, start row, col, ext. list !20 = !{i32 6, !"SemOut", i8 0, i8 0, !40, i8 2, i32 1, i8 1, i32 1, i8 2, null} !21 = !{i32 7, !"SemOut", i8 0, i8 0, !41, i8 2, i32 3, i8 1, i32 1, i8 1, null} !22 = !{i32 8, !"SemOut", i8 0, i8 0, !42, i8 2, i32 6, i8 1, i32 1, i8 0, null} !23 = !{i32 9, !"SV_Position", i8 0, i8 3, !43, i8 2, i32 1, i8 4, i32 0, i8 0, null}
每个记录包含以下字段:
Idx | Type | Description |
---|---|---|
0 | i32 | Unique signature element record ID, used to identify the element in operations. |
1 | String metadata | Semantic name. |
2 | i8 | ComponentType (enum value). |
3 | i8 | SemanticKind (enum value). |
4 | Metadata | Metadata list that enumerates all semantic indexes of the flattened parameter. |
5 | i8 | InterpolationMode (enum value). |
6 | i32 | Number of element rows. |
7 | i8 | Number of element columns. |
8 | i32 | Starting row of element packing location. |
9 | i8 | Starting column of element packing location. |
10 | Metadata | Metadata list of additional tag-value pairs; can be 'null' or empty. |
最后的元数据列表用于指定附加属性和未来扩展。
签名记录元数据
典型情况下,着色器通常有两个签名:输入和输出,而域着色器有一个额外的补丁常量签名。这些签名由签名元素记录组成,并附加到着色器入口元数据中。下面的示例将解释元数据的详细信息。Vertex shader HLSL
以下是上述顶点着色器的HLSL代码。语义索引的分配将在下面的部分中解释:
struct Foo { float a; float b[2]; }; struct VSIn { uint vid : SV_VertexID; float3 pos : Position; Foo foo[3] : SemIn1; // semantic index assignment: // foo[0].a : SemIn1 // foo[0].b[0] : SemIn2 // foo[0].b[1] : SemIn3 // foo[1].a : SemIn4 // foo[1].b[0] : SemIn5 // foo[1].b[1] : SemIn6 // foo[2].a : SemIn7 // foo[2].b[0] : SemIn8 // foo[2].b[1] : SemIn9 float f : SemIn10; }; struct VSOut { float f : SemOut1; Foo foo[3] : SemOut2; // semantic index assignment: // foo[0].a : SemOut2 // foo[0].b[0] : SemOut3 // foo[0].b[1] : SemOut4 // foo[1].a : SemOut5 // foo[1].b[0] : SemOut6 // foo[1].b[1] : SemOut7 // foo[2].a : SemOut8 // foo[2].b[0] : SemOut9 // foo[2].b[1] : SemOut10 float4 pos : SV_Position; }; void main(in VSIn In, // input signature out VSOut Out) // output signature { ... }
输入签名被打包为与IA阶段兼容的形式。打包算法必须为输入签名元素分配以下起始位置:
Input element | Rows | Columns | Start row | Start column |
---|---|---|---|---|
uint VSIn.vid | 1 | 1 | 0 | 0 |
float3 VSIn.pos | 1 | 3 | 1 | 0 |
float VSIn.foo.a[3] | 3 | 1 | 2 | 0 |
float VSIn.foo.b[6] | 6 | 1 | 5 | 0 |
float VSIn.f | 1 | 1 | 11 | 0 |
Input element | Rows | Columns | Start row | Start column |
---|---|---|---|---|
uint VSOut.f | 1 | 1 | 1 | 2 |
float VSOut.foo.a[3] | 3 | 1 | 1 | 1 |
float VSOut.foo.b[6] | 6 | 1 | 1 | 0 |
float VSOut.pos | 1 | 4 | 0 | 0 |
语义索引分配
DXIL中的语义索引分配与DXBC完全相同。语义索引分配,简写为s.idx,是对所有具有相同语义名称的字段进行连续枚举,就像签名被打包用于IA阶段一样。也就是说,给定一个复杂的签名元素,例如VSOut的foo[3],它的语义名称是SemOut,起始索引为2,该元素被展开为单独的字段:foo[0].a,foo[0].b[0],...,foo[2].b[1],并且这些字段分别获得连续的语义索引2、3、...、10。语义索引对用于设置IA阶段和通过StreamOut API捕获单个签名寄存器的值。
用于VS签名的DXIL
相应的DXIL元数据如下所示:
!dx.entryPoints = !{ !1 } !1 = !{ void @main(), !"main", !2, null, null } ; Signatures: In, Out, Patch Constant (optional) !2 = !{ !3, !4, null } ; Input signature (packed accordiong to IA rules) !3 = !{ !10, !11, !12, !13, !14 } ; element idx, semantic name, etype, sv, s.idx, interp, rows, cols, start row, col, ext. list !10 = !{i32 1, !"SV_VertexID", i8 0, i8 1, !30, i32 0, i32 1, i8 1, i32 0, i8 0, null} !11 = !{i32 2, !"Position", i8 0, i8 0, !30, i32 0, i32 1, i8 3, i32 1, i8 0, null} !12 = !{i32 3, !"SemIn", i8 0, i8 0, !32, i32 0, i32 3, i8 1, i32 2, i8 0, null} !13 = !{i32 4, !"SemIn", i8 0, i8 0, !33, i32 0, i32 6, i8 1, i32 5, i8 0, null} !14 = !{i32 5, !"SemIn", i8 0, i8 0, !34, i32 0, i32 1, i8 1, i32 11, i8 0, null} ; semantic index assignment: !30 = !{ i32 0 } !32 = !{ i32 1, i32 4, i32 7 } !33 = !{ i32 2, i32 3, i32 5, i32 6, i32 8, i32 9 } !34 = !{ i32 10 } ; Output signature (tightly packed according to pipeline stage packing rules) !4 = !{ !20, !21, !22, !23 } ; element ID, semantic name, etype, sv, s.idx, interp, rows, cols, start row, col, ext. list !20 = !{i32 6, !"SemOut", i8 0, i8 0, !40, i32 2, i32 1, i8 1, i32 1, i8 2, null} !21 = !{i32 7, !"SemOut", i8 0, i8 0, !41, i32 2, i32 3, i8 1, i32 1, i8 1, null} !22 = !{i32 8, !"SemOut", i8 0, i8 0, !42, i32 2, i32 6, i8 1, i32 1, i8 0, null} !23 = !{i32 9, !"SV_Position", i8 0, i8 3, !43, i32 2, i32 1, i8 4, i32 0, i8 0, null} ; semantic index assignment: !40 = !{ i32 1 } !41 = !{ i32 2, i32 5, i32 8 } !42 = !{ i32 3, i32 4, i32 6, i32 7, i32 9, i32 10 } !43 = !{ i32 0 }
细分着色器的例子 Hull Shader example
一个曲面细分着色器(Hull Shader,HS)由两个入口点函数定义:控制点(Control Point,CP)函数用于计算控制点,以及片元常量(Patch Constant,PC)函数用于计算片元常量数据,包括细分因子。这两个函数的输入都是整个片元的输入控制点,因此每个元素可以通过行索引,并且还可以通过顶点索引进行索引。
以下是一个HS示例的入口点元数据和签名列表:
; !105 is extended parameter list containing reference to HS State: !101 = !{ void @HSMain(), !"HSMain", !102, null, !105 } ; Signatures: In, Out, Patch Constant !102 = !{ !103, !104, !204 }
入口点记录指定了以下内容:(1) CP函数HSMain作为主要符号,以及(2) 通过可选的元数据节点!105指定的PC函数。
CP输入签名描述一个输入控制点:
!103 = !{ !110, !111 } ; element ID, semantic name, etype, sv, s.idx, interp, rows, cols, start row, col, ext. list !110= !{i32 1, !"SV_Position", i8 0, i8 3, !130, i32 0, i32 1, i8 4, i32 0, i8 0, null} !111= !{i32 2, !"array", i8 0, i8 0, !131, i32 0, i32 4, i8 3, i32 1, i8 0, null} ; semantic indexing for flattened elements: !130 = !{ i32 0 } !131 = !{ i32 0, i32 1, i32 2, i32 3 }
请注意,SV_OutputControlPointID和SV_PrimitiveID输入元素是通过特殊的Dxil内置函数加载的SGV,它们在签名中根本不存在。它们具有SemanticInterpretationKind::NotInSig的语义解释。
CP输出签名描述一个输出控制点:
!104 = !{ !120, !121 } ; element ID, semantic name, etype, sv, s.idx, interp, rows, cols, start row, col, ext. list !120= !{i32 3, !"SV_Position", i8 0, i8 3, !130, i32 0, i32 1, i8 4, i32 0, i8 0, null} !121= !{i32 4, !"array", i8 0, i8 0, !131, i32 0, i32 4, i8 3, i32 1, i8 0, null}
Hull着色器需要一个扩展参数来定义额外的状态:
; extended parameter HS State !105 = !{ i32 3, !201 } ; HS State record defines patch constant function and other properties ; Patch Constant Function, in CP count, out CP count, tess domain, tess part, out prim, max tess factor !201 = !{ void @PCMain(), 4, 4, 3, 1, 3, 16.0 }
PC-output signature:
!204 = !{ !220, !221, !222 } ; element ID, semantic name, etype, sv, s.idx, interp, rows, cols, start row, col, ext. list !220= !{i32 3, !"SV_TessFactor", i8 0, i8 25, !130, i32 0, i32 4, i8 1, i32 0, i8 3, null} !221= !{i32 4, !"SV_InsideTessFactor", i8 0, i8 26, !231, i32 0, i32 2, i8 1, i32 4, i8 3, null} !222= !{i32 5, !"array", i8 0, i8 0, !131, i32 0, i32 4, i8 3, i32 0, i8 0, null} ; semantic indexing for flattened elements: !231 = !{ i32 0, i32 1 }
在操作中访问签名值
没有与签名元素对应的函数参数或变量。而是使用loadInput和storeOutput函数来在操作中访问签名元素的值。这些访问是标量的。以下是操作的签名:
; overloads: SM5.1: f16|f32|i16|i32, SM6.0: f16|f32|f64|i8|i16|i32|i64 declare float @dx.op.loadInput.f32( i32, ; opcode i32, ; input ID i32, ; row (relative to start row of input ID) i8, ; column (relative to start column of input ID), constant in [0,3] i32) ; vertex index ; overloads: SM5.1: f16|f32|i16|i32, SM6.0: f16|f32|f64|i8|i16|i32|i64 declare void @dx.op.storeOutput.f32( i32, ; opcode i32, ; output ID i32, ; row (relative to start row of output ID) i8, ; column (relative to start column of output ID), constant in [0,3] float) ; value to store
LoadInput/storeOutput接受input/output元素ID,该ID是签名元数据记录的唯一标识。row参数是从元素开始的数组元素行索引;寄存器索引是通过将元素的起始行和行参数值相加来获得的。类似地,column参数是相对列索引;打包的寄存器组件是通过将元素的起始列(打包列)和列值相加得到的。存在多个重载函数来访问不同原始类型的元素。LoadInput还接受一个额外的顶点索引参数,该参数表示DS CP-inputs和GS inputs的顶点索引;在其他情况下,顶点索引必须为undef(未定义)。
签名打包
根据运行时的限制,签名元素必须打包到N个4-32位寄存器的空间中。DXIL包含了打包后的签名。打包算法比DX11更加激进。然而,DXIL的打包只是对驱动程序实现的建议。驱动程序编译器可以根据需要重新排列签名元素,同时保持连接的流水线阶段的兼容性。DXIL被设计成易于“重定位”签名元素,因为loadInput/storeOutput的行和列索引不需要改变,因为它们相对于每个元素的起始行/列而言。
签名打包类型
两个流水线阶段可以以四种不同的方式连接,从而产生四种打包类型。
-
输入汇编(Input Assembly):仅有VS输入。所有元素都映射到唯一的寄存器,它们不能被打包在一起。不使用插值模式。
-
连接到光栅化器(Connects to Rasterizer):包括VS输出、HS CP-input/output和PC-input、DS CP-input/output、GS input/output、PS input。 根据约束、元素可以被打包在一起。 使用插值模式,并且连接的签名之间必须保持一致。 虽然HS CP-output和DS CP-input签名不经过光栅化器,但它们仍然被视为如此。原因是HS CP-input和HS CP-output在通过的HS情况下,必须具有相同的打包方式以提高效率。
-
片元常量(Patch Constant): 包括HS PC-output和DS PC-inout。 SV_TessFactor和SV_InsideTessFactor是唯一在此处相关的系统值(SVs),并且这是它们唯一合法的位置。它们具有特殊的打包考虑。 不使用插值模式。
-
Pixel Shader Output:只有PS 输出。只有SV_Target映射到输出寄存器空间。不执行任何打包操作,语义索引对应于渲染目标的索引。
打包约束
在DXIL中,打包算法比DXBC更严格和更激进,尽管仍然兼容。特别是,数组签名元素不会被拆分为标量,即使每个数组访问可以通过文字索引消除歧义。DXIL和DXBC签名的打包方式并不相同,因此跨编译器版本将它们链接到单个流水线中是不被支持的。
签名元素的行维度表示索引范围。如果约束允许,两个相邻或重叠的索引范围将合并为一个单独的索引范围。
打包约束如下:
1.一个寄存器的所有4个分量必须具有相同的插值模式
2.包含系统值(SVs)的寄存器分量必须位于不包含系统值的分量的右侧。
3.SV_ClipDistance 和 SV_CullDistance 有额外的约束条件:a. 它们可以一起打包。b. 必须占用最多2个寄存器(8 个分量)。c. SV_ClipDistance 必须使用线性插值模式。
- 包含系统值(SVs)的寄存器不能在索引范围内,但例外情况是Tessellation Factors(TessFactors)。
5.如果索引范围R1与TessFactor索引范围R2重叠,那么R1必须完全包含在R2内。因此,当打包时,外部和内部TessFactors占据不相交的索引范围。
-
如果非TessFactor索引范围重叠,它们会被合并为一个更大的范围。
-
所有非SGV(系统几何值)被打包后,SGV必须被打包。如果有多个SGV,则按照HLSL声明的顺序进行打包。
为SGV打包
两个连接签名的非SGV部分必须匹配;但是,SGV部分不需要匹配。一个例子是像在PS中声明SV_PrimitiveID作为输入。如果VS连接到PS,PS的SV_PrimitiveID值将由硬件合成;此外,在VS中输出SV_PrimitiveID是不允许的。如果GS连接到PS,GS可以将SV_PrimitiveID声明为其输出。
不幸的是,SGV(系统几何值)规范对于连接着色器的分离编译造成了一些复杂性。例如,GS输出SV_PrimitiveID,而PS按顺序输入SV_IsFrontFace和SV_PrimitiveID。在GS和PS签名中,SV_PrimitiveID的位置是不兼容的。在SM5.0及之前的版本中,对于这种模糊性,很难进行更多的处理;程序员将不得不依赖SDKLayers来捕捉潜在的不匹配情况。
SM5.1及更高版本的着色器在使用PSO对象描述流水线状态的D3D12+运行时上工作。因此,驱动编译器在编译过程中可以访问连接着色器,尽管HLSL编译器不能。驱动编译器可以轻松解决签名中SGV歧义的问题。对于SM5.1及更高版本,HLSL编译器将确保声明的SGV适合于打包的签名;然而,它会将SGV的起始行列位置设置为(-1, 0),以便驱动编译器必须在PSO编译期间解决SGV的放置问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?