7

How do I assign an explicit uniform location when I want to use the uniform in different shader stages of the same program?

When automatic assignment is used, uniforms in different stages are assigned to the same location when the identifiers match. But how can I define the location in the shader using the

layout (location = ...)

syntax?

Following quote from: https://www.opengl.org/wiki/Uniform_(GLSL)/Explicit_Uniform_Location

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.

Following quote from the GLSL Spec:

No two default-block uniform variables in the program can have the same location, even if they are unused, otherwise a compile-time or link-time error will be generated.

I'm using OpenGL 4.3.

Due to immense READING THE CODE, I figured out, that the uniform is unused. That leads to the following situation: On a GTX 780 the following code runs without problems (although it seems it shouldn't). On an Intel HD 5500 onboard graphics chip the code produces a SHADER_ID_LINK error at link time, according to the GL_ARB_DEBUG_OUTPUT extension. It states, that the uniform location overlaps another uniform.

Vertex Shader:

#version 430 core

layout(location = 0) in vec4 vPosition;
layout(location = 2) in vec4 vTexCoord;

layout(location = 0) uniform mat4 WorldMatrix; // <-- unused in both stages

out vec4 fPosition;
out vec4 fTexCoord;

void main() { ... }

Fragment Shader:

#version 430 core

in vec4 fPosition;
in vec4 fTexCoord;

layout(location = 0) out vec4 Albedo;
layout(location = 1) out vec4 Normal;

layout(location = 0) uniform mat4 WorldMatrix; // <-- unused in both stages
layout(location = 1) uniform mat4 InverseViewProjectionMatrix;
layout(location = 2) uniform samplerCube Cubemap;

void main() { ... }

However, when the uniform is used, no problems occour. Assumed I interpret the GLSL Spec right, this seems to be not as it's supposed. Although, this is exactly how I would like it to function.

Still, there is the problem of overlapping uniforms, when the uniform is not used.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Henkk
  • 609
  • 6
  • 18
  • What OpenGL version are you using? – Reto Koradi Sep 05 '15 at 20:00
  • OpenGL version is 4.3 – Henkk Sep 05 '15 at 20:04
  • After reading both the original extension spec and the latest GL and GLSL specs, I'm still not sure what the expected behavior is. The only place I find which forbids anything is the "No two default-block uniform variables in the program can have the same location, even if they are unused, otherwise a compile-time or link-time error will be generated" quote you already discovered. But using the same uniform in different stages isn't using _two_ uniforms in my book. But that might be just my interpretation. – derhass Sep 07 '15 at 19:06
  • That interpretation sounds reasonable. I think under normal circumstances (no unused uniforms) everything works as it should. But when the uniform is not used, the behaviour is highly misleading. – Henkk Sep 07 '15 at 20:50

1 Answers1

3

see complete GL+VAO/VBO+GLSL+shaders example in C++

  • extracted from that example:

On GPU side:

#version 400 core
layout(location = 0) in vec3 pos;
  • you need to specify GLSL version to use this
  • not sure from which they add layout location but for 400+ it will work for sure

  • VBO pos is set to location 0

On CPU side:

// globals
GLuint vbo[4]={-1,-1,-1,-1};
GLuint vao[4]={-1,-1,-1,-1};
const GLfloat vao_pos[]=
    {
//  x    y    z     //ix
    -1.0,-1.0,-1.0, //0
    +1.0,-1.0,-1.0, //1
    +1.0,+1.0,-1.0, //2
    -1.0,+1.0,-1.0, //3
    -1.0,-1.0,+1.0, //4
    +1.0,-1.0,+1.0, //5
    +1.0,+1.0,+1.0, //6
    -1.0,+1.0,+1.0, //7
    };
// init
GLuint i;
glGenVertexArrays(4,vao);
glGenBuffers(4,vbo);
glBindVertexArray(vao[0]);

i=0; // VBO location
glBindBuffer(GL_ARRAY_BUFFER,vbo[i]);
glBufferData(GL_ARRAY_BUFFER,sizeof(vao_pos),vao_pos,GL_STATIC_DRAW);
glEnableVertexAttribArray(i);
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);

