PDA

View Full Version : glUniform + VBO bug?



_x57_
08-22-2011, 05:17 AM
I have a problem and a solution and I would like to know if i missed sth in the specs or if it is a bug:

I am drawing geometry from a VBO twice per frame with an active shader. Between the draw calls (basically setup pointers + glDrawArrays(...)) I change a uniform variable in the shader that indicates another draw mode like this:



...
glUniform1i(glGetUniformLocation(myProgram, "renderPass"), 0);
myDrawVBO();
glUniform1i(glGetUniformLocation(myProgram, "renderPass"), 1);
glUseProgram(0); //why are these lines needed?
glUseProgram(myProgram); //why are these lines needed?
myDrawVBO();
...


The uniform variable renderPass is only correctly updated when I unbind my shader and bind it again in between, otherwise it remains the same and renderPass==0 for both VBO draw passes. Why is that?

When drawing sth in immediate mode instead with vbo it works without the bind/unbind...

BionicBytes
08-22-2011, 06:15 AM
Once a shader is compiled and linked, the uniform locations don't change. Therefore you can query all that info just the once at the time of compile/link.
Therefore your code should be more like:


....shader compile and link
UniformLocation = glGetUniformLocation(myProgram, "renderPass");


....Draw routine
glUseProgram(myProgram);
glUniform1i(UniformLocation, 0);
myDrawVBO();
glUniform1i(UniformLocation, 1);
myDrawVBO();
glUseProgram(0);

_x57_
08-22-2011, 07:54 AM
Thank you for the answer, indeed i do not need to call getUniformLocation every frame.. i'll remember that.

However, i meant sth different:

Without the bind / unbind command BEFORE the second "myDrawVBO" and AFTER the "glUniform" the uniform IS NOT CHANGED despite the glUniform call.
Only the bind/unbind seems to trigger sth that the uniform is correctly updated for the shader and my second VBO draw correctly renders with renderPass==1.

Of course I also bind my shaders once before use and unbind them when I am done with them like you suggested.

BionicBytes
08-22-2011, 08:51 AM
whats you GFX card and driver versions?
Could be a bug then.

_x57_
08-22-2011, 09:05 AM
I have tested it on a GF GTX 560 and a GF GTX 560ti with current driver 280.26 on Win7 64.

I have also experienced the problem on a GF 480 GTX but not on a GF 9800 (driver 266.58 for the latter). However only for the two 560 cards I confirmed that the "glUseProgram" seems to solve the problem.

I also have to say that i am not finished "porting" my application to the new 560 card (opengl2.1->gl3/4 compatibility) and there seem still to be some other issues which i have not determined yet (but shaders compile fine and no glerrors).

Can anyone reproduce the problem?

_x57_
08-23-2011, 06:41 AM
I have further investigated the issue:

The original cause of the problem that i cannot update my uniform variables anymore with glUniform* seems to arise from glLinkProgram:



glUseProgram(myProgram)
...
glBindFragDataLocation(...)
...

glLinkProgram(myProgram) //need to relink to update fragdatalocations
checkLinkStatus(); //verify link is successful

glUniform*(...) //does not work


The glUniform* does not work after glLinkProgram unless I call glUseProgram(myProgram) again after linking and before glUniform.

However, glLinkProgram should install the new executables immediately if the program was already in use before, right?

_arts_
08-23-2011, 07:30 AM
As a result of a successful link operation, all active user-defined uniform variables belonging to program will be initialized to 0

from http://www.opengl.org/sdk/docs/man/xhtml/glLinkProgram.xml

each time you link a program, even already linked program, you must get uniform locations again. This sounds normal since you have a new program installed on the graphic card, so memory state has changed.

_x57_
08-23-2011, 07:39 AM
You are right arts, thats why i always do glUniform*(glGetUniformLocation(shaderProgram, "foo"),bar) - however, the uniform does not get set using this as stated above.

Sorry, i wasn't clear about that before.

Alfonse Reinheart
08-23-2011, 11:05 AM
It is perfectly legal OpenGL code to relink programs after they've been linked before. However, just because something is perfectly legal OpenGL code does not mean one should do it. Relinking programs is something that is very rare. And in general, the best way to avoid driver bugs is to avoid doing rare things.

