变量语法
使用以下语法规则声明 HLSL 变量。
[Storage_Class] [Type_Modifier] 类型名称[Index] [: Semantic] [: Packoffset] [: Register]; [Annotations] [= Initial_Value]
参数
-
Storage_Class
-
可选的存储类修饰符,为编译器提供有关变量作用域和生存期的提示;可以以任何顺序指定修饰符。
值 说明 extern 将全局变量标记为着色器的外部输入;这是所有全局变量的默认标记。 不能与 static 组合。 nointerpolation 在将顶点着色器的输出传递给像素着色器之前,不要对其进行插值。 precise 当应用于变量时,precise 关键字将以以下方式限制用于生成分配给该变量的值的任何计算: - 单独的操作保持独立。 例如,当 mul 和 add 操作已融合为 mad 操作时,precise 会强制这些操作保持独立。 相反,你必须明确地使用 mad 内部函数。
- 维护操作顺序。 在指令顺序可能被打乱以提高性能的情况下,precise 确保编译器保持所写的顺序。
- IEEE 不安全操作受到限制。 如果编译器可能使用了不考虑 NaN(非数字)和 INF(无限)值的快速数学运算,则 precise 强制遵守有关 NaN 和 INF 值的 IEEE 要求。 如果没有 precise,这些优化和数学运算就不是 IEEE 安全的。
- 限定变量 precise 不会使使用变量的操作变为 precise。 由于 precise 仅传播到对分配给 precise 限定变量的值有贡献的操作,因此正确进行所需的计算 precise 可能很棘手,因此我们建议你在声明着色器输出 precise 的地方直接标记它们,无论是在结构字段、输出参数还是条目函数的返回类型上。 以这种方式控制优化的能力通过禁用可能由于累积精度差异而影响最终结果的优化来保持修改后的输出变量的结果不变性。 当你希望曲面细分的着色器保持水密补丁接缝或在多个过程中匹配深度值时,这很有用。 示例代码:
HLSLmatrix g_mWorldViewProjection;
void main(in float3 InPos : Position, out precise float4 OutPos : SV_Position)
"
// 操作是精确的,因为它提供了精确的参数 OutPos
OutPos = mul( float4( InPos, 1.0 ), g_mWorldViewProjection );
}
shared 标记一个变量以在效果之间共享;这是对编译器的一个提示。 groupshared 为计算着色器的线程组共享内存标记一个变量。 在 D3D10 中,具有组共享存储类的所有变量的最大总大小为 16kb;在 D3D11 中,最大大小为 32kb。 请参阅示例。 static 标记一个局部变量,使其仅初始化一次,并在函数调用之间保持不变。 如果声明不包含初始值设定项,则该值设置为零。 标记为 static 的全局变量对应用程序不可见。 uniform 标记一个变量,该变量的数据在着色器的整个执行过程中是恒定的(例如顶点着色器中的材质颜色);默认情况下,全局变量被视为统一。 volatile 标记一个经常变化的变量;这是给编译器的一个提示。 此存储类修饰符仅适用于局部变量。
注意:HLSL 编译器当前忽略此存储类修饰符。 -
Type_Modifier
-
可选的变量类型修饰符。
值 说明 const 标记一个着色器无法更改的变量,因此,必须在变量声明中对其进行初始化。 默认情况下,全局变量被视为 const(通过向编译器提供 /Gec 标志来抑制此行为)。 row_major 标记一个在一行中存储四个组件的变量,以便它们可以存储在一个常量寄存器中。 column_major 标记一个在一列中存储 4 个组件的变量,以优化矩阵数学。 注意
如果未指定类型修饰符值,编译器将使用 column_major 作为默认值。
-
类型
-
数据类型 (DirectX HLSL) 中列出的任何 HLSL 类型。
-
Name[Index]
-
唯一标识着色器变量的 ASCII 字符串。 若要定义可选数组,请使用 index 作为数组大小,数组大小为正整数 = 1。
-
语义
-
可选参数使用信息,由编译器用于链接着色器输入和输出。 顶点和像素着色器有多个预定义语义。 编译器忽略语义,除非它们是在全局变量上声明的,或者是传递给着色器的参数。
-
Packoffset
-
用于手动打包着色器常量的可选关键字。 请参阅 packoffset (DirectX HLSL)。
-
注册
-
用于手动将着色器变量分配给特定寄存器的可选关键字。 请参阅寄存器 (DirectX HLSL)。
-
注释
-
附加到全局变量的字符串形式的可选元数据。 注释由效果框架使用,而被 HLSL 忽略;要查看更详细的语法,请参阅注释语法。
-
Initial_Value
-
可选初始值;值的数量应与类型中的组件数量相匹配。 标记为 extern 的每个全局变量都必须用文字值初始化;标记为 static 的每个变量都必须用常量初始化。
未标记为 static 或 extern 的全局变量不会编译到着色器中。 编译器不会自动为全局变量设置默认值,也无法在优化中使用它们。 若要初始化这种类型的全局变量,请使用反射获取其值,然后将该值复制到常量缓冲区。 例如,可以使用 ID3D11ShaderReflection::GetVariableByName 方法获取变量,使用 ID3D11ShaderReflectionVariable::GetDesc 方法获取着色器变量说明,并从 D3D11_SHADER_VARIABLE_DESC 结构的 DefaultValue 成员获取初始值。 若要将值复制到常量缓冲区,必须确保缓冲区是使用 CPU 写入访问权限 (D3D11_CPU_ACCESS_WRITE) 创建的。 有关如何创建常量缓冲区的详细信息,请参阅操作说明:创建常量缓冲区。
还可以使用效果框架自动处理反射和设置初始值。 例如,可以使用 ID3DX11EffectPass::Apply 方法。
重要
Direct3D 12 中已删除对此功能的支持,包括反映默认初始值设定项的功能。
示例
下面是着色器变量声明的几个示例。
float fVar;
float4 color;
int iVar[3];
uniform float4 position : SV_POSITION;
//Default initializers; supported up to Direct3D 11.
float fVar = 3.1f;
int iVar[3] = {1,2,3};
const float4 lightDirection = {0,0,1};
组共享
HLSL 使计算着色器的线程能够通过共享内存交换值。 HLSL 提供屏障基元(如 GroupMemoryBarrierWithGroupSync 等),以确保对着色器中共享内存的读取和写入进行正确的排序,并避免数据争用。
注意
硬件按组执行线程(翘曲或波前),如果只同步属于同一组的线程是正确的,有时可以省略屏障同步以提高性能。 但我们强烈反对这种省略,原因如下:
- 这种省略会导致不可移植的代码,这些代码在某些硬件上可能不起作用,也不适用于通常以较小的组执行线程的软件光栅化器。
- 与使用全线程屏障相比,通过这种省略可能实现的性能改进将是微不足道的。
在 Direct3D 10 中,写入 groupshared 时没有线程同步,因此这意味着每个线程都被限制在数组中的一个位置进行写入。 在写入时,使用 SV_GroupIndex 系统值对此数组进行索引,以确保没有两个线程会发生冲突。 在读取方面,所有线程都可以访问整个数组进行读取。
struct GSData
{
float4 Color;
float Factor;
}
groupshared GSData data[5*5*1];
[numthreads(5,5,1)]
void main( uint index : SV_GroupIndex )
{
data[index].Color = (float4)0;
data[index].Factor = 2.0f;
GroupMemoryBarrierWithGroupSync();
...
}
包装
打包矢量和标量子组件,其大小足以防止跨越寄存器边界。 例如,这些都是有效的:
cbuffer MyBuffer
{
float4 Element1 : packoffset(c0);
float1 Element2 : packoffset(c1);
float1 Element3 : packoffset(c1.y);
}
不能混合打包类型。
与 register 关键字一样,packoffset 可以是特定于目标的。 子组件打包仅适用于 packoffset 关键字,而不适用于 register 关键字。 在 cbuffer 声明中,Direct3D 10 目标忽略 register 关键字,因为它被认为是为了跨平台兼容性。
打包的元素可能会重叠,编译器不会给出错误或警告。 在本例中,Element2 和 Element3 将与 Element1.x 和 Element1.y 重叠。
cbuffer MyBuffer
{
float4 Element1 : packoffset(c0);
float1 Element2 : packoffset(c0);
float1 Element3 : packoffset(c0.y);
}
使用 packoffset 的示例为:HLSLWithoutFX10 示例。