DXIL之类型体系
DXIL之类型体系
DXIL 中大部分 LLVM 类型系统的构造都是合法的。基本类型
支持以下基本类型:
- void
- metadata
- i1, i8, i16, i32, i64
- half, float, double
SM6.0 假设原生硬件支持 i32 和 float 类型。
i8 仅在一些内置函数中用于表示掩码、枚举常量值或元数据,它不支持在着色器中进行内存访问或计算。
HLSL 中的 min12int、min16int 和 min16uint 数据类型被映射为 i16。
在 SM6.0 中,half 和 i16 被视为对应的 DXBC min-precision 类型(min16float、min16int/min16uint)。
HLSL 编译器优化器将 half、i16 和 i8 数据视为硬件原生支持的数据类型;也就是说,饱和度、范围裁剪以及 INF/NaN 处理都按照 IEEE 标准进行。这样的语义允许优化器重用 LLVM 优化 passes。
硬件对于双精度(double)的支持是可选的,并且由 RequiresHardwareDouble CAP 位进行保护。
硬件对于 i64 的支持是可选的,并且由 CAP 位进行保护。
向量 Vectors
HLSL 向量会被标量化。它们不参与计算,但可以在声明中存在,以将原始变量布局传达给工具、调试器和反射机制。 未来的 DXIL 可能会增加对于 <2 x half> 和 <2 x i16> 向量的支持,或是提供关于打包相关 half 和 i16 数量的提示。
矩阵 Matrics
矩阵会被降低为向量,并且不会被指令引用。它们可以在声明中存在,以将原始变量布局传达给工具、调试器和反射机制。
数组 Arrays
指令只能引用一维基本类型数组。然而,复杂的数组,例如多维数组或用户定义类型,可以存在以将基本变量布局传达给工具、调试器和反射机制。用户定义的类型
原始的 HLSL 用户自定义类型(UDT)会被降级,不会被指令引用。然而,它们可以在声明中存在,以将原始变量布局传达给工具、调试器和反射机制。某些资源操作会返回“分组”UDT,用于将多个返回值组合在一起;这样的UDT会立即被“解构”,转换为组件,然后被其他指令使用。类型转换
LLVM指令支持类型之间的显式转换。精度限定符
默认情况下,所有的浮点数 HLSL 操作被视为“快速”或非精确的操作。HLSL 和驱动程序编译器可以对这些操作进行重构。非精确的 LLVM 指令包括:fadd、fsub、fmul、fdiv、frem、fcmp,它们都标有“快速”数学标志。HLSL 中的 precise 类型限定符要求所有对值有贡献的操作在进行优化时都需要遵守 IEEE 标准。编译器开关 /Gis 隐式地将所有变量和值声明为 precise 类型。
精确行为在 LLVM 指令中以不设置“快速”数学标志的方式表示:fadd、fsub、fmul、fdiv、frem、fcmp。每个与计算精确值相关的调用指令都用 dx.precise 元数据进行注释,指示驱动程序编译器不得执行不符合 IEEE 标准的优化操作。
类型注释
在DXIL中,用户自定义类型可以用于为结构字段“附加”其他属性。例如,DXIL可以包含用于反射目的的结构和函数的类型注释。 namespace MyNameSpace { struct MyType { float field1; int2 field2; }; } float main(float col : COLOR) : SV_Target { ..... } !dx.typeAnnotations = !{!3, !7} !3 = !{i32 0, %"struct.MyNameSpace::MyType" undef, !4} !4 = !{i32 12, !5, !6} !5 = !{i32 6, !"field1", i32 3, i32 0, i32 7, i32 9} !6 = !{i32 6, !"field2", i32 3, i32 4, i32 7, i32 4} !7 = !{i32 1, void (float, float*)* @"main", !8} !8 = !{!9, !11, !14} !9 = !{i32 0, !10, !10} !10 = !{} !11 = !{i32 0, !12, !13} !12 = !{i32 4, !"COLOR", i32 7, i32 9} !13 = !{i32 0} !14 = !{i32 1, !15, !13} !15 = !{i32 4, !"SV_Target", i32 7, i32 9} !16 = !{null, !"lib.no::entry", null, null, null}
类型/字段注释元数据层次结构在DXIL中递归地模仿了LLVM类型层次结构。dx.typeAnnotations
是一种类型注释节点的元数据,其中每个节点代表特定类型的类型注释。
对于每个类型注释节点,第一个值表示注释的类型:
!3 = !{i32 0, %"struct.MyNameSpace::MyType" undef, !4} !7 = !{i32 1, void (float, float*)* @"main", !8}
!3是一个节点表示一种类型, !7是一种节点表示一种类型。
!dx.typeAnnotations = !{!3, !7}
Idx | type |
---|---|
0 | 结构体注释 |
1 | 函数注释 |
第二个值表示名称,第三个值是相应的类型元数据节点。
结构体注解从结构体大小(以字节为单位)开始,然后是字段注解列表:
!4 = !{i32 12, !5, !6} !5 = !{i32 6, !"field1", i32 3, i32 0, i32 7, i32 9} !6 = !{i32 6, !"field2", i32 3, i32 4, i32 7, i32 4}
!4中描述了结构体总大小是12字节,第一个字段放在!5中,第2个字段放在!6中。
字段注释是一系列由标签号和其值组成的对。字段注释对的定义如下:
Idx | Type |
---|---|
0 | SNorm |
1 | UNorm |
2 | Matrix |
3 | Buffer Offset |
4 | Semantic String |
5 | Interpolation Mode |
6 | Field Name |
7 | Component Type |
8 | Precise |
函数注解是一系列的参数注解:
!7 = !{i32 1, void (float, float*)* @"main", !8} !8 = !{!9, !11, !14}
每个参数注解包含输入/输出类型,字段注释和语义索引:
!9 = !{i32 0, !10, !10} !10 = !{} !11 = !{i32 0, !12, !13} !12 = !{i32 4, !"COLOR", i32 7, i32 9} !13 = !{i32 0} !14 = !{i32 1, !15, !13} !15 = !{i32 4, !"SV_Target", i32 7, i32 9}
【推荐】国内首个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吗?