Problem with OpenGL Introspection API

I’m trying to create a simple function that queries an SSBO in some shader.
It should return the name of the field, its offset, and its count (if the field is an array).

This is how the SSBO is defined in my shader

struct Test_Struct
{
    vec2 Vec2_1;
    vec2 Vec2_2;
};

layout(std430, binding = 0) readonly buffer Input
{
    float[10] floats;

    Test_Struct[10] test_structs;
};

I simply want to retrieve every field that is defined inside the SSBO.
Currently I’m using OpenGL’s Introspection API, when I run my function it yields the following results.

“floats[0]”, with ‘10’ elements at offset ‘0’
“test_structs[0].Vec2_1” with ‘1’ element at offset=40
“test_structs[0].Vec2_2”, with ‘1’ element at offset=48

The float array with 10 elements is correct.
The problem is with the structs. No matter what I tried it seems like OpenGL “skips(?)” the structs as fields and instead “inlines” the struct definition inside the SSBO. Moreover, it seems like the rest of test_structs array is ignored, it returns only 1 element, this only happens with structs.

I built my function around this StackOverflow answer, the code is somewhat similar.
Am I using the right tool for this? Is there a better option?

Edit:
Just to be clear, this is the output I’m expecting to get

“floats[0]”, with ‘10’ elements at offset ‘0’
“test_structs” with ‘10’ elements at offset=40

The first section in that program introspection page is about how names work, which defines what you will see from introspecting a shader. If you look at the first paragraph of the arrays section, it tells you that arrays of basic types don’t work like arrays of non-basic types.

The long and short of it is that every distinct introspectable quantity in OpenGL is either a basic type or an array of basic types. These are the atomic, queriable constructs in the language. Anything which is not atomic is recursively broken down into a sequence of such atomics. So every structure is broken down into its atomic components. But this also means that an array of structures (or array of arrays) is also broken down into each array element, which recursively creates a distinct queryable name for array element and every member field in that array.

So you should get 10 pairs of test_struct[X].Vec2_1 and test_struct[X].Vec2_2, where X goes from 0 to 9. As such, structs are kind of a convenience feature in GLSL; they don’t really exist at the level of the introspection API.

So you’re never going to get two outputs. You ought to get 21 (one for the array, 20 for the 10 pairs of struct members). If you’re not getting that, then either there is a bug in your querying code or a bug in the driver.

Thank you for the reply.
What you said in the last paragraph is interesting.
When I run glGetProgramResourceiv with GL_NUM_ACTIVE_VARIABLES as the prop, I get ‘3’, but according to what you wrote I should be getting much more.
Also if I use GL_BUFFER_DATA_SIZE I get ‘200’ which is correct, I seem to have lost 180 bytes.
This might actually be a Driver bug, I’ll have to do some tests…

Here is the actual code I’m using, maybe there’s something I’m overlooking

// Number of active SSBO variables
static constexpr GLenum bufferDataSize = GL_BUFFER_DATA_SIZE;
GLint ssboSize = 0;

glGetProgramResourceiv(shaderProgram.GetProgramID(), GL_SHADER_STORAGE_BLOCK, ssboIndex, 1, &bufferDataSize, 1, nullptr, &ssboSize);

// Number of active SSBO variables
static constexpr GLenum numberOfActiveVariablesProperty = GL_NUM_ACTIVE_VARIABLES;
GLint numberOfVariables = 0;

glGetProgramResourceiv(shaderProgram.GetProgramID(), GL_SHADER_STORAGE_BLOCK, ssboIndex, 1, &numberOfActiveVariablesProperty, 1, nullptr, &numberOfVariables);
        
_ssboElements.reserve(numberOfVariables);


// SSBO variable indices
static constexpr  GLenum activeVariablesProperty = GL_ACTIVE_VARIABLES;
std::vector<GLint> variableIndices = std::vector<GLint> (numberOfVariables);

glGetProgramResourceiv(shaderProgram.GetProgramID(), GL_SHADER_STORAGE_BLOCK, ssboIndex, 1, &activeVariablesProperty, numberOfVariables, nullptr, variableIndices.data());


// SSBO variable offsets
for(std::size_t i = 0; i < variableIndices.size(); ++i)
{
    // Get SSBO variable array size
    int arraySize = 0;

    static constexpr GLenum arraySizeProperty = GL_ARRAY_SIZE;
    glGetProgramResourceiv(shaderProgram.GetProgramID(), GL_BUFFER_VARIABLE, variableIndices[i], 1, &arraySizeProperty, sizeof(arraySize), nullptr, &arraySize);

    // Length of variable name 
    int nameBufferLength = 0;

    static constexpr GLenum nameLengthProperty = GL_NAME_LENGTH;
    glGetProgramResourceiv(shaderProgram.GetProgramID(), GL_BUFFER_VARIABLE, variableIndices[i], 1, &nameLengthProperty, sizeof(nameBufferLength), nullptr, &nameBufferLength);

    // SSBO variable name
    std::string name;
    name.resize(static_cast<std::size_t>(nameBufferLength) - 1);

    glGetProgramResourceName(shaderProgram.GetProgramID(), GL_BUFFER_VARIABLE, variableIndices[i], nameBufferLength, nullptr, name.data());


    GLint offset = 0;

    // SSBO variable offset
    static constexpr GLenum offsetProperty = GL_OFFSET;
    glGetProgramResourceiv(shaderProgram.GetProgramID(), GL_BUFFER_VARIABLE, variableIndices[i], 1, &offsetProperty, sizeof(GLint), nullptr, &offset);


    _ssboElements.insert(std::make_pair(name, SSBOElement(offset, arraySize)));
};

I don’t immediately see any problems with this code, but I do wonder what exactly the purpose of this is. You specified std430 layout in your shader. As such, you don’t need to query the layout; it’s well-defined.

So unless your code is unaware of what’s in the SSBO, there’s no real reason to query the layout. Just copy an equivalently-laid-out C++ struct into the buffer.

I’m tired of specifying buffer offsets manually, I’m trying to create a tool that will do the “heavy-lifting” for me. I want to query the SSBO so I can cache the offsets, and later when I want to update the field’s value, I want to call it by name, and use the queried offset to use in glBufferSubData

I ran this code on 3 separate computers, they all had the same result as I did.
This tells me that there’s maybe a problem with my function. @Alfonse_Reinheart, it would be of great help if you could please take a closer look at it, to see where my mistake is

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.