4
\$\begingroup\$

I've been working in my off-time to do some system design for a game engine as a fun and challenging project, and i've hit a bit of a wall. I've made an interface which is a renderer that i try to incorporate elements similar to both d3d11 and opengl so that implementations of both are still very functional behind said interface. In the process of working with abstracting shaders, i was wondering if there was a way to actually enumerate the uniforms / cbuffers in a shader using shader reflection, rather than query for them by name. In openGL, the only way to set a variable is if you query for it by name (I think), and in d3d11 you are able use the ID3D11ShaderReflection interface, but are only able to query cbuffers and variables by name. My knowledge is pretty new in opengl, but i was wondering if there was a way to grab or make a list of all the uniforms / cbuffers present in the shader, so that i could query the rest of my engine for the values needed.

What this would do for me is simple: Rather than my engine requiring compiled knowledge beforehand of what variables some specific shader might need, it can instead obtain the names and typenames of all the variables it requires to function correctly. Then i can code a resource container that can hold any number of types and resources, which are associated by variable name in a map or similar, which would allow me to compare the names of the values in the resource container, with what the shader required, and then tell you which uniform /cbuffer value you didn't bind to something. Is this possible, because it sounds incredibly useful.

\$\endgroup\$
4
\$\begingroup\$

I can only answer from OpenGL point of view. Yes you can do what you are looking for. You can query shader information using glGetProgrami, glGetActiveUniform and glGetActiveAttrib

E.g. the following code queries the available uniforms from a shader:

int maxLength;
glGetProgramiv(shaderProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
char name[...]; // Size should be allocated to be maxLength + 1

int uniformCount;
glGetProgramiv(shaderProgram, GL20.GL_ACTIVE_UNIFORMS, &uniformCount);

for (int i = 0; i < uniformCount; i++) {
    int size;
    int type;
    glGetActiveUniform(shaderProgram, i, maxLength, NULL, &size, &type, name);
}

Note that the returned name can depend on the driver implementation. For array uniforms nVidia drivers return uniformName[0] whereas AMD drivers return just uniformName. I recommend checking for this and removing the trailing [0] if present. Also if you are mixing programmable and fixed function pipeline, the returned uniforms can contain built-in uniforms in addition to your custom ones. So another advice is to skip uniform names starting with gl_.

\$\endgroup\$
6
\$\begingroup\$

Yes, you can do this with d3d11 shader reflection. Here is some code showing how to iterate over a constant buffer, as well as grab all the values within the cbuffer. This doesn't require you to know anything about the shader.

ID3D11ShaderReflection* reflection = NULL;
D3D11Reflect(description->buffer, description->length, &reflection);

D3D11_SHADER_DESC desc;
reflection->GetDesc(&desc);

//
//Find all constant buffers
//
for(unsigned int i = 0; i < desc.ConstantBuffers; ++i)
{
    unsigned int register_index = 0;
    ID3D11ShaderReflectionConstantBuffer* buffer = NULL;
    buffer = reflection->GetConstantBufferByIndex(i);

    D3D11_SHADER_BUFFER_DESC bdesc;
    buffer->GetDesc(&bdesc);

    for(unsigned int k = 0; k < desc.BoundResources; ++k)
    {
        D3D11_SHADER_INPUT_BIND_DESC ibdesc;
        reflection->GetResourceBindingDesc(k, &ibdesc);

        if(!strcmp(ibdesc.Name, bdesc.Name))
            register_index = ibdesc.BindPoint;
    }

    //
    //Add constant buffer
    //
    ConstantShaderBuffer* shaderbuffer = new ConstantShaderBuffer(register_index, Engine::String.ConvertToWideStr(bdesc.Name), buffer, &bdesc);
    mShaderBuffers.push_back(shaderbuffer);
}

And then grabbing variables within the cbuffer.

//
//Populate constant buffer with variables
//
for(unsigned int j = 0; j < desc->Variables; ++j)
{
    ID3D11ShaderReflectionVariable* variable = NULL;
    variable = buffer->GetVariableByIndex(j);

    D3D11_SHADER_VARIABLE_DESC vdesc;
    variable->GetDesc(&vdesc);

    ShaderVariable* shadervariable = new ShaderVariable();
    shadervariable->name = Engine::String.ConvertToWideStr(vdesc.Name);
    shadervariable->length = vdesc.Size;
    shadervariable->offset = vdesc.StartOffset; 
    mSize += vdesc.Size;
    mVariables.push_back(shadervariable);
}

Note, I am clearly using some of my own classes in this situation to store the information in a format my system prefers.

\$\endgroup\$
  • \$\begingroup\$ Awesome, my question has been answered perfectly, but between 2 of the answers. If you include a little on how to do this in openGL like from the other answers, this will be the complete answer. Thanks for the d3d perspective though, i was about to give up hope. \$\endgroup\$ – FatalCatharsis Sep 18 '13 at 20:24
2
\$\begingroup\$

The OpenGL functions glGetActiveUniform and glGetActiveAttrib will grab information such as the variable's type and name for an uniform/attrib at a given index

glGetProgramiv can get various bits of information related to a shader program, such as the total number of uniforms and attributes.

The first answer for this stack overflow link shows an example of how to load information for all uniforms in a shader https://stackoverflow.com/questions/4783912/how-can-i-find-a-list-of-all-the-uniforms-in-opengl-es-2-0-vertex-shader-pro

\$\endgroup\$
  • \$\begingroup\$ That's epic, but i'm only going to be able to use this if it is available in both opengl and d3d11. Would you happen to know of any way to do this in d3d11? \$\endgroup\$ – FatalCatharsis Sep 18 '13 at 5:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.