My suggestion: stop relinking. Whatever you're doing that you feel requires rebinding your output data is unnecessary anyway; just use a different array with glDrawBuffers.

I'm not saying that this isn't a driver bug. But it is one that is easily avoidable.

_x57_
08-24-2011, 02:09 AM
It is perfectly legal OpenGL code to relink programs after they've been linked before. However, just because something is perfectly legal OpenGL code does not mean one should do it. Relinking programs is something that is very rare. And in general, the best way to avoid driver bugs is to avoid doing rare things.
[/CODE]

Sometimes I get the feeling i use lots of rare things with opengl - seems to come with gpgpu on glsl... ;)

[quote=Alfonse Reinheart]
My suggestion: stop relinking. Whatever you're doing that you feel requires rebinding your output data is unnecessary anyway; just use a different array with glDrawBuffers.

You are right, i can easily avoid this. Why is it that I assign outputs to color numbers via glFragDataLocation first and then define a mapping to the drawbuffers anyway? Two mappings right one after another or is there sth happening in between?

OpenGL engine seems like a giant network of configurable data-crossbars ...

Alfonse Reinheart
08-24-2011, 03:13 AM
Why is it that I assign outputs to color numbers via glFragDataLocation first and then define a mapping to the drawbuffers anyway?

Many of the annoying things in OpenGL can be summed up by the following two phrases:

1: Maximize backwards compatibility.

2: It sounded like a good idea at the time.

First, there came ARB_draw_buffers (http://www.opengl.org/registry/specs/ARB/draw_buffers.txt). This came around 2004, so FBOs didn't exist yet and even GLSL was only 1.10. The idea was to simply allow shaders to render to multiple buffers in the default framebuffer, like the GL_AUXi buffers and such.

It gave GLSL fragment shaders new outputs: gl_FragData[], which was an array of some number of possible outputs. Since this was an array, you still needed some way to say where each array entry went. And thus, you needed glDrawBuffers: a mapping from array index to an actual buffer name.

EXT_framebuffer_objects gave us FBOs, but really that didn't affect this; all it gave us was a different set of values for glDrawBuffers.

The next major change came with EXT_texture_integer combined with EXT_fbo. Now, it became desirable to write to integer textures. gl_FragData is ultimately defined as follows:



out vec4 gl_FragData[#];


That's a real problem, because a `vec4` is a floating-point vector. And GL 3.0 was introducing integer textures and render targets. You needed a way to write to an integer output. Which now means that you need to be able to define an integer output.

Enter EXT_gpu_shader4, which is where we see the first appearance of glBindFragDataLocation (in EXT form, of course). Thanks to point 1, the ARB wasn't willing to fundamentally rewrite how buffer draw mapping worked. So they took the route of least resistence.

Each fragment shader output in the gl_FragData days had an index. Therefore, in the post-gl_FragData days, it will have an index again. Except now, it's arbitrarily defined by the user. It causes the least change to the API. Shaders can define these outputs, and outside of some glBindFragDataLocation work, no other part of the user's code would have to change to make everything work under the new system. Everything would work as it did before.

And 5 years later, we would look back and go, "this seems silly."

elFarto
08-24-2011, 04:18 AM
And 5 years later, we would look back and go, "this seems silly."
This seems to be a recurring theme with OpenGL.

_x57_
08-24-2011, 08:08 AM
Wow, that was a great history round-up... thanks!

I think i should adapt this thinking a little bit more in the future - i mean that some things may seem strange but are there because of backward compatibility.
I remember when I first used FBO + shaders with several buffers/outputs: I implemented the glFragDataLocation routines and then stumbled across glDrawBuffers mapping. I thought for quite a while that I did not correctly understand what glDrawBuffers does since I already made the connection between shader outputs and FBO buffers with fragDataLocation. Finally I just used identity mapping and it worked.

Of course now i realize if I had first implemented a non-identity drawbuffer mapping I would probably never had the problems with glLinkProgram since I would not touch fragDataLocation...