2
votes

I'm having trouble understanding the std140 layout for GLSL uniform buffer objects. I'm under the impression that in the following uniform block, the int will begin at offset 0 and the matrix will begin at offset 16. The following uniform is giving me a bad matrix, apparent because nothing draws on-screen.

layout (std140) uniform Camera
{
    int renderMode;
    mat4 projection;
} camera;

The point of renderMode is that it tells me that the uniform update code doesn't work.

I have the following code (homegrown) to help me along. This is the code that makes the Open GL calls in my C++ application. This code is inside a class called UniformBufferObject.

#define UpdateExData(o, s, d) glBindBuffer(GL_UNIFORM_BUFFER, _uboId); \
    glBufferSubData(GL_UNIFORM_BUFFER, o, s, d); \
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

int Alignment(int offset, int alignment)
{
    int leftOver = offset % alignment;
    if (leftOver > 0)
    {
        return offset + (alignment - leftOver);
    }
    else
    {
        return offset;
    }
}

template<typename T, typename... Args>
void UpdateEx(int offset, const std::vector<T>& data, Args&... args)
{
    auto mySize = sizeof(T) * data.size();
    int myAlignment = Alignment(offset, 16); // fixed for vectors of vec4 for now
    UpdateExData(myAlignment, mySize, data.data());
    UpdateEx(myAlignment + mySize, args...);
}

template<typename... Args>
void UpdateEx(int offset, int& data, Args&... args)
{
    auto mySize = sizeof(int);
    int myAlignment = Alignment(offset, mySize); // assume 4 byte alignment for ints
    UpdateExData(myAlignment, mySize, &data);
    UpdateEx(myAlignment + mySize, args...);
}

template<typename... Args>
void UpdateEx(int offset, const glm::mat4& data, Args&... args)
{
    auto mySize = sizeof(glm::mat4);
    int myAlignment = Alignment(offset, 16); // assume 16-byte alignment for mat4
    UpdateExData(myAlignment, mySize, &data);
    UpdateEx(myAlignment + mySize, args...);
}

The line of code that initiates the update is as follows. m is an integer. cam is glm::mat4. The purpose of this piece is to update the camera in my shaders.

cameraUbo->UpdateEx(0, m, cam);

If I flip the uniform around such that the matrix is first, and update the call above to cameraUbo->UpdateEx(0, cam, m), the matrix updates but renderMode no longer works.

I really have no idea what's wrong and what really confuses me is that GL_UNIFORM_BLOCK_DATA_SIZE returns well beyond the 80 that I expect. I get different values, above 1000, across the 5 shaders it's in.

I do have another uniform that appears to work perfectly, and has the same size across the two shaders in appears in.

struct TowerLight
{
    vec4 position;
    vec4 color;
};

layout(std140) uniform Towers
{
    int lightCount;
    TowerLight lights[MaxLights];
};

With the following code. count is an integer and lights is std::vector<struct_of_2_vec4>.

ubo->UpdateEx(0, towerCount, lights);

[Edit]

This might be a bug in my video card driver. I have a Radeon 6870. If I use the default uniform block layout or drop my shader version to 430 from 440, I get a block size of 80 across all shaders.

2

2 Answers

3
votes

This page explains well how alignment works http://learnopengl.com/#!Advanced-OpenGL/Advanced-GLSL

layout (std140) uniform ExampleBlock
{
    //               // base alignment  // aligned offset
    float value;     // 4               // 0 
    vec3 vector;     // 16              // 16  (must be multiple of 16 so 4->16)
    mat4 matrix;     // 16              // 32  (column 0)
                     // 16              // 48  (column 1)
                     // 16              // 64  (column 2)
                     // 16              // 80  (column 3)
    float values[3]; // 16              // 96  (values[0])
                     // 16              // 112 (values[1])
                     // 16              // 128 (values[2])
    bool boolean;    // 4               // 144
    int integer;     // 4               // 148
}; 
0
votes

Video card driver thing. Doesn't support Open GL 4.5.