PDA

View Full Version : Array of uniform blocks



overTaker
02-10-2014, 11:54 AM
Hello.

I was trying create an array of uniform blocks in GLSL. Actually, that went fine, but the problem is, to pass more than one block to it.
My problem in detail is that, only the last passed block is present in the shader.

The situation looks like following:
I have an array of blocks, let's say



layout(binding=0) uniform MyBlock
{
unsigned int a;
vec3 b;
mat4 c;
}block[5];


I am certain about accessing it's elements via block[0].a, but I'm not certain about passing the block values from the C++.
My method only works for the last passed block - like all the previous ones weren't passed at all, ot were just erased.
I'm doing it like this:

First, prepare the general code for the block:


index = glGetUniformBlockIndex(shader, "MyBlock");
glGetActiveUniformBlockiv(shader, index, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
glGetUniformIndices(shader, namesCount, names, indices);
glGetActiveUniformsiv(shader, namesCount, indices, GL_UNIFORM_OFFSET, offsets);
glGenBuffers(1, &ubo);


Then prepare and pass the data:



memcpy(data + offsets[0], &a, sizeof(GLuint));
//repeat for all of the block members

glBindBufferBase(GL_UNIFORM_BUFFER, currentIndex, ubo);
glBufferData(GL_UNIFORM_BUFFER, blockSize, data, GL_DYNAMIC_DRAW);


The above code changes the currentIndex variable to match the array index in GLSL.
With the above code everything, including shaders, compiles fine, but all the indexed blocks (except for the last one) have null values.
For example, when I pass a value to indices 0, 1, 2, 3 and 4, the 0-3 are all nulls, but the fourth one works fine. Or if I just pass a values to 0 and 1, only the 1 is filled with the actual data.

I, for myself, am thinking the error is in

index = glGetUniformBlockIndex(shader, "MyBlock");
this line, where "MyBlock" should be "MyBlock[index]", which I tried, but didn't work, or in binding the buffers (the last two lines), which I tried to change to other ones, but it didn't help as well.

So here comes my final question: how do I fill an array of uniform blocks via uniform buffer?

tonyo_au
02-10-2014, 07:00 PM
try doing it this way


struct item {
unsigned int a;
vec3 b;
mat4 c;
};

layout(std140) uniform MyBlock
{
item i[8];
};


now you can use glBufferData to bulk load the array or glBufferSubData to load in parts. Be careful of alignment/padding (http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt) problems when using arrays

overTaker
02-11-2014, 12:42 AM
So, when I use the glBufferData/SubData now, the struct, that is inside the block layout is still filled byte-by-byte, just like previously? (Except for the padding, you've mentioned of course.)

tonyo_au
02-11-2014, 04:56 PM
Yes. If you setup an array in memory of your 8 entries (with the correct padding in the structure if needed) you can load the shader array with a simple


glBufferData(GL_UNIFORM_BUFFER,sizeof(item_struct) *8,&item[0], GL_DYNAMIC_DRAW);

overTaker
02-12-2014, 04:30 AM
I see.

What about if I would like to send the array indices separately - let's say send one, and then after some time send second etc. - like a dynamic array?
I tried to do something, but my struggles gave me no result.

My logic looks like this:
First bind the buffer range with offset equals to size of the struct * index of the element and size of the same size as the struct size
Then use glBufferData with parameters, just like yours.

But it gave me no results, so I tried a bit different way:
First bind the buffer base
Then use glBufferSubData with offset equals to size of the struct * index of the element and size of the same size az the struct's (just like the binding in my previous attempt).

But it didn't work as well. By didn't work I mean the values in the shader was all zeros.


I would also like to clarify the std140 layout. As far as i can tell the variables has to be aligned to power of 2 like float, int etc. 4 bytes, vec3 and vec4 16 bytes. So all I need to do, is to send one additional float (or any other 4-byte variable) after sending vec3?

tonyo_au
02-12-2014, 05:12 PM
What about if I would like to send the array indices separately
you can use glBufferSubData although if you are changing data from draw to draw you should be using a standard uniform not a uniform buffer.

std140 layout
with your layout

in the glsl
a - 4 bytes
b - 16 bytes
c - 64 bytes

but in c++ it would be
a - 4 bytes
b - 12 bytes
c - 64 bytes

I try to avoid vec3 as you do not actually save space and it makes alignment between gpu and cpu harder

overTaker
02-13-2014, 02:25 AM
The whole thing from my previous post was actually working, it was just my fault to see that I accidentaly delete UBO name. Silly me.

But here comes two theoritical questions, that I'm really curious about.
The first one, is what is the difference between using glBindBufferRange + glBufferData and glBindBufferBase + glBufferSubData routines?
And the second one is what makes the need of the padding, when using std140 layout? I mean, why is the padding there?

Thank you for your help.

overTaker
02-13-2014, 10:00 AM
Now, when the data is sent, there still is an error, that I cannot trace.
I decided to make the buffer without any custom layout styles, and I'm specifying the offsets for the struct manually. I'm sure the offsets are fine, as I checked them twice and debugged to be sure.

The problem is that, the sent data isn't actually the data I originally sent.
The data that I want to send is unsigned int, three vec3s and five floats. unsigned int is sent fine, but then after the vec3s everything is messed up. I tried to see if it's vec3s fault, and it turned on - yes it is indeed, as when I moved the floats before vec3s, they were fine. It looks like an alignment problem to me, but I can't really find any reliable source of informations for this topic, and I thought, the padding is required only with std140 layout, while I'm not using it.
I tried however to switch to std140 to see if the lack of it might be the cause of the problems, as well as making the vec3s vec4s, but in both cases nothing has changed.

I'm sending the buffer like this:


glBindBufferBase(GL_UNIFORM_BUFFER, blockIndex, UBO);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(MyStruct) * structIndex, sizeof(MyStruct), data);


And setting up the data array like this:


memcpy(data + offsets[n], &vec3variable[0], sizeof(glm::vec3)); //instead of glm::vec3 could be float with result multiplied by 3


What do I miss here?

tonyo_au
02-13-2014, 04:42 PM
If you do not use the std140 it is your responsibility to ask OpenGL for the offsets for each data item as the specs do not define how the data will aligned. If you use std140 layout it is defined.
Please read this spec very carefully


glBufferSubData(GL_UNIFORM_BUFFER, sizeof(MyStruct) * structIndex, sizeof(MyStruct), data);
will NOT be loading the data where you think

overTaker
02-14-2014, 04:53 AM
You are right, it works flawless now.
Thank you very much for your help!