PDA

View Full Version : Uniform Buffers and BindBufferRange



thokra
08-17-2011, 10:20 AM
Hey everyone!

I'm currently playing around with glBindBufferBase. Let's assume I have a uniform buffer of size 4 * 16 * sizeof(GLfloat), where one collection of uniforms has the size 1 * 16 * sizeof(GLfloat). Now I'm rendering 4 objects and I want to bind one collection at a time and use it within the active shader with the following block declaration:




layout(std140) uniform Collection
{
vec4 c0;
vec4 c1;
vec4 c2;
vec4 c3;
}



Since my driver reports an offset alignment of 256 machine units, I get an INVALID_VALUE error when binding buffer range like this:



glBindBufferRange(GL_UNIFORM_BUFFER, buffer_index, offset, 16 * sizeof(GLfloat));

with offset = i * 16 * sizeof(GLfloat) and i {0, 1, 2, 3}.

Do I have to face facts and realize that I can only bind a collection which has a size equal to the offset alignment, or am I terribly missing somethin here?

Do I need to resort to indexing into an array of structs within the UB and use a constant size (which totally defeats the purpose), or go back to using glUniform*() which will result in a massive increase in API calls with more then just a few objects?

I'm using OpenGL 3.3 Core. Thank you!

Edit: BTW, gDEBugger confirmed that the buffer is setup correctly and so is the uniform block binding.

Alfonse Reinheart
08-17-2011, 10:33 AM
Honestly, I was going to tell you to look at the reference docs, but sadly, they are incomplete on this point.

When you use glBindBufferRange on uniform buffers, the `offset` parameter must be aligned to an implementation-dependent value: GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT. Also, the size bound must be a minimum of GL_UNIFORM_BLOCK_SIZE_DATA.

So you need to fetch these values, then build your buffer objects based on them:



GLint uniformBufferAlignSize = 0;
GLint uniformBufferMinSize = 0;
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniformBufferAlignSize);
glGetIntegerv(GL_UNIFORM_BLOCK_SIZE_DATA, &uniformBufferMinSize);

thokra
08-17-2011, 11:30 AM
Honestly, I was going to tell you to look at the reference docs, but sadly, they are incomplete on this point.

OT: I always do my best to check every available reference BEFORE posting in a board. I checked the core spec, the ARB extension spec, the GL wiki. However, you could not have guessed that.



[..] the `offset` parameter must be aligned to an implementation-dependent value[..]

I already mentioned the offset alignment of 256 bytes (in my case), so I'm aware of this. Also, since I'm using the std140 layout, my block size is known in advance and I did of course match the size of each collection to the block size.

My point is: If I have to honor this limitation, the minimal collection of uniforms I can bind at a time would have to span 256 bytes. Right?

This is what I don't get. It basically seems to render uniform blocks with small uniform collections unusable, unless you bind it using BindBufferBase and use some indexing scheme within a shader.

Am I right or simply too stupid?

Ilian Dinev
08-17-2011, 11:52 AM
Afaik, the original alignment was 64, and was recently changed for future-proof with VAX AVX and stuff (why??). 64 was a nice number: a mat4 for world-pos, or mat4x3 for worldpos and a vec4 for something else.
But anyway the update to 256-byte alignment isn't that bad, imho: At most you'll lose 192 bytes per object; and if there are 50k bigger objects in the scene, you'll waste only 9MB. For many tiny instanced objects, usually you'll keep instance-consts in 3-4 floats, and index into an array.

I'd have loved to have 4-byte alignment, though.

Ilian Dinev
08-17-2011, 11:57 AM
Correction: ARB_map_buffer_alignment.txt was speaking about 256 bits instead of bytes, so the 256 alignment you're seeing is a somewhat arbitrary limitation an IHV has decided upon.

Alfonse Reinheart
08-17-2011, 12:43 PM
My point is: If I have to honor this limitation, the minimal collection of uniforms I can bind at a time would have to span 256 bytes. Right?

No; that's just the required alignment for binding.

If your uniform block is less than this size, then there will simply be extra space at the end. If your uniform block is only 64 bytes, then you need to allocate chunks in your buffer to the required alignment.

So your buffer would look like this:



---------- 0 bytes
block data
block data
---------- 64 bytes: end of block data you care about
unused
unused
unused
---------- 1*alignment bytes: end of *actual* block data.
block data
block data
---------- 1*alignment + 64 bytes
unused
unused
unused
---------- 2*alignment: end of second block data.
...


Your uniform block definition in GLSL does not need to have the unused area in padding. That is, it doesn't need extra unused fields. This padding is all about how much room you give each section in your buffer object.

thokra
08-17-2011, 01:11 PM
Man, thank you guys so much! Works like a charm.

With my current setup I'm losing 10 vec4s per object which is not a problem at all. I had suspected I needed to waste a bit of memory, but I got way to caught up in it.

djee.
12-09-2011, 07:03 AM
Hello,


When you use glBindBufferRange on uniform buffers, the `offset` parameter must be aligned to an implementation-dependent value: GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT.
That's OK for me, written in the spec.


Also, the size bound must be a minimum of GL_UNIFORM_BLOCK_SIZE_DATA.
I couldn't find any other reference to this, neither in the spec (3.3 core) nor even in any result from Google (which actually returns only this thread as its only search result). Could you tell me more about it? Was it removed? Is it a future feature?

Thanks.

Alfonse Reinheart
12-09-2011, 09:37 AM
I couldn't find any other reference to this, neither in the spec (3.3 core) nor even in any result from Google (which actually returns only this thread as its only search result).

The bottom of page 73 and the top of page 74 of the 3.3 core OpenGL specification. Are you using an up-to-date PDF reader with search capabilities?