统一显式位置和绑定点索引

4
我在GLSL中遇到了以下错误。以下是片段着色器代码:
#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)

当我编译此着色器成程序时,没有声明SOME_SPECIAL_CASE,并且在运行时设置u_blendMode uniform变量,驱动程序会生成以下错误:
GL_INVALID_OPERATION 错误生成。value是无效的;期望 GL_INT 或 GL_UNSIGNED_INT64_NV。
但我希望得到这样的错误:
GL_INVALID_OPERATION 错误生成。‘location’ 无效。
因为如果我不设置SOME_SPECIAL_CASE预处理器标志,则没有该索引(0)的位置。然后我决定检查我拥有的uniform变量,它需要GL_INT或GL_UNSIGNED_INT64_NV,所以我基于其位置(零)查询uniform变量名称:
 char buff[20];
 GLsizei len = 0;
 glGetActiveUniformName(prog.progHandle, 0, 20, &len, buff);

获得名称为'colorTex',这是一个名为DIFFUSE_TEX_UNIT的sampler2D uniform的绑定索引,该索引也为零。

到目前为止,我认为uniform位置和绑定点不使用相同的索引,我仍然相信它们不会,因为否则当使用SOME_SPECIAL_CASE编译此着色器时,它将失败,就像我在工作历史中编写的许多其他着色器一样。因此,当我设置不存在的uniform位置并且使用特定类型(GLSL - uint)时,sampler2D uniform绑定索引为什么会受到影响看起来非常奇怪。

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

这也与sampler2D的类型不匹配(因此错误至少在类型不匹配方面是正确的)。这是一个驱动程序的错误吗?

还有一件事,我尝试在文档中检查绑定和位置索引是否真正重叠,并找到了这个声明:

在同一个着色器或同一个程序中将相同的统一变量位置分配给两个统一变量是非法的。即使这两个统一变量具有相同的名称和类型,并且定义在不同的着色器阶段中,也不允许显式地为它们分配相同的统一变量位置;将出现链接器错误。

这是完全错误的!我已经这样做了好多年。在读完这些文字后,我再次尝试了一下。在顶点着色器和片段着色器中具有相同位置的相同uniform编译并正常工作。

我的设置:

  • NVIDIA Quadro P2000,驱动程序版本419.17
  • OpenGL 4.5
  • Windows 10 64位

关于能否在相同位置使用相同的uniform,在至少NVIDIA GPU上,以下代码可以编译并正常运行:

顶点着色器:

#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); 
}

片段着色器:

#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该函数使用 uniform 的 indices,而不是 uniform 的 locations。对于所有 uniform introspection functions 都会使用 indices(除非它接受名称)。 - Nicol Bolas
最初,我非常确定维基百科是正确的(毕竟我写了它)。然而,我发现了这句话:“因此,在链接到单个程序中的所有着色器中,具有相同名称的所有静态使用的统一变量的类型、初始化程序和任何位置指示符必须匹配。”,这让一切都成了疑问。因为问题现在变成了...那些是“两个变量”还是只是再次声明了同一个变量? - Nicol Bolas
需要注意的是,“任何位置指示符”这个词语是在GLSL 4.60中添加的;在之前的版本中它是不存在的,而且在ARB_explicit_uniform_location中也没有。因此,这感觉像是一种从未被提及的错误修复。 - Nicol Bolas
1个回答

5

这是驱动程序的错误吗?

不是。 glGetActiveUniformName 需要统一的 索引,而不是统一的 位置。 索引不能从着色器中设置; 它们只是所有统一变量,从0到活动统一变量的数量。 索引仅用于 检查统一变量的属性

没有办法获取统一位置并询问统一变量的统一索引(或名称)。

但是如果我不设置SOME_SPECIAL_CASE预处理器标志,我希望会得到这样一个错误:

...

因为没有该索引(0)的位置。

当然可以。 不使用显式位置的统一变量永远不会具有与具有显式位置的统一变量相同的位置。 但是,这里发生的并非如此。

如果SOME_SPECIAL_CASE未定义,则u_blendMode的声明不存在。由于位置0从未被显式统一变量使用,因此现在可用于隐式位置分配。
因此,实现可以将colorTex的位置分配为零(请注意,这与将binding分配为零是不同的)。
如果您想始终保留位置0,则必须始终可见u_blendMode的声明,即使您从未使用过它。规范允许实现仍然优化掉这样的声明,但是显式位置本身不会被优化掉。因此,如果您对未使用的统一变量使用location = 0,则位置0可能是有效的,也可能无效。但是,如果它有效,则将始终引用u_blendMode
关于在同一位置使用相同的统一变量,在NVIDIA GPU上,以下代码可以编译并正常运行:
GLSL规范已经解决了这个问题,只要两个声明本身是相同的,现在有两个显式统一变量位置可以相同,因此跨着色器阶段的统一变量位置应该可以工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接