Reputation: 787
I am seeing a problem where I get the wrong size of by GLSL uniform block.
This is the block in the shader:
uniform MaterialInfo {
vec3 Ka;
vec3 Ks;
vec3 Kd;
};
And then the following code to prepare my uniform buffer object:
blockIndex = glGetUniformBlockIndex(program, "MaterialInfo");
if (blockIndex == -1) {
fprintf(stderr, "Could not bind uniform block\n");
}
printf("Found blockindex materialinfo: %d\n", blockIndex);
glGetActiveUniformBlockiv(program, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
blockBuffer = (GLubyte *) malloc(blockSize);
cout << "GLSL blocksize: " << blockSize << endl;
cout << "sizeof glm::vec3 type: " << sizeof(mesh->Ka) << endl;
cout << "sizeof 3 x glm::vec3 type: " << 3 * sizeof(mesh->Ka) << endl;
The reports the following:
GLSL blocksize: 48
sizeof glm::vec3 type: 12
sizeof 3 x glm::vec3 type: 36
In summary, I was expecting the GLSL blocksize to be 36, not 48. vec3 in GLSL should be a float like my glm::vec3 types.
Also note that I am using a HD4000 beta driver: OpenGL 4.0.0 Build 9.17.10.2792. I have not had the chance to test on a different computer. Is this me misunderstanding something?
Followup after replies:
So in this case the correct way to submit my glm::vec3 floats is the following?
glGetUniformIndices(program, 3, namesMaterial, indices);
glGetActiveUniformsiv(program, 3, indices, GL_UNIFORM_OFFSET, offset);
memcpy(blockBuffer + offset[0], glm::value_ptr(mesh->Ka), 4 * sizeof(GLfloat));
memcpy(blockBuffer + offset[1], glm::value_ptr(mesh->Ks), 4 * sizeof(GLfloat));
memcpy(blockBuffer + offset[2], glm::value_ptr(mesh->Kd), 4 * sizeof(GLfloat));
Upvotes: 3
Views: 1171
Reputation: 473667
In summary, I was expecting the GLSL blocksize to be 36, not 48. vec3 in GLSL should be a float like my glm::vec3 types.
Why? There's nothing in the OpenGL specification that guarantees what the block size is.
Or at least, not with that definition of a uniform block.
Without providing a proper memory layout qualifier, shared
is used by default. This allows the implementation to put whatever padding it feels comfortable with between elements. So you can't just assume that 3 vec3
s will take up 12 consecutive float
s in size. Each vec3
will be 3 consecutive float
s, but there's no guarantee that there won't be padding.
If you want a fixed, known, and consistent layout for uniform blocks, then you need to use the std140
layout. Otherwise, you're getting what the implementation gives you. The OpenGL specification details what the rules are for std140
's element layout.
Of course, with std140
layout, the size of this block will also be 48, because it pads all vec3
elements to vec4
s.
So in this case the correct way to submit my glm::vec3 floats is the following?
This is correct, assuming:
memoryBlock
is either a mapped buffer object pointer or something you upload with glBufferSubData
GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
).mesh->Ka
and its ilk actually do 4 floats. Otherwise you're copying from unallocated memory.Again, I urge you to stop querying this stuff and use std140
layout. When you do, you won't have to query offsets or anything; you can design structs that are perfectly layout-compatible with the GLSL definitions across all hardware.
Upvotes: 3
Reputation: 35933
Many GPUs pad each uniform to be size of vec4:
See this implementation note from OpenGL wiki (emphasis mine):
Implementation note: OpenGL implementations are allowed to reject shaders for implementation-dependent reasons. So you can have fewer active uniform components by your reckoning and still fail to link due to uniform limits. This is usually on hardware that is inately vector hardware. Pre GeForce 8xxx hardware, and all ATi hardware does this. In this case, you should assume that each separate uniform takes up 4 components, much like it would in D3D. That means a "uniform float" is 4 components, a mat2x4 is 16 components (each row is 4 components), but a mat4x2 is 8 components.
Upvotes: 3