2
votes

I'm having difficulty understanding how glBindBufferRange offset / alignment works in the Nvidia example project gl_commandlist_basic. I've read that the offset needs to be a multiple of GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT which is 256 and/or that offset and alignment is very important with glBindBuffer range.. I have an example UBO that works with mat4/vec4 and a non-working example with mat4/mat3/vec4. The UBO doesn't add up to be a multiple of 256 in either case. I'm try to send vec4(0.f, 1.f, 0.f, 1.f).

If mat4 = 64 bytes, mat3 = 36 bytes, vec4 = 16 bytes then the working example has 64+16=80 bytes, which isn't a multiple of 256. The non-working example has 64+36+16 = 116 bytes.

NV uses an inline called uboAligned which is defined as

inline size_t uboAligned(size_t size) { return ((size + 255) / 256) * 256; }

Removing this from the working/non made no difference either way.

I assume I need to add some "padding" to the UBO in the form of a float/vec2/vec3/vec4, etc. How do I determine the correct amount of padding I need if I want to use the mat4/mat3/vec4 UBO?

/* APPLICATION */
typedef struct
{
    glm::mat4 MM;
    // glm::mat3 NM;
    glm::vec4 Cs;
} myData0;

Gluint objectUBO;
glCreateBuffers(1, &objectUBO);
glNamedBufferData(objectUBO, uboAligned(sizeof(abjObjectData) * 2), 0, GL_STATIC_DRAW); //

for (unsigned int i = 0; i < allObj.size(); ++i)
{
    myData0 myDataTemp;
    myDataTemp.Cs = glm::vec4(0.f, 1.f, 0.f, 1.f);
    glNamedBufferSubData(objectUBO, sizeof(abjObjectData) * i, sizeof(abjObjectData), &objDataInit);
}

//hot loop
for (unsigned int i = 0; i < allObj.size(); ++i)
{
    glBindBufferRange(GL_UNIFORM_BUFFER, 1, objectUBO, uboAligned(sizeof(abjObjectData)) * i, sizeof(abjObjectData));
    //draw
}

/* HW */
out vec4 Ci;

struct ObjectData
{
    mat4 MM;
    // mat3 NM;
    vec4 Cs;
};

layout (std140, binding = 1) uniform objectBuffer { ObjectData object; };

void main()
{
    Ci = object.Cs;
}
2
"Removing this from the working/non made no difference either way." How exactly did you remove that? What did you make the function return?Nicol Bolas
Well if for example I only send the vec4 in the UBO the uboAligned() offset for the second object will be 256 and won't read the value in the shader, but without uboAligned() the offset will be 16 and work....If I manually pad the UBO with 240 bits of extra data then it will read. However, the NV example which has some padding doesn't' add up to 256. I'm confused about the offset / padding having to equal (?) GL_UNIFORM_BUFFER_OFFSET_ALIGNMENTbvs

2 Answers

1
votes

Simple typo with glNamedBufferData. Changing from

glNamedBufferData(objectUBO, uboAligned(sizeof(abjObjectData) * 2), 0, GL_STATIC_DRAW);

to

glNamedBufferData(objectUBO, uboAligned(sizeof(abjObjectData)) * 2, 0, GL_STATIC_DRAW);

fixes the offset / alignment problems.

-2
votes

OpenGL uses a particular alignment. Assuming you are using std140 layout, for example, this is a structure defined in Cpp :

struct PointLight
{
    glm::vec3 position;
    int padding; //this is needed for alignement
    glm::vec3 color; // because a vec3 has to be aligned
    float intensity; //no need for alignment because a float can be read directly without alignement
};

That you can pass to a uniform of a struct like this in shader :

uniform light
{
    vec3 Position;
    vec3 Color;
    float Intensity;
} PointLight;

I would test something like :

struct ObjectData
{
    mat4 MM;
    mat3 NM;
    vec3 padding; //I think you have to add 3 floats of padding
    vec4 Cs;
};

But I couldn't find more infos on it, I don't remember where I found it on the first place.

You have different type of memory layout tho, you can check them, std140 define memory layout in the specs here and I advise you to use this layout. If you don't, shared layout will be used and you have to query the layout. You can use OpenGL to query the layout to know what padding you should add BlockLayoutQuery

Concerning glBindBufferRange, I've never heard about the 256bits alignement. Here is an example of how I use it :

const int pointLightCount = SomeNumber;
int pointLightBufferSize = sizeof(PointLight) * pointLightCount + sizeof(int) * 4;

glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]);
glBufferData(GL_SHADER_STORAGE_BUFFER, pointLightBufferSize, 0, GL_DYNAMIC_COPY);
void * lightBuffer = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
((int*) lightBuffer)[0] = pointLightCount;
 for (int i = 0; i < pointLightCount; ++i) {
        PointLight p = { something };
        ((PointLight*) ((int*) lightBuffer + 4))[i] = p;
    }
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, ssbo[0], 0, pointLightBufferSize); //Bind all the buffer