when you attach data to location

then you need to have set the layout location for it the same in all shaders it use it inside single stage. You can not assign the same location to more than one VBO at once (that is the state you copied all about).

By single stage is meant single/set of glDrawArrays/glDrawElements calls without changing shader setup. If you have more shader program stages (more than one of fragment/vertex/geometry...) then the location can be set differently for each stage but inside each stage all its shader programs must have the same location setup.

By single stage start you can assume each glUseProgram(prog_id); call and it ends by glUseProgram(0); or another stage start ...

[edit2] here are the uniforms for non nVidia drivers

vertex shader:

// Vertex
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location = 0) in vec3 pos;
layout(location = 2) in vec3 nor;
layout(location = 3) in vec3 col;
layout(location = 0) uniform mat4 m_model;  // model matrix
layout(location =16) uniform mat4 m_normal; // model matrix with origin=(0,0,0)
layout(location =32) uniform mat4 m_view;   // inverse of camera matrix
layout(location =48) uniform mat4 m_proj;   // projection matrix
out vec3 pixel_pos;     // fragment position [GCS]
out vec3 pixel_col;     // fragment surface color
out vec3 pixel_nor;     // fragment surface normal [GCS]
void main()
    {
    pixel_col=col;
    pixel_pos=(m_model*vec4(pos,1)).xyz;
    pixel_nor=(m_normal*vec4(nor,1)).xyz;
    gl_Position=m_proj*m_view*m_model*vec4(pos,1);
    }

fragment shader:

// Fragment
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location =64) uniform vec3 lt_pnt_pos;// point light source position [GCS]
layout(location =67) uniform vec3 lt_pnt_col;// point light source color&strength
layout(location =70) uniform vec3 lt_amb_col;// ambient light source color&strength
in vec3 pixel_pos;      // fragment position [GCS]
in vec3 pixel_col;      // fragment surface color
in vec3 pixel_nor;      // fragment surface normal [GCS]
out vec4 col;
void main()
    {
    float li;
    vec3 c,lt_dir;
    lt_dir=normalize(lt_pnt_pos-pixel_pos); // vector from fragment to point light source in [GCS]
    li=dot(pixel_nor,lt_dir);
    if (li<0.0) li=0.0;
    c=pixel_col*(lt_amb_col+(lt_pnt_col*li));
    col=vec4(c,1.0);
    }

These are rewritten shaders from the linked example with layout location used for uniforms. You have to add:

  • #extension GL_ARB_explicit_uniform_location : enable

for 400 profile to make it work

On CPU side use glGetUniformLocation as usual

id=glGetUniformLocation(prog_id,"lt_pnt_pos"); glUniform3fv(id,1,lt_pnt_pos);
id=glGetUniformLocation(prog_id,"lt_pnt_col"); glUniform3fv(id,1,lt_pnt_col);
id=glGetUniformLocation(prog_id,"lt_amb_col"); glUniform3fv(id,1,lt_amb_col);
glGetFloatv(GL_MODELVIEW_MATRIX,m);
id=glGetUniformLocation(prog_id,"m_model"   ); glUniformMatrix4fv(id,1,GL_FALSE,m);
m[12]=0.0; m[13]=0.0; m[14]=0.0;
id=glGetUniformLocation(prog_id,"m_normal"  ); glUniformMatrix4fv(id,1,GL_FALSE,m);
for (i=0;i<16;i++) m[i]=0.0; m[0]=1.0; m[5]=1.0; m[10]=1.0; m[15]=1.0;
id=glGetUniformLocation(prog_id,"m_view"    ); glUniformMatrix4fv(id,1,GL_FALSE,m);
glGetFloatv(GL_PROJECTION_MATRIX,m);
id=glGetUniformLocation(prog_id,"m_proj"    ); glUniformMatrix4fv(id,1,GL_FALSE,m);

Or the defined position:

