3
votes

I am experiencing the following error in GLSL. Here is the fragment shader:

#version 450 core
#define DIFFUSE_TEX_UNIT   0
#define INDEX_UNIFORM_LOC  0

layout(binding  = DIFFUSE_TEX_UNIT)   uniform  sampler2D colorTex;

#ifdef SOME_SPECIAL_CASE

 layout (location = INDEX_UNIFORM_LOC) uniform uint u_blendMode;

 //...more code here related to the case

 #endif
 //... rest of the code(not important)

Now,when I compile this shader into program without declaring SOME_SPECIAL_CASE,and still set u_blendMode uniform during runtime,I am getting the following error from driver:

GL_INVALID_OPERATION error generated. value is invalid; expected GL_INT or GL_UNSIGNED_INT64_NV.

But I would expect to get an error like this:

GL_INVALID_OPERATION error generated. ' location ' is invalid.

Because there is no location with such an index (0) if I don't set SOME_SPECIAL_CASE preprocessor flag. Then I decided to check what uniform I have got, which requires GL_INT or GL_UNSIGNED_INT64_NV,so I queried uniform name based on its location (zero):

 char buff[20];
 GLsizei len = 0;
 glGetActiveUniformName(prog.progHandle, 0, 20, &len, buff);

And got the name 'colorTex',which is the name of sampler2D uniform that has binding index DIFFUSE_TEX_UNIT ,that is also zero. Untill now,I believed uniform location and binding points do not use same indices and I still believe they don't,because otherwise this shader,when compiled with SOME_SPECIAL_CASE active would fail,as well as many other shaders I have written thru my work history.Hence it looks utterly weird why that sampler2D uniform binding index is affected when I am setting non-existing uniform location,and I also use specific type (GLSL - uint)

glProgramUniform1ui(prog, location, (GLuint)value);

Which also doesn't match the type of sampler2D (so the error is kinda right at least about type mismatch). Is it a driver bug?

One more thing,I tried to check in docs if binding and location indices really overlap and found this statement:

It is illegal to assign the same uniform location to two uniforms in the same shader or the same program. Even if those two uniforms have the same name and type, and are defined in different shader stages, it is not legal to explicitly assign them the same uniform location; a linker error will occur.

This is just absolutely wrong! I have been doing this for years. And tried that again after reading those lines. Having same uniform,with same location in both vertex and fragment shader compiles and works fine.

My setup:

  • NVIDIA Quadro P2000, driver 419.17
  • OpenGL 4.5
  • Windows 10 64bit

Regarding the ability to use same uniform on same location,at least on NVIDIA GPU the following compiles and runs fine:

Vertex shader

#version 450 core
#define MVP_UNIFORM_LOC 2

layout(location = 0)      in vec2 v_Position;
layout(location = MVP_UNIFORM_LOC) uniform mat4 u_MVP;
smooth out vec2 texCoord;
void main()
{
    texCoord = v_Position;
    gl_Position = u_MVP * vec4(v_Position,0.0,1.0); 
}

Fragment shader:

#version 450 core
#define MVP_UNIFORM_LOC 2
#define TEX_MAP_UNIT 5

layout(binding  = TEX_MAP_UNIT ) uniform  sampler2D texMap;
layout(location = MVP_UNIFORM_LOC) uniform mat4 u_MVP;
smooth in  vec2 texCoord;
out vec4 OUTPUT;

 void main()
{
    vec4 tex = texture(texMap, texCoord);    
    OUTPUT =  u_MVP * tex; 
}
1
glGetActiveUniformName FYI: This function takes uniform indices, not uniform locations. Indices are used for all uniform introspection functions (unless it takes a name).Nicol Bolas
Originally, I was pretty sure that the Wiki was right about that (I wrote it, after all). However, I uncovered this quote "Hence, the types, initializers, and any location specifiers of all statically used uniform variables with the same name must match across all shaders that are linked into a single program.", and that throws everything into doubt. Because the question now becomes... are those "two variables" or just the same variable being declared again?Nicol Bolas
And it should be noted, the "any location specifiers" wording was added in GLSL 4.60; it wasn't there in prior versions, and it isn't there in ARB_explicit_uniform_location. So this feels like some kind of bug fix that was never mentionedNicol Bolas

1 Answers

5
votes

Is it a driver bug?

No. glGetActiveUniformName takes uniform indices, not uniform locations. Indices cannot be set from the shader; they're just all of the uniform variables, from 0 to the number of active uniforms. Indices are only used for introspecting properties of uniforms.

There's no way to take a uniform location and ask for the uniform index (or name) of the uniform variable.

But I would expect to get an error like this: ... Because there is no location with such an index (0) if I don't set SOME_SPECIAL_CASE preprocessor flag.

Sure there is. Uniform variables which do not use explicit locations will never have the same location as a uniform variable that does have an explicit location. However, that's not what's happening here.

If SOME_SPECIAL_CASE is not defined, then the declaration of u_blendMode does not exist. Since location 0 was never used by an explicit uniform variable, it is now available for implicit location assignment.

So the implementation can assign the location of colorTex to zero (note that this is different from assigning the binding to zero).

If you want to reserve location 0 always, then the declaration of u_blendMode must always be visible, even if you never use it. The specification allows implementations to still optimize away such declarations, but the explicit location itself is not optimized away. So if you use location = 0 for a uniform that goes unused, then location 0 may or may not be a valid location. But if it is valid, it will always refer to u_blendMode.


Regarding the ability to use same uniform on same location,at least on NVIDIA GPU the following compiles and runs fine:

The GLSL specification has worked this out, and it is now OK to have two explicit uniform locations that are the same, so long as the two declarations are themselves identical. So cross-shader-stage uniform locations are supposed to work.