0x003
0x003

Reputation: 81

Aligning memory of SSBO that is an array of structs containing an array?

I'm flattening out an octree and sending it to my fragment shader using an SSBO, and I believe I am running into some memory alignment issues. I'm using std430 for the layout and binding a vector of voxels to this SSBO this is the structure in my shader. I'm using GLSL 4.3 FYI

struct Voxel
{
    bool data;   // 4
    vec4 pos;    // 16
    vec4 col;    // 16
    float size;  // 4
    int index;   // 4
    int pIndex;  // 4
    int cIdx[8]; // 4, 16 or 32 bytes?
};

layout (std430, binding=2) buffer octreeData
{
    Voxel voxels[];
};

I'm not 100% sure but I think I'm running into an issue using the int cIdx[8] array inside of the struct, looking at the spec (page 124, section 7.6)

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

I'm not entirely sure what the alignment is, I know the vec4's take up 16 bytes of memory, but how much does my array? If it was just sizeof(int)*8 that would be 32, but it says that it's set to the size of a single array element and then rounded up to a vec4 right? So does that mean my cIdx array has a base alignment of 16 bytes? There's no follow up members so is there padding getting added to my struct?

So total structure memory = 52 bytes (if we only allocate 4 bytes for cIdx), would that mean there is 12 bytes of padding being added on that I need to account for that may be causing me issues? If it was allocating 16 bytes would that be 64 bytes total for the structure and no memory alignment issues?

My corresponding c++ structure

struct Voxel
{
    bool data;
    glm::vec4 pos;
    glm::vec4 col;
    float size;
    int index;
    int pIndex;
    int cIdx[8];
};

I'm then filling in my std::vector<Voxel> and passing it to my shader like so

 glGenBuffers(1, &octreeSSBO);
 glBindBuffer(GL_SHADER_STORAGE_BUFFER, octreeSSBO);
 glBufferData(GL_SHADER_STORAGE_BUFFER, voxelData.size()*sizeof(Voxel), voxelData.data(), GL_DYNAMIC_DRAW);
 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, octreeSSBO);

reading directly from the voxelData vector, I can confirm that the data is getting filled in correctly, and I can even occasionally see that the data is getting passed to the shader but behaving incorrectly compared to what I would expect to see based on the values I'm looking at.

Does it look like there are memory alignment issues here?

Upvotes: 2

Views: 1603

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473397

I'm not entirely sure what the alignment is

The specification is very clear as to what the base alignment of things are. Your problem is not in item #4 (std430 doesn't do the rounding specified in #4 anyway).

Your problem is in #2:

If the member is a two- or four-component vector with components consuming N basic machine units, the base alignment is 2N or 4N, respectively.

In GLSL, vec4 has a base alignment of 16. That means that any vec4 must be allocated on a 16-byte boundary.

pos must be on a 16-byte boundary. However, data is only 4 bytes. Therefore, 12 bytes of padding must be inserted between data and pos to satisfy std430's alignment requirements.

However, glm::vec4 has a C++ alignment of 4. So the C++ compiler does not insert a bunch of padding between data and pos. Thus, the types in the two languages do not agree.

You should explicitly align all GLM vectors in C++ structs that you want to match GLSL, using C++11's alignas keyword:

struct Voxel
{
    bool data;
    alignas(16) glm::vec4 pos;
    alignas(16) glm::vec4 col;
    float size;
    int index;
    int pIndex;
    int cIdx[8];
};

Also, I would not assume that the C++ type bool and the GLSL type bool have the same size.

Upvotes: 3

Related Questions