Alejandro Molina
Alejandro Molina

Reputation: 13

glBufferSubData offsets for structs

I am actually working on a OpenGL 3.3 render engine, exactly I'm trying to create a dynamic number of lights in my scene.

To do that I'm working with Uniform Buffer Objects (UBO) and I'm having problems when I try to pass data through and UBO that will be read or write in a struct with different kind of data.

I did that for point lights and directional lights and everything is fine because I'm using only vec3 data. The problem is when I define focal lights which struct is:

#version 330 core

#define MAX_NUM_TOTAL_LIGHTS 100
...
struct FocalLight{
    vec3 f_light_position;
    vec3 f_light_direction;
    vec3 f_light_diffuse_intensity;
    vec3 f_light_specular_intensity;
    float f_apperture_angle;
    float f_attenuation;
};
layout(std140) uniform focalLights{
    FocalLight f_lights[MAX_NUM_TOTAL_LIGHTS];
};

Well, the position, direction, diffuse intensity and specular intensity is fine and my fragment receive this data correctly from the buffer. But I am not able to write & read the data for f_apperture_angle and f_attenuation.

Here is the code executed on CPU which I used to write my buffer data, where focal_lights is a vector with instances of my FocalLight class (std::vector<FocalLight> focal_lights) which content I checked is correct:

if(block_focal_lights_id != -1) {
    glUniformBlockBinding(programId, block_focal_lights_id, 2);
    //Loading from light vectors
    glGenBuffers(1, &buffer_focal_lights_id);
    glBindBuffer(GL_UNIFORM_BUFFER, buffer_focal_lights_id);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 24 * focal_lights.size(), 0, GL_DYNAMIC_DRAW);
    int offset = 0;
    for (unsigned int i=0; i<focal_lights.size(); i++) {
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].position);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].direction);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].diffuse_intensity);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].specular_intensity);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].apperture_angle);
        offset += 16;
        glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].attenuation);
        offset += 16;
    }
}

I probe to change the kind of data for my f_apperture_angle to vec3 and I can read it with the offsets defined before, but nothing about working with a simple float. I'm sure about the binding of the buffer is correct and I know the problem is on the glBufferData or glBufferSubdata code.

Anyone see the problem?

Finally its working, thanks Rabbid76: 2 focal lights, 2 directional lights and one point light

Upvotes: 1

Views: 1190

Answers (2)

Neithy
Neithy

Reputation: 139

One call to buffer data is all you need.

glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(FocalLight)*focal_lights.size(), focal_lights);

Using multiple small buffer data copies clogs API and driver resulting in very poor performance.

To deal with alignment of std140 (or preferably std430) you just need to add padding to your structures on c++ side or reorder members.

struct FocalLight{
  vec3 f_light_position;
  float f_apperture_angle;
  vec3 f_light_direction;
  float f_attenuation;
  vec3 f_light_diffuse_intensity;
  float pad1;
  vec3 f_light_specular_intensity;
  float pad2;
};

Alternatively, you can use compiler built-in directive for aligment __declspec(align(16))

Upvotes: 1

Rabbid76
Rabbid76

Reputation: 210909

You have to consider special alignment rules when binding data to a std140 standard uniform block layout.

See OpenGL 4.6 API Compatibility Profile Specification; 7.6.2.2 Standard Uniform Block Layout; page 144

When the std140 layout is specified, the offset of each uniform in a uniform block can be derived from the definition of the uniform block by applying the set of rules described below.

  1. If the member is a scalar consuming N basic machine units, the base alignment is N

....

  1. If the member is a three-component vector with components consuming N basic machine units, the base alignment is 4N.

....

  1. If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this substructure are then assigned offsets by applying this set of rules recursively, where the base offset of the first member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.

  2. If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (9).


When you apply this rules to your data structure, this results in the following offsets:

struct FocalLight                    // size 80 (rule 9 and 10)
{
    vec3 f_light_position;           // offset 0  (rule 3 and 10)
    vec3 f_light_direction;          // offset 16 (rule 3)
    vec3 f_light_diffuse_intensity;  // offset 32 (rule 3)
    vec3 f_light_specular_intensity; // offset 48 (rule 3)
    float f_apperture_angle;         // offset 60 (rule 1)
    float f_attenuation;             // offset 64 (rule 1)
};
layout(std140) uniform focalLights{
    FocalLight f_lights[MAX_NUM_TOTAL_LIGHTS];
};

Binding the data:

int offset = 0;
for (unsigned int i=0; i<focal_lights.size(); i++) {

    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].position);

    offset += 16; // rule 3
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].direction);

    offset += 16; // rule 3
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].diffuse_intensity);

    offset += 16; // rule 3
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float) * 3, focal_lights[i].specular_intensity);

    offset += 12; // rule 1
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].apperture_angle);

    offset += 4; // rule 1
    glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(float), &focal_lights[i].attenuation);

    offset += 16; // rules 9 and 10
}

Upvotes: 2

Related Questions