在Vulkan的UBO或SSBO中能在其中定义Vec3类型么?

Number of views 159

我认为Vec3类型是一个非常合适的类型。它只占用3个浮点数,而我的数据只需要3个浮点数。我想在UBO或SSBO的结构中这样使用:

layout(std140) uniform UBO
{
  vec4 data1;
  vec3 data2;
  float data3;
};

layout(std430) buffer SSBO
{
  vec4 data1;
  vec3 data2;
  float data3;
};

然后,在我的C/C++代码中,我可以这样做来创建与GLSL中匹配的数据结构:

struct UBO
{
  vector4 data1;
  vector3 data2;
  float data3;
};

struct SSBO
{
  vector4 data1;
  vector3 data2;
  float data3;
};

这样是不是一个比较好的方案?

2 Answers

首先你在Shader中定义了std140的布局,但是c++侧又没有定义aligns(16),这样上传的数据是会错乱的。另外std140的布局对布局内的所有元素有基底对齐(Base Alignment)与对齐偏移量(Aligned Offset)的要求,大致上为如下定义:
image.png
回看你的UBO定义:

layout(std140) uniform UBO
{
  vec4 data1;// 16字节
  vec3 data2;// 12字节,实际占用16字节
  float data3;// 4字节
};

但是实际上data2理论上只应该占用12字节,但是实际占用了16字节,所以从你整个ubo数据结构来看,你多浪费了4字节。
为了减少4字节的浪费,你实际上应该把data3的float一起归并到data2中:

layout(std140) uniform UBO
{
  vec4 data1;// 16字节
  vec4 data2and3;// 16字节
};

这样你就能把数据空间充分利用了。这也是为什么我们需要严格避免在std140布局中使用vec3的原因,因为gpu会把它当作vec4对待。
而std430通常用于大数据处理,如你上面说到的SSBO,它的布局方式std140与std430都可以使用。但是ubo只能使用std140。

在Vulkan的Uniform Buffer Object (UBO) 或 Shader Storage Buffer Object (SSBO) 中,确实可以定义 vec3 类型。然而,需要注意的是 vec3 在内存布局上有一些特殊的处理。

UBO 中的 vec3

在 UBO 中,由于 std140 布局规则,vec3 会被填充到 4 个浮点数的大小以保持对齐。这意味着在实际的内存布局中,vec3 会占用 16 字节(即 4 个浮点数)。这会导致一些不必要的填充,增加了内存消耗。

例如,你的 UBO 结构体将被解释为:

layout(std140) uniform UBO
{
  vec4 data1; // 16 bytes
  vec3 data2; // 16 bytes (due to padding)
  float data3; // 4 bytes
};

总大小将是 36 字节,而不是 12 字节(3 + 3 + 1)。

SSBO 中的 vec3

在 SSBO 中,你可以选择不同的布局规则。如果使用 std430 布局,vec3 不会被自动填充到 4 个浮点数的大小。因此,vec3 将占用 12 字节(3 个浮点数),这与你预期的一致。

例如,你的 SSBO 结构体将被解释为:

layout(std430) buffer SSBO
{
  vec4 data1; // 16 bytes
  vec3 data2; // 12 bytes
  float data3; // 4 bytes
};

总大小将是 32 字节,而不是 36 字节。

C/C++ 中的数据结构

在 C/C++ 中,你可以定义与 GLSL 中一致的数据结构。为了确保内存布局的一致性,你可以使用 alignas 关键字来确保结构体成员的对齐方式与 GLSL 中的一致。

struct alignas(16) UBO
{
  glm::vec4 data1;
  glm::vec3 data2;
  float data3;
};

struct alignas(16) SSBO
{
  glm::vec4 data1;
  glm::vec3 data2;
  float data3;
};

这里使用了 glm::vec4glm::vec3 来模拟 GLSL 的 vec4vec3 类型,并使用 alignas(16) 确保对齐方式正确。

总结

  • 在 UBO 中,vec3 会被填充到 4 个浮点数大小。
  • 在 SSBO 中,如果你使用 std430 布局,vec3 不会被填充。
  • 在 C/C++ 中,使用 alignas 确保结构体成员的对齐方式与 GLSL 中的一致。

这样,你的方案是可行的,但需要注意不同布局规则下的内存对齐问题。