3

I have the following situation. UBO structure that holds 4 uint64_t in an array.

  struct MaterialData{
     uint64_t  comps[4];
  };
  layout (std140, binding = 0) uniform MaterialBlock {
      MaterialData data[50];
  }material;

The problem is,only index = 0 gives me correct data.But if I change to GLSL built-in type vector of uint64_t, it all works ok:

  struct MaterialData{
     u64vec4  textures;
  };

It surely has something to do with padding rules for std140.And here is what OpenGL 4.5 specs says:

If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The array may have padding at the end; the base offset of the member following the array is rounded up to the next multiple of the base alignment.

So I understand from this part

the base alignment and array stride are set to match the base alignment of a single array element

that each array's member alignment is the size of the member,which is 8 bytes.Though this is not exactly clear.And in reality it doesn't work.I can get along with u64vec4 but I want to know how to pad an array of uint64_t?

Michael IV
  • 11,016
  • 12
  • 92
  • 223

2 Answers2

5

You missed the key phrase:

and rounded up to the base alignment of a vec4

That means the stride for an array of 64-bit integers is the same as the alignment of a vec4: 16. So a 64-bit integer in an array has 8 bytes of useful data, followed by 8 bytes of padding, followed by the next element.

So in C++, the equivalent array would have to look something like:

struct 64_bit_array_element
{
  std::uint64_t value;
  std::uint8_t padding[8];
};

struct UBO
{
  64_bit_array_element comps[4];
};

Of course, that's hugely wasteful. A better way to handle this is to use an array of u64vec2s.

Or you could just use an SSBO and std430 layout, for which the offending line in the specification does not apply.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

You already are quoting the relevant part of the GL spec, I just add some emphasis here:

If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4.

In std140, array stride is always a multiple of 16 bytes. (This is btw one major difference to the later std430, which is allowed for SSBOs). So if you really want the uint64_t[4] type, you need to pad each element with additional 8 bytes, wasting 50% of the storage.

I do see 2 alternatives:

  1. just use u64vec4 as you already do. Since even the [] operator is defined for vectors in GLSL, there is no significant difference to uint64_t[4]. You just get additional swizzling operators.
  2. Since you seem to use modern GL anyway, switch over to an SSBO with std430 layout.
derhass
  • 43,833
  • 2
  • 57
  • 78
  • Yeah,indeed,waste of space using array... "SSBO access, all things being equal, will likely be slower than UBO access" https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object makes me feel like UBO is more optimal for performance.Though I never benchmarked – Michael IV Aug 19 '18 at 14:34
  • @MichaelIV: There are such things as memory/performance tradeoffs. Also, if the array is of a small size, then the cost of accessing it will quickly be eliminated thanks to caching. Lastly, pay attention to words like "all things being equal"; I didn't write those words because I like being wordy. – Nicol Bolas Aug 19 '18 at 14:48