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?
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.