着色器常量 (HLSL)

在着色器模型 4 中,着色器常量存储在内存中的一个或多个缓冲区资源中。 它们可以组织成两种类型的缓冲区:常量缓冲区 (cbuffer) 和纹理缓冲区 (tbuffer)。 常量缓冲区针对常量变量使用情况进行了优化,其特点是访问延迟较低,CPU 更新更频繁。 出于此原因,其他大小、布局和访问限制适用于这些资源。 纹理缓冲区像纹理一样进行访问,并且对于任意索引数据的性能更好。 无论使用哪种类型的资源,应用程序可创建的常量缓冲区或纹理缓冲区的数量没有限制。

声明常量缓冲区或纹理缓冲区看起来非常类似于 C 中的结构声明,其中添加了 register 和 packoffset 关键字,用于手动分配寄存器或打包数据

BufferType Name [: register(b#)] { VariableDeclaration [: packoffset(c#.xyzw)]; ... };

参数

BufferType

[in] 缓冲区类型。

BufferType 说明
cbuffer 常量缓冲区
tbuffer 纹理缓冲区

Name

[in] 包含唯一缓冲区名称的 ASCII 字符串。

register(b#)

[in] 可选关键字,用于手动打包常量数据。 常量只能在常量缓冲区中打包在寄存器中,其中起始寄存器由寄存器编号 (#) 提供。

VariableDeclaration

[in] 变量声明,类似于结构成员声明。 这可以是任何 HLSL 类型或效果对象(纹理或采样器对象除外)。

packoffset(c#.xyzw)

[in] 可选关键字,用于手动打包常量数据。 常量可打包在任何常量缓冲区中,其中寄存器编号由 (#) 提供。 子分量打包(使用 xyzw 重排)适用于大小适合单个寄存器(不跨寄存器边界)的常量。 例如,float4 无法打包在以 y 分量开始的单个寄存器中,因为它不适合四分量寄存器。

备注

常量缓冲区允许将着色器常量组合在一起并同时提交,而不是逐个调用来单独提交每个常量,这减少了更新着色器常量所需的带宽。

常量缓冲区是像缓冲区一样访问的专用缓冲区资源。 每个常量缓冲区最多可容纳 4096 个向量;每个向量最多包含四个 32 位值。 每个管道阶段最多可绑定 14 个常量缓冲区(保留 2 个额外槽供内部使用)。

纹理缓冲区是像纹理一样访问的专用缓冲区资源。 纹理访问(与缓冲区访问相比)可针对任意索引数据提供更好的性能。 每个管道阶段最多可绑定 128 个纹理缓冲区。

缓冲区资源旨在最大程度地减少设置着色器常量开销。 效果框架(请参阅 ID3D10Effect 接口)将管理更新常量和纹理缓冲区,你也可使用 Direct3D API 更新缓冲区(请参阅复制和访问资源数据 (Direct3D 10) 以获取信息)。 应用程序还可将数据从另一个缓冲区(例如呈现器目标或流输出目标)复制到常量缓冲区中。

有关在 D3D10 应用程序中使用常量缓冲区的详细信息,请参阅资源类型 (Direct3D 10)创建缓冲区资源 (Direct3D 10)

有关在 D3D11 应用程序中使用常量缓冲区的详细信息,请参阅 Direct3D 11 中的缓冲区简介如何:创建常量缓冲区

常量缓冲区不需要将视图绑定到管道。 但是,纹理缓冲区需要视图,并且必须绑定到纹理槽(或使用效果时必须与 SetTextureBuffer 绑定)

有两种方法可打包常量数据:使用寄存器 (DirectX HLSL)packoffset (DirectX HLSL) 关键字。

Direct3D 9 与 Direct3D 10 和 11 之间的差异:

  • 与 Direct3D 9 中的常量自动分配不同(Direct3D 9 不执行打包,而是将每个变量分配给一组 float4 寄存器),HLSL 常量变量遵循 Direct3D 10 和 11 中的打包规则。

组织常量缓冲区

常量缓冲区允许将着色器常量组合在一起并同时提交,而不是逐个调用来单独提交每个常量,这减少了更新着色器常量所需的带宽。

有效使用常量缓冲区的最佳方法是,根据它们的更新频率将着色器变量组织成常量缓冲区。 这样,应用程序就可最大程度地减少更新着色器常量所需的带宽。 例如,着色器可能会声明两个常量缓冲区,并根据更新频率组织每个缓冲区中的数据:需要基于每个对象更新的数据(如世界矩阵)分组到一个可针对每个对象进行更新的常量缓冲区中。 这与描述场景特征的数据是分开的,因此更新频率可能要低得多(当场景发生更改时)。

cbuffer myObject
{       
    float4x4 matWorld;
    float3   vObjectPosition;
    int      arrayIndex;
}
 
cbuffer myScene
{
    float3   vSunPosition;
    float4x4 matView;
}
        

默认常量缓冲区

有两个默认常量缓冲区可用,即 $Global 和 $Param。 使用用于 cbuffer 的相同打包方法,将放置在全局范围内的变量隐式添加到 $Global cbuffer。 在效果框架外部编译着色器时,函数参数列表中的统一参数显示在 $Param 常量缓冲区中。 在效果框架内部编译时,所有统一参数都必须解析为全局范围内定义的变量。

示例

下面是一个来自 Skinning10 示例(它是由矩阵数组组成的纹理缓冲区)的示例。

tbuffer tbAnimMatrices
{
    matrix g_mTexBoneWorld[MAX_BONE_MATRICES];
};
      

此示例声明手动分配一个常量缓冲区以从特定寄存器开始,并按子分量打包特定元素。

cbuffer MyBuffer : register(b3)
{
    float4 Element1 : packoffset(c0);
    float1 Element2 : packoffset(c1);
    float1 Element3 : packoffset(c1.y);
}
      

着色器模型 4