PDA

View Full Version : Multiple uniform blocks (is it possible?)



BrianDFS
05-15-2010, 03:24 PM
After looking everywhere, I cannot find anyone who has discussed successfully getting multiple buffer blocks to work for a shader program.

I have however found several cases where people have observed what appears to be broken behavior.

Here:

http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=272501

http://developer.nvidia.com/forums/index.php?showtopic=3541&pid=10322&st=0&#entry1032 2

The NVidia post is also reposted on gamedev.net with no responses.

I post here now, because I too am experiencing issues with trying to get more than one uniform block to work with a shader program.

I've tested this on both ATI and NVidia hardware with the latest drivers for each -- and no matter what I try, it does not work on either card.

Does anyone have any practical real world experience getting this to work? Thanks.

randall
05-16-2010, 02:59 AM
I had no problems with multiple uniform blocks on NVIDIA. Post some code maybe I can help.

BrianDFS
05-16-2010, 04:49 AM
I had no problems with multiple uniform blocks on NVIDIA. Post some code maybe I can help.
Sorry, but my code is too abstracted to easily post a test case. The best I can suggest is to look at the code in the first link I posted.

That code along with their suggested changes does not work on either of my NVidia or ATI cards.

I get black on NVidia and green on ATI.

BrianDFS
05-16-2010, 08:03 AM
Okay, got it working. Wow. It's a bit unintuitive. Basically a level of indirection. It appears the big thing to understand is that you cannot relocate the uniform block. Am I right?

I have a "global" uniform block that I share between several shader programs. The thing that was throwing me off is that for some programs the compiler would assign it to uniform block index 0, while others would get uniform block index 1.

Regardless of the block's assigned uniform block index for each shader program, I need to assign the corresponding uniform buffer to the appropriate "uniform buffer binding index" before rendering.

This cannot be safely done once at initialization time and forgotten about due what I described above with the same uniform block possibly being assigned different uniform block indices across various shader programs that use the uniform block index.

Thus, when changing shader programs, it is necessary to make the call to glBindBufferBase to establish the connection between uniform block and uniform buffer.

Alfonse Reinheart
05-16-2010, 02:00 PM
This cannot be safely done once at initialization time and forgotten about due what I described above with the same uniform block possibly being assigned different uniform block indices across various shader programs that use the uniform block index.

No, you're misunderstanding how this works. UBO binding (http://www.opengl.org/wiki/Uniform_Buffer_Object#OpenGL_Usage) works exactly like texture object binding.

The OpenGL context has a number of slots for binding UBOs. There are GL_MAX_UNIFORM_BUFFER_BINDINGS number of slots for UBO binding.

Uniform Blocks in a program can be set to use one of the slots in the context. You do this by first querying the block index using the block name (glGetUniformBlockIndex). This is similar to how you need to use glGetUniformLocation in order to set a uniform's value with glUniform. Block indices, like uniform locations, are specific to a program.

Once you have the block index, you use glUniformBlockBinding to set that specific program to use a particular uniform buffer slot in the context.

Let's say you have a global UBO that you want to use for every program. To make using it easier, you want to bind it just once.

So first, you pick a uniform buffer slot in the context, one that always will refer to this UBO. Let's say you pick slot 8.

When you build a program object that may use this global uniform buffer, what you do is quite simple. First, after linking the program, call glGetUniformBlockIndex(program, "NameOfGlobalUniformBlock"). If you get back GL_INVALID_INDEX, then you know that the global uniform block isn't used in that program. Otherwise you get back a block index.

If you got a valid block index, then you call glUniformBlockBinding(program, uniformBlockIndex, 8). Remember that 8 is the uniform buffer context slot that we selected earlier. This causes this particular program to use uniform buffer slot #8 to find the buffer for "NameOfGlobalUniformBlock".

Finally, to set the actual buffer in the context, call glBindBufferRange(GL_UNIFORM_BUFFER, 8, bufferObjectName, offset, size);

And that's it. You never need to touch these again.

BrianDFS
05-16-2010, 03:17 PM
Alfonse, thanks for the response. That all makes sense. However, the only caveat to that is when you end up with more unique uniform blocks than GL_MAX_UNIFORM_BUFFER_BINDINGS due shader permutation explosion. It will likely be pretty easy to exceed that value, at least for now.

For example, my current NVidia card returns 36 for that value. Of course at that point, I guess you could start looking at more clever ways to deal with it.

Alfonse Reinheart
05-16-2010, 05:09 PM
However, the only caveat to that is when you end up with more unique uniform blocks than GL_MAX_UNIFORM_BUFFER_BINDINGS due shader permutation explosion. It will likely be pretty easy to exceed that value, at least for now.

Um, how many global uniform blocks would you have? We're talking about uniform blocks that are global for a lot of different programs.

If you have more than around 2, maybe you should combine them together.