I've run into an issue while attempting to use SSBOs as follows:
GLuint lightSSBO;
glGenBuffers(1, &lightSSBO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, lightSSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(int) sizeof(LightData) * 10, NULL, GL_DYNAMIC_DRAW);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int), &lightCount);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, sizeof(int), sizeof(LightData) * lights.size(), &lights[0]);
void* ptr = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
std::cout << "lightCount value = " << *(int*)ptr << "\n";
std::cout << "First Light: \n";
ptr = (int*)ptr 1;
std::cout << "\tType: " << *(unsigned int*)ptr << "\n";
ptr = (int*)ptr 1;
std::cout << "\tPosition: " << vec4ToString((glm::vec4*)ptr) << "\n";
ptr = (float*)ptr 4;
std::cout << "\tDirection: " << vec4ToString((glm::vec4*)ptr) << "\n";
ptr = (float*)ptr 4;
std::cout << "\tColour: " << vec4ToString((glm::vec4*)ptr) << "\n";
ptr = (float*)ptr 4;
std::cout << "\tSize: " << *(float*)ptr << "\n";
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
In the code excerpt above, ptr outputs the correct values I've assigned to lightCount and to lights[0], meaning the buffer ostensibly has the correct data. However, when I try to access beyond lightCount on the GPU (Lights[0]), I get incorrect values. For example, Lights[0].Colour simply returns (0,0,0,?), despite displaying correctly as (1,0.2,0.2,1) throught ptr above.
struct LightData
{
uint Type;
vec4 Position;
vec4 Direction;
vec4 Colour;
float Size;
};
layout (std430, binding = 0) buffer LightBuffer
{
int LightCount;
LightData[] Lights;
};
void main()
{
FragColor = Lights[0].Colour;
}
And the output of the console for completeness: (The correct values)
lightCount value = 1
First Light:
Type: 1
Position: 0.000000, 0.000000, 0.000000, 1.000000
Direction: -0.707107, 0.000000, -0.707107, 1.000000
Colour: 1.000000, 0.200000, 0.200000, 1.000000
Size: 0
Note: Changing the shader colour to be Position is still not correct and does not change if I change the underlying data, indicating that the Light array is either not making it to the GPU or I'm accessing it incorrectly somehow.
Does anyone know why this is happening? Additionally, I'm confused as to the meaning/options for glBufferData's final GLenum parameter, even https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_storage_buffer_object.txt doesn't seem to mention it.
CodePudding user response:
GLSL structs and C structs have different rules on alignment. For structs, the spec states:
If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this substructure are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.
Let's analyze the struct:
struct LightData
{
uint Type;
vec4 Position;
vec4 Direction;
vec4 Colour;
float Size;
};
The largest member is a vec4 (=16 byte alignment). That means that the whole struct also needs to be aligned to 16 bytes. The members of the struct have a size of 4 3 * 16 4 = 56 bytes. The next bigger multiple of 16 is 64 which means that all structs need additional 8bytes of padding at the end.
Since you have an additional member int LightCount;
before the first struct, you also need to add padding between them to ensure that the first struct starts at a 16 byte offset.
The C struct that matches your glsl buffer definition should thus look like:
struct LightData
{
unsigned int Type;
vec4 Position;
vec4 Direction;
vec4 Colour;
float Size;
int padding[2];
}
struct Buffer
{
int LightCount;
int padding[3];
Light Lights[NUM_LIGHTS];
}