id=64; glUniform3fv(id,1,lt_pnt_pos);
id=67; glUniform3fv(id,1,lt_pnt_col);
id=70; glUniform3fv(id,1,lt_amb_col);
glGetFloatv(GL_MODELVIEW_MATRIX,m);
id= 0; glUniformMatrix4fv(id,1,GL_FALSE,m);
m[12]=0.0; m[13]=0.0; m[14]=0.0;
id=16; glUniformMatrix4fv(id,1,GL_FALSE,m);
for (i=0;i<16;i++) m[i]=0.0; m[0]=1.0; m[5]=1.0; m[10]=1.0; m[15]=1.0;
id=32; glUniformMatrix4fv(id,1,GL_FALSE,m);
glGetFloatv(GL_PROJECTION_MATRIX,m);
id=48; glUniformMatrix4fv(id,1,GL_FALSE,m);

Looks like nVidia compiler handles the locations differently. In case it does not work properly try workaround for buggy drivers to set locations with different step per data type:

  • 1 location: float,int,bool
  • 2 locations double
  • 3 locations vec3
  • 4 locations vec4
  • 6 locations dvec3
  • 8 locations dvec4
  • 9 locations mat3
  • 16 locations mat4
  • etc ...
Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 2
    Sadly, this only describes the situation concerning attributes and not uniforms. The sample does not even use uniform locations. – Henkk Sep 07 '15 at 09:20
  • 1
    Thanks for the effort, but this example does not use the same uniform in different stages. This does not answer my question. – Henkk Sep 07 '15 at 11:14
  • 1
    Imagine you want to use "uniform mat4 m_model;" in the fragment shader. Or recheck my question. I added a few lines to make this more clear. – Henkk Sep 07 '15 at 11:16
  • @Henkk see [edit2] reindexed the locations according to your driver specs you most likely overlap another uniform ... as mat4 takes 16 locations and starts from `0` then your next free location is `16` if you place something below that then the uniforms overlap (you should post your code to be sure of this) it looks like my nVidia handles locations differently (more logically but not according to specs in your link) – Spektre Sep 07 '15 at 15:17
  • @Henkk your `WorldMatrix` is the same uniform in both vertex and fragment shader so the location must be the same that is OK on your side problem is either with overlap with different uniform (location < 16) or some Intel driver bug/issue (they have a lot of them) – Spektre Sep 07 '15 at 15:25
  • 1
    Where do these offsets (16 locations for mat4 etc.) come from? I can't find anything about them in the GLSL spec. To me it looks like incremental locations are only assigned to arrays and structs. – Henkk Sep 07 '15 at 17:18
  • @Henkk mat4 is in real array of 16 floats so if Intell handles it as an array type it should take 16 locations ... nVidia handles it as non array type. Just try if it works – Spektre Sep 07 '15 at 18:16
  • 3
    @Spektre: The [`GL_ARB_explicit_uniform_location` spec](https://www.opengl.org/registry/specs/ARB/explicit_uniform_location.txt) explicitely states: "Each transparent basic-type uniform variable **consumes exactly one location, regarless of it's type**. Transparent basic-types, as defined in section 4.1 are booleans, integers, floats, doubles, vectors and **matrices**." – derhass Sep 07 '15 at 18:51
  • @Henkk You are assuming the Intel driver behaves according GL specs I have many times see different behavior on Intel gfx drivers with newer stuff... – Spektre Sep 07 '15 at 20:29
  • 2
    @Spektre: "You are assuming the Intel driver behaves according GL specs I have many times see different behavior on Intel gfx drivers with newer stuff..." Then that's a driver bug, and you should explicitly point out that it's a workaround for such a bug. You should *not* suggest (as your answer does here) that uniform locations are *byte offsets*. – Nicol Bolas Dec 14 '15 at 05:02
  • @NicolBolas edited answer debulleted + added your suggestion. BTW it is not BYTE offset but looks more like gfx WORD/register pointer/location Offset. – Spektre Dec 14 '15 at 10:03