1

I'm trying to understand std140 alignment for sub-structs within a struct.

As far as I understood, the base alignment for a sub-struct is supposed to be the member with the largest alignment rounded to the alignment of a vec4 (which is 16 bytes aligned). The size of the sub-struct is supposed to be the total size of all members.

The offset for the first member of the sub-struct is supposed to be the offset for the sub-struct itself (which is 16 bytes aligned). The offset for the first member following the sub-struct is also supposed to be 16 bytes aligned.

Did I understand the rules completely wrong? Because I've applied them in 2 situations, which both should be valid, yet only one situation works properly. The official documentation is not exactly clear on this, while tutorials out there never cover this case or give any example.

For example, I've got the following case ("directional_light" is the main struct, while "layered_atlas_tile" is the offending sub-struct):

struct layered_atlas_tile // Alignment: 16 | Size: 8
{
    int Index; // Alignment: 16 | Size: 4
    uint Size; // Alignment: 4 | Size: 4
};

struct directional_light
{
    glm::vec4 Ambient; // Alignment: 16 | Size: 16
    glm::vec4 Diffuse; // Alignment: 16 | Size: 16
    glm::vec4 Specular;// Alignment: 16 | Size: 16

    glm::mat4 LightSpaceMatrix; // Alignment: 16 | Size: 64

    glm::vec3 Direction; // Alignment: 16 | Size: 12
    float Padding1;

    layered_atlas_tile ShadowMapTile; // Alignment: 16 (?) (Padding1 ensures we're 16 aligned) | Size: 8 (?)
    // Padding2 and padding3 ensure that the next member after ShadowMapTile is 16 aligned.
    // The next member, in this case, will be "Ambient" in an array of directional_light
    float Padding2;
    float Padding3;
};

Well, this case completely fails and I have no idea why. But simply moving Padding2 and Padding3 inside the sub-struct itself works perfectly:

struct layered_atlas_tile // Alignment: 16 | Size: 16
{
    int Index; // Alignment: 16 | Size: 4
    uint Size; // Alignment: 4 | Size: 4
    float Padding2;
    float Padding3;
};

struct directional_light
{
    glm::vec4 Ambient; // Alignment: 16 | Size: 16
    glm::vec4 Diffuse; // Alignment: 16 | Size: 16
    glm::vec4 Specular;// Alignment: 16 | Size: 16

    glm::mat4 LightSpaceMatrix; // Alignment: 16 | Size: 64

    glm::vec3 Direction; // Alignment: 16 | Size: 12
    float Padding1;

    layered_atlas_tile ShadowMapTile; // Alignment: 16 (?) (Padding1 ensures we're 16 aligned) | Size: 16 (?)
};

Why is this layout valid when inserting Padding2 and Padding3 directly into the "layered_atlas_tile" struct, yet it completely fails if placed outside right below the sub-struct?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Th3 D0ctor
  • 35
  • 5
  • Not really. Since the largest size of an element of my sub-struct is 4 bytes, rounded up to the alignment of a vec4 results in 16 bytes. Per the rules of the documentation, alignment of the sub-struct has to obey a 16 bytes aligned offset, while the next element following the sub-struct also obeys the same alignment. Padding2/3 obey these rules both outside & inside the sub-struct, and it's not required for them to be 16 aligned when outside since they're not used (if they were an actual member, the story would change). The culprit seems to be the size of the sub-struct itself. – Th3 D0ctor Dec 02 '21 at 22:33
  • Per this article, the size of the sub-struct is the size required by the individual members (including their offset padding) rounded up to the sub-struct alignment: https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html This means that my "layered_atlas_tile" size cannot be 8 bytes and has to be padded to 16 bytes. – Th3 D0ctor Dec 02 '21 at 22:35
  • Also found a chinese article (which completely eludes me & I never saved it, so I cannot link it at this moment) with a similar situation to mine, which padded after the sub-struct as well with no issue. The official documentation, like I mentioned before, is not clear on this specific case (or at least it used to be and it's been modified to a less useful version, judging by the oreilly link. The chinese link also seemed to have a screenshot from an older version of the docs which provided examples for this sub-struct case, which is missing from the latest docs). – Th3 D0ctor Dec 02 '21 at 22:41

0 Answers0