spraff
spraff

Reputation: 33395

When do GLSL interface block layouts agree with C/C++ structures?

I am reading about std140 and std430 layouts in GLSL.

Consider, in C++:

struct Foo
{
    glm::vec4 alpha;
    float     beta;
    float     gamma;
    glm::vec4 delta;
    float     epsilon;
};

In GLSL:

uniform (layout=std140) MyUniformBlock
{
    vec4  alpha;
    float beta;
    float gamma;
    vec4  delta;
    float epsilon;
};

If I'm reading the documentation correctly, the offsets in MyUniformBlock will be 0, 16, 32, 48, 64, bytes: all multiples of sizeof(vec4)==16. Right?

However, in C/C++, it is permitted for gamma to have an offset of 20 bytes, right?

So under what conditions will the C/C++ struct and the GLSL interface block have the same layout?

If I use layout=packed and give the struct __attribute__ ((packed)) or equivalent, is that guaranteed to match?

Also, consider

Foo array [N];

If C/C++ permits sizeof(Foo) to be not a multiple of sizeof(vec4) because there is no padding after the last element, does that cause problems if I try to send the whole of array to the buffer with glBufferData?

Upvotes: 2

Views: 794

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473407

If I'm reading the documentation correctly, the offsets in MyUniformBlock will be 0, 16, 32, 48, 64, bytes: all multiples of sizeof(vec4)==16. Right?

No. A naked float in std140 has a base alignment of 4 bytes and takes up 4 bytes. Your structure's offsets are:

uniform (layout=std140) MyUniformBlock
{
    vec4  alpha;     //offset 0,     size 16,    min-alignment 16
    float beta;      //offset 16,    size 4,     min-alignment 4
    float gamma;     //offset 20,    size 4,     min-alignment 4
    vec4  delta;     //offset 32,    size 16,    min-alignment 16
    float epsilon;   //offset 48,    size 4,     min-alignment 4
};

If I use layout=packed and give the struct __attribute__ ((packed)) or equivalent, is that guaranteed to match?

packed layout gives you fewer guarantees than std140. In fact, it gives you basically nothing. You can't even assume that all the members you specify in the block will contribute to the layout, because compilers are allowed to optimize away the storage for any variable that you don't use. You can't assume anything with packed; you must always query the layout explicitly.

Broadly speaking, std140 will match most compilers' natural C and C++ layout, so long as:

  1. You never use vec3. Note that this applies to matrices that have 3 columns/rows (depending on your matrix orientation).
  2. You alignas/_Alignas your vector members properly. vec2 objects must be 8-byte aligned, and vec4 must be 16-byte aligned. Note that this applies to matrix types that have 2 or 4 columns/rows too.

    C's _Alignas feature only applies to variable declarations, so you can't apply it to the type. You have to put it on each variable as you use it.

  3. Under std140, you never make arrays of anything that isn't a vec4 or equivalent.
  4. Under std140, struct members of a block should be aligned to 16-bytes.

If you don't follow these rules, then your C or C++ structure layout will have to be different from the GLSL equivalent in order to match up properly. For example, a float array[5] in GLSL would have to be a vec4 array[5] in C or C++.

Upvotes: 2

Related Questions