HLSL 5.0 float1x3 与 float3x1 常量缓冲区打包规则

HLSL 5.0 float1x3 vs float3x1 constant buffer packing rule

我目前正在努力了解 HLSL 5.0 和 D3D11 中的常量缓冲区打包规则。所以我玩了一下 fxc.exe:

// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.18773
//
//
// Buffer Definitions:
//
// cbuffer testbuffer
// {
//
//   float foo;                         // Offset:    0 Size:     4
//   float3x1 bar;                      // Offset:    4 Size:    12 [unused]
//
// }
//
//
// Resource Bindings:
//
// Name                                 Type  Format         Dim Slot Elements
// ------------------------------ ---------- ------- ----------- ---- --------
// testbuffer                        cbuffer      NA          NA    0        1

到目前为止,一切都如我所料。 float3x1 的大小为 12 个字节,因此可以放在第一个 16 字节的槽中,因为之前的变量大小为 4 个字节。 将 float3x1 更改为 float1x3 后,编译器输出现在如下所示:

// Generated by Microsoft (R) HLSL Shader Compiler 6.3.9600.18773
//
//
// Buffer Definitions:
//
// cbuffer testbuffer
// {
//
//   float foo;                         // Offset:    0 Size:     4
//   float1x3 bar;                      // Offset:   16 Size:    36 [unused]
//
// }
//
//
// Resource Bindings:
//
// Name                                 Type  Format         Dim Slot Elements
// ------------------------------ ---------- ------- ----------- ---- --------
// testbuffer                        cbuffer      NA          NA    0        1

所以 HLSL 编译器似乎突然给 float1x3 中的每个浮点数分配了自己的 16 字节槽,这是相当浪费的。我用谷歌搜索了很多以了解这种行为,但找不到任何东西。我希望你们中的一些人能给我解释一下,因为这种行为真的让我很困惑。

这个答案是根据我对HLSL的理解猜想的,它默认使用列主矩阵打包。 HLSL 中的寄存器由四个 4 字节部分组成,每个寄存器总共 16 个字节。然后每个寄存器充当具有四列的单行。

当您声明一个 float3x1 时,您就是在声明一个具有 3 列和 1 行的矩阵。这非常适合 HLSL 的寄存器打包方法,其中单行可以包含 16 个字节。

当您声明一个 float1x3 时,您就是在声明一个具有一列三行的矩阵。由于 HLSL 处理寄存器打包的方式,它必须将数据分布在 3 组寄存器中并保留 3x3 矩阵的 space。

如果您需要一个 1xX 矩阵,您最好声明一个向量,而不是声明一个向量,它会自动适合单个寄存器,并且可以在任何情况下使用 1x3 或 3x1 矩阵。