PDA

View Full Version : GLSL glGetUniformLocation Failing



devmane144
04-26-2016, 05:58 PM
Hello everybody,

I am having a problem with my shaders. I was writing a point light shadow mapping shader in GLSL #120 and I ran into a snag. My uniforms are defined as follows:


const int MAX_LIGHTS = 4;

uniform sampler2D shadowTextSamp0;
uniform sampler2D shadowTextSamp1;
uniform sampler2D shadowTextSamp2;
uniform sampler2D shadowTextSamp3;

uniform samplerCube shadowCube0;
uniform samplerCube shadowCube1;
uniform samplerCube shadowCube2;
uniform samplerCube shadowCube3;


Which is pretty straightforward. But I need to bind a cube map to the samplerCubes, so I used glGetUniformLocation to grab the uniform reference from my programs, but when I call it with the name of shadowCube# whichever it may be, it returns -1. My code for grabbing the handles is as follows:



std::string parseCube = "shadowCube" + str;
std::cout << glGetUniformLocation(inShade->getID(), parseCube.c_str());

where str represents a std::string of an integer, which is meant to get shadowCube#
I checked how the string was made to avoid space errors, and I've made sure the variables are the EXACT same name. I'm not sure where I went wrong...

Could somebody please give me a reason why this may have happened?

Thanks,
Devmane144

GClements
04-26-2016, 06:54 PM
Could somebody please give me a reason why this may have happened?

The most likely reasons are:
1. The shader doesn't actually use the variable in question, so the variable gets deleted during linking (glGetUniformLocation() only works for active uniform variables).
2. The program didn't link successfully (in this case, glGetUniformLocation() will generate a GL_INVALID_OPERATION error).

If you need consistent access to uniform variables regardless of whether or not they are actually used in a particular shader, you need to use named uniform blocks with a shared or std140 layout. Uniform variables in the default uniform block will simply be discarded if they aren't used.

devmane144
04-26-2016, 08:27 PM
The most likely reasons are:
1. The shader doesn't actually use the variable in question, so the variable gets deleted during linking (glGetUniformLocation() only works for active uniform variables).
2. The program didn't link successfully (in this case, glGetUniformLocation() will generate a GL_INVALID_OPERATION error).

If you need consistent access to uniform variables regardless of whether or not they are actually used in a particular shader, you need to use named uniform blocks with a shared or std140 layout. Uniform variables in the default uniform block will simply be discarded if they aren't used.

On what condition are they deleted? Because the other samplers are used exactly the same way almost and they are declared 4 times in the code, just as the other samplers are. Is there a way to explicitly ensure they are never deleted?'

In addition to that, the linking doesn't seem to throw any compilation errors when I check the integrity of the shaders. There's no "this kind of sampler doesn't exist" kind of errors. But maybe there's something I'm missing in compilation? Which function exactly would catch the linking error?

I'm thinking this is a matter of the uniform (which is a global uniform) being deleted, but I'm not sure why.

Devmane144

GClements
04-27-2016, 06:52 PM
On what condition are they deleted? Because the other samplers are used exactly the same way almost and they are declared 4 times in the code, just as the other samplers are.

If the variable's value has no effect upon any the shader's outputs, it will be deleted. So if you e.g. read a value from the sampler via texture() but never use that value, the texture() call will be deleted from the code and the sampler variable itself will be deleted (because, once the dead code has been removed, nothing uses it).



Is there a way to explicitly ensure they are never deleted?'

Use a named uniform block with a "shared" or "std140" layout. These allow a single UBO to be used with multiple shaders regardless of which variables the shader actually uses. Consequently, variables cannot be deleted.

Or you could just not care whether glGetUniformLocation() returns -1. glUniform*() simply does nothing (i.e. no error is generated) if the location parameter is -1.



In addition to that, the linking doesn't seem to throw any compilation errors when I check the integrity of the shaders. There's no "this kind of sampler doesn't exist" kind of errors. But maybe there's something I'm missing in compilation? Which function exactly would catch the linking error?

There may not be any error. Having unused variables isn't an error, it just causes them to be deleted. If there is a linking error, and the resulting program isn't actually valid, then glGetUniformLocation() will generate a GL_INVALID_OPERATION error and its result is (I believe) undefined (but -1 is plausible).



I'm thinking this is a matter of the uniform (which is a global uniform) being deleted, but I'm not sure why.

Would changing the uniform variable to refer to a different texture unit (or changing the contents of the texture itself) actually affect that shader's output in any way? If it doesn't (e.g. because the sampler only exists for "future" code which hasn't yet been written, or because you're accidentally reading the wrong variable somewhere, or whatever), the variable will be deleted.

If you want confirmation, you can list the active uniforms for a shader program with e.g.


GLint count;
glGetProgramiv(inShade->getID(), GL_ACTIVE_UNIFORMS, &count);
for (int i = 0; i < count; i++) {
char name[100];
glGetActiveUniformName(inShade->getID(), i, sizeof(name), (GLsizei*) NULL, name);
std::cout << name << "\n";
}