Search code examples
vulkan

Does order within push constant structs matter, even when using alignas()?


I have a "Sprite" struct that I hand over as a push constant.

struct Sprite
{   
    glm::vec2 position;
    alignas(16) Rect uvRect; //a "Rect" is just 2 glm::vec2s
    alignas(4) glm::uint32 textureIndex;
    alignas(4) glm::float32 rotation;
};

In my .vert file, I describe its layout as:

layout(push_constant) uniform Push
{
    vec2 offset; //the 'position' part of the Sprite
    vec2 origin; //The first part of the Sprite struct's "uvRect"
    vec2 extent; //The second part of the Sprite struct's "uvRect"
    uint textureIndex;
    float rotation;
}
push;

This doesn't work: I get a black screen.

However, if I rearrange Sprite so that it goes:

struct Sprite
{   
    glm::vec2 position;
    alignas(4) glm::uint32 textureIndex;
    alignas(16) Rect uvRect; //a "Rect" is just 2 glm::vec2s
    alignas(4) glm::float32 rotation;
};

...and then change the layout descriptor thing in the .vert file accordingly, suddenly it does work. Does anyone know why this might be?


Solution

  • In your first structure you align the Rect to the 16 byte boundary, but push constant buffer in vulkan is expecting another vec2 to be tightly packed. Assuming I'm reading the alignment spec correctly archive, structures are aligned on a multiple of a 16 byte boundary, whereas a 2 component vector has an alignment twice that of its base component.

    An array or structure type has an extended alignment equal to the largest extended alignment of any of its members, rounded up to a multiple of 16.

    A scalar or vector type has an extended alignment equal to its base alignment.

    A two-component vector has a base alignment equal to twice its scalar alignment.

    A scalar of size N has a scalar alignment of N.

    Thus the offset in of uvRect in C is 16, but in GLSL the offset of origin is 8 and extent is 16.

    By changing the order Vulkan will begin looking for origin on the 8 byte alignment, which after 3 dwords would be an offset of 16, which then matches what C is expecting.