openglglslshaderfragment-shadershader-storage-buffer

Only get garbage from Shader Storage Block?


I have bound the shader storage buffer to the shader storage block like so

GLuint index = glGetProgramResourceIndex(myprogram, GL_SHADER_STORAGE_BLOCK, name);
glShaderStorageBlockBinding(myprogram, index, mybindingpoint);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mybuffer)
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, mybindingpoint, mybuffer, 0, 48);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 48, &mydata);

mydata points to a std::vector containing 4 glm::vec3 objects.

Because I bound 48 bytes as the buffer range I expect lights[] to hold 48/(4*3) = 4 vec3s.

layout(std430) buffer light {
    vec3 lights[];
};

The element at index 1 in my std::vector holds the data x=1.0, y=1.0, z=1.0.

But viewing the output by doing

gl_FragColor = vec4(lights[1], 1.0);

I see yellow (x=1.0, y=1.0, z=0.0) pixels. This is not what I loaded into the buffer.

Can somebody tell me what I am doing wrong?

EDIT

I just changend the shader storage block to

layout(std430) buffer light {
    float lights[];
};

and output

gl_FragColor = vec4(lights[3],lights[4],lights[5],1.0);

and it works (white pixels).

If somebody can explain this, that would still be great.


Solution

  • It's because people don't take this simple advice: never use a vec3 in a UBO/SSBO.

    The base alignment of a vec3 is 16 bytes. Always. Therefore, when it is arrayed, the array stride (the number of bytes from one element to the next) is always 16. Exactly the same as a vec4.

    Yes, std430 layout is different from std140. But it's not that different. Specifically, it only prevents the base alignment and stride of array elements (and base alignment of structs) from being rounded up to that of a vec4. But since the base alignment of vec3 is always equal to that of a vec4, it changes nothing about them. It only affects scalars and vec2's.