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 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.