PDA

View Full Version : Shader uniform buffer and glFlush problem.



Robinson
03-25-2012, 08:11 AM
I have a question about glUniformBlockBinding and shaders, particularly concerning some behaviour I've not found documented anywhere (of course, it could just be me). I have a compound model consisting of 6 different meshes. Each different mesh has a different material. A material is just a uniform block that I bind into the shader before rendering the mesh. So I'm effectively doing this whenever I render the model and all of its meshes:


for(auto i = mesh.begin(); i != mesh.end(); ++i)
{
phongShader->Set("Material", (*i)->Material()->ShaderVariables());

...DrawIndexedTriangles(...)
}

Where phongShader->Set(...) is a method that does a glUniformBlockBinding for the given shader variable block. Now when I do this, assuming meshes each use a different material and each material specifies a different colour for the shader (white, blue, red, green, yellow, for testing purposes), the colours displayed by each mesh are wrong.

When I issue a glFlush after drawing each mesh, then the colours are correct:


for(auto i = mesh.begin(); i != mesh.end(); ++i)
{
phongShader->Set("Material", (*i)->Material()->ShaderVariables());

...DrawIndexedTriangles(...)

glFlush();
}

So here's what I've done to try to debug things:

(1) Checked that each material has the correct parameters and is bound correctly.

(2) Hard coded each material when drawing all meshes, to verify the material/shader is OK.

(3) Verified that the correct material is being referred to by the correct mesh.

(4) Rendered only the first mesh from the model (it was the correct colour).

(5) Rendered two meshes from the model (the first mesh was rendered the same colour as the second mesh [???])


I've checked and checked again and come to the following conclusion given that it works with a glFlush: when I bind the uniform block to the shader, its current binding is overwritten by my next call to bind the uniform block, that is until GL decides to send the batch for processing, at which time my next call to change the shader uniform block succeeds.

Note that my ordinary uniform (the modelview transform matrix) is fine. This uniform is set directly on the program and isn't a uniform block. That is, all meshes appear at the correct location with the correct orientation.

Can anyone explain this behaviour for me please?

Here's a screenshot demonstrating the problem : tinypic.com (http://i42.tinypic.com/8xn1jo.png)

Thanks.

kRogue
03-25-2012, 10:57 PM
My two cents: glFlush(); should not have helped. AFAIK, it does not affect GL state, it only tells the GL implementation to wait until all pending commands are processed.

Now, there are still a few places where things can go wrong: is your program a multi-threaded beast? is there another thread that has a GL context in the same share group? If so it is possible that the other thread is "doing" something that makes the state change (namely calling glUniformBlockBinding on the same program).. another issue is that are the buffers used mapped? It is an error to use a buffer object for a GL operation while it is mapped. it might be that if the buffer object is mapped the glFlush() makes the GL implementation flush the changes of the mapped buffers to VRAM... just a thought...

Check for GL errors, and ideally, make a debug context and see of the GL implementation gives some hints to you. Lastly, what GL implementation are you using?

On a side note... the usual pattern (in my opinion) for using UBO's is the following:

(1) A GLSL program has a convention of what each binding block ID holds (much akin to what is often done with textures, i.e. for example texture unit 0 is color, unit 1 is bump, whatever like that).

(2) when to draw a different material, one binds a different buffer object, i.e. glBindBufferRange()

There is nothing at wrong with calling glUniformBlockBinding() and using that to decide what buffer object to use, but it limits the number of objects you'll drawn to the number of binding points.... though I guess if you have something running around to do the juggling, you can mix both glUniformBlockBinding and glBindBufferRange to cut down on the number of buffer object bindings...

Robinson
03-26-2012, 11:05 AM
Thanks for your reply kRogue. To answer your questions, I've been doing extensive result checking (in debug mode) from all GL functions, so I know it's not that. I have an ATI video card and their latest drivers, and I'm using 3.3 at the moment, although I know it supports 4. Now I'm quite happy to make the assumption that it isn't GL that's the problem, it's my knowledge :-). I've always found uniform buffers to be extremely difficult to get my head around. I don't fully understand point (1) and (2) you make there. So I think I'll go over what my understanding is and you or someone can correct me.

(1) Create a uniform buffer:

glGenBuffers(1, &MyID);

(2) Destroy a uniform buffer:

glDeleteBuffers(1, &MyID);

(3) Update a uniform buffer:

glBindBuffer(GL_UNIFORM_BUFFER, MyID);
glBufferData(GL_UNIFORM_BUFFER, size, data, GL_DYNAMIC_DRAW);

Now, here's the bit I think I'm doing incorrectly:

(4) Binding a buffer to a binding point, how is ??? chosen?

glBindBufferBase(GL_UNIFORM_BUFFER, ???, blockID);

(5) Then when configuring the shader:

glUniformBlockBinding(shaderID, blockPosition, ???);

Where `blockPosition' = glGetUniformBlockIndex(shaderID, "Material");

Now at the moment I'm generating a unique "???" for each uniform buffer and then when I call glUniformBlockBinding I assume that tells the shader to link the block in the shader called "Material" with the value I bound the uniform buffer to when I called glBindBufferBase.

Presumably this is wrong?

Thanks for any assistance you can give me.

Robinson
03-26-2012, 02:14 PM
OK, I fixed it.

I didn't realise I simply had to do this whenever I wanted to bind a new material uniform buffer (or any buffer):

glBindBufferBase(GL_UNIFORM_BUFFER, position, variables->ID());

Where position is the index I'd assigned the uniform block in the shader program, and variables->ID() is the GL ID for the uniform buffer.

Now it works :-).

It was actually easier than many tutorials, superbible and any number of question/answers online make it seem.

kRogue
03-27-2012, 04:58 AM
I find the easiest way to think of UBO's is that it works the similar as a textures:

the value V set with glUniformBlockBinding(shaderID, glGetUniformBlockIndex(shaderID, "GLSL-name"), V) determines what "uniform buffer object unit" to use, where "uniform buffer object unit" is analogous to texture unit (i.e. glUniform1i(shader, glGetUniformLocation(shaderID, "my_texture"), i) means that my_texture sources from GL_TEXTUREi glBindBufferBase(GL_UNIFORM_BUFFER, i, buffer_object_ID) is analogous to glBindTextureUnitParameterEXT(GL_TEXTUREi, textureID).