为什么 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会 return 产生垃圾并使我的程序崩溃?
Why does glGetProgramiv GL_ACTIVE_UNIFORMS occasionally return garbage and crash my program?
我从此处找到的 Anton 的 opengl 教程配套代码中改编了一些代码:
https://github.com/capnramses/antons_opengl_tutorials_book/blob/master/02_shaders/main.c
在测试 print_all 或 dump_all 函数以打印有关着色器的所有内容时,我在日志中注意到 glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, ¶ms)
将放置一个非常负的垃圾值(并且由于 for 循环检查而没有做任何事情)或非常积极并且在 glGetActiveUniform(shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)
处使我的程序崩溃,因为它只应该处理 i
到 NUM_ACTIVE_UNIFORMS - 1
的值;我相信它不会期望超过 100 万。我还检查了调用后 glGetError 的结果,没有设置错误标志。
这是我的转储函数和我的日志图片:
void dump_all(FILE* file, GLuint shader_program)
{
int params = -1;
fprintf(file, "--------------------\nshader program %i info:\n", shader_program);
glGetProgramiv (shader_program, GL_LINK_STATUS, ¶ms);
fprintf(file, "GL_LINK_STATUS = %i\n", params);
glGetProgramiv (shader_program, GL_ATTACHED_SHADERS, ¶ms);
fprintf(file,"GL_ATTACHED_SHADERS = %i\n", params);
glGetProgramiv (shader_program, GL_ACTIVE_ATTRIBUTES, ¶ms);
fprintf(file, "GL_ACTIVE_ATTRIBUTES = %i\n", params);
const int MAX_LENGTH = 64;
int i;
for (i = 0; i < params; i++)
{
char name[MAX_LENGTH];
int actual_length = 0; // not used atm.
int size = 0;
GLenum type;
glGetActiveAttrib (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name);
if (size > 1)
{
int j;
for (j = 0; j < size; j++)
{
char long_name[MAX_LENGTH];
int location;
sprintf (long_name, "%s[%i]", name, j);
location = glGetAttribLocation (shader_program, long_name);
fprintf (file, " %i) type:%s name:%s location:%i\n",
i, gl_type_to_string(type), long_name, location);
}
}
else
{
int location = glGetAttribLocation (shader_program, name);
fprintf(file, " %i) type:%s name:%s location:%i\n", i, gl_type_to_string (type), name, location);
}
}
printf("\nbefore the active uniform call\n");
glGetProgramiv (shader_program, GL_ACTIVE_UNIFORMS, ¶ms);
GLenum error = glGetError();
printf("\nThere is an error (0 for no error and 1 for an error): %d\n", error != GL_NO_ERROR);
printf("\nglGetError: %d\n", error);
printf("\nafter the get active uniform call\n");
fprintf(file, "GL_ACTIVE_UNIFORMS = %i\n", params);
for (i = 0; i < params; i++)
{
char name[MAX_LENGTH];
int actual_length = 0; // not used atm.
int size = 0;
GLenum type;
printf("\nright before the thing\n");
glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name);
printf("\nright after the thign\n");
if (size > 1)
{
printf("\nin the if block\n");
int j;
for (j = 0; j < size; j++)
{
char long_name[MAX_LENGTH];
int location;
sprintf (long_name, "%s[%i]", name, j);
location = glGetUniformLocation (shader_program, long_name);
fprintf(file, " %i) type:%s name:%s location:%i\n",
i, gl_type_to_string (type), long_name, location);
}
}
else
{
printf("\nin the else block\n");
int location = glGetUniformLocation (shader_program, name);
fprintf(file, " %i) type:%s name:%s location:%i\n",
i, gl_type_to_string (type), name, location);
}
}
print_program_info_log(file, shader_program);
}
我的负数时的日志:http://i.stack.imgur.com/Xu9nq.png
当值很大并导致程序崩溃时我的日志:http://i.stack.imgur.com/QBP1o.png
注意:我是运行来自无法找到文件的目录的应用程序。此外,我还没有在任何地方调用 glUseProgram() 。因此,我正在转储尚未成功链接或什至已附加任何成功编译的着色器的内容。
这是间歇性问题;大多数时候,日志会正确打印出 0 表示活动制服。到目前为止,我已经看到了三种结果; 1.) 数字太大而崩溃。 2.) 这个数字是疯狂的负数并且不会崩溃。 3.) 数字很大但不是太大并且在崩溃调用下面的循环中花费了大量时间,只是打印出垃圾。
这是驱动程序错误,还是我在做一些本质上未定义的事情?
编辑 1:
在调用glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, ¶ms)
returns 一个巨大的数字的情况下,对 glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)
的调用在它下面的 for 循环的第一次迭代中崩溃,值为零对于 i
。但是,当 glGetProgramiv
returns 0 用于主动制服时,调用 glGetActiveUniform
和 0 用于 i
不会崩溃(我将 for 循环硬编码为绕一圈)。这让我觉得这里发生的事情不仅仅是返回给我的未初始化数据。
编辑 2
根据要求,这是一个给出奇怪值的最小示例程序:
#include <stdio.h>
#undef main
int main(int argc, char ** argv)
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_Window *window = SDL_CreateWindow("Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
if(!window)
{
printf("no window created : %s\n", SDL_GetError());
}
SDL_GLContext context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, context);
SDL_GL_SetSwapInterval(1);
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if(err != GLEW_OK)
{
printf("glew failed to init: %s", glewGetErrorString(err));
return -1;
}
GLuint program = glCreateProgram();
glLinkProgram(program); // I can create and attach shaders, compile them, or whatever; I get the same result.
GLint num;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &num);
printf("NUM UNIFORMS: %d", num);
int run_loop = 1;
while(run_loop)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
SDL_Quit();
run_loop = 0;
break;
default:
break;
}
}
SDL_GL_SwapWindow(window);
}
return 0;
}
在无法 link 的程序上调用 glGetProgramiv(GL_ACTIVE_UNIFORMS) 是一个非常糟糕的主意。
很可能驱动程序从未到达进程中设置该值的位置,只是将未初始化的内存返回给您。
这是否构成驱动程序错误尚有争议,因为程序处于无效状态。
查询未能link的程序的制服数(或任何其他状态)在 GL 中有效。 OpenGL 4.3 core profile specification 在第 7.3.1 节中指出:
If a program is linked unsuccessfully, the link may have failed for a
number of reasons, including cases where the program required more
resources than supported by the implementation. Implementations are
permitted, but not required, to record lists of resources that would
have been considered active had the program linked successfully. If an
implementation does not record information for any given interface,
the corresponding list of active resources is considered empty. If a
program has never been linked, all lists of active resources are
considered empty.
连同第 7.13 节关于 glGetProgramiv():
If pnam
e is ACTIVE_ATTRIBUTES
, the number of active attributes (see
section 7.3.1) in program is returned. If no active attributes exist,
zero is returned. If pname is ACTIVE_UNIFORMS
, the number of active
uniforms in program is returned. If no active uniforms exist, zero is
returned.
请注意,第 7.3.1 节在此引文中仅被引用一次,但它定义 "resources" 以包括制服和属性(以及其他)。
一般来说,GL 永远不会 return 向您提供未初始化的数据 - 除非您查询您自己负责填充的内容(例如缓冲区、帧缓冲区等)。 glGet*()
调用要么成功(和 returns 有效数据),要么因某些 GL 错误而失败(并且不向参数指向的内存写入任何内容)。
如果您看到一些未初始化的 GL 状态,那很可能是驱动程序错误。请注意,查询未能 link 的程序的资源是一个特别不寻常的(实际上也是非常无用的)操作(我从未尝试过这样做),所以这可能是一个未经充分测试的代码路径和我可以想象你可能会遇到一些驱动程序错误。
由于您有一个能够重现该问题的最小测试程序,我能给出的唯一建议是:更新您的驱动程序。如果这没有帮助,请提交错误报告。
我从此处找到的 Anton 的 opengl 教程配套代码中改编了一些代码: https://github.com/capnramses/antons_opengl_tutorials_book/blob/master/02_shaders/main.c
在测试 print_all 或 dump_all 函数以打印有关着色器的所有内容时,我在日志中注意到 glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, ¶ms)
将放置一个非常负的垃圾值(并且由于 for 循环检查而没有做任何事情)或非常积极并且在 glGetActiveUniform(shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)
处使我的程序崩溃,因为它只应该处理 i
到 NUM_ACTIVE_UNIFORMS - 1
的值;我相信它不会期望超过 100 万。我还检查了调用后 glGetError 的结果,没有设置错误标志。
这是我的转储函数和我的日志图片:
void dump_all(FILE* file, GLuint shader_program)
{
int params = -1;
fprintf(file, "--------------------\nshader program %i info:\n", shader_program);
glGetProgramiv (shader_program, GL_LINK_STATUS, ¶ms);
fprintf(file, "GL_LINK_STATUS = %i\n", params);
glGetProgramiv (shader_program, GL_ATTACHED_SHADERS, ¶ms);
fprintf(file,"GL_ATTACHED_SHADERS = %i\n", params);
glGetProgramiv (shader_program, GL_ACTIVE_ATTRIBUTES, ¶ms);
fprintf(file, "GL_ACTIVE_ATTRIBUTES = %i\n", params);
const int MAX_LENGTH = 64;
int i;
for (i = 0; i < params; i++)
{
char name[MAX_LENGTH];
int actual_length = 0; // not used atm.
int size = 0;
GLenum type;
glGetActiveAttrib (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name);
if (size > 1)
{
int j;
for (j = 0; j < size; j++)
{
char long_name[MAX_LENGTH];
int location;
sprintf (long_name, "%s[%i]", name, j);
location = glGetAttribLocation (shader_program, long_name);
fprintf (file, " %i) type:%s name:%s location:%i\n",
i, gl_type_to_string(type), long_name, location);
}
}
else
{
int location = glGetAttribLocation (shader_program, name);
fprintf(file, " %i) type:%s name:%s location:%i\n", i, gl_type_to_string (type), name, location);
}
}
printf("\nbefore the active uniform call\n");
glGetProgramiv (shader_program, GL_ACTIVE_UNIFORMS, ¶ms);
GLenum error = glGetError();
printf("\nThere is an error (0 for no error and 1 for an error): %d\n", error != GL_NO_ERROR);
printf("\nglGetError: %d\n", error);
printf("\nafter the get active uniform call\n");
fprintf(file, "GL_ACTIVE_UNIFORMS = %i\n", params);
for (i = 0; i < params; i++)
{
char name[MAX_LENGTH];
int actual_length = 0; // not used atm.
int size = 0;
GLenum type;
printf("\nright before the thing\n");
glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name);
printf("\nright after the thign\n");
if (size > 1)
{
printf("\nin the if block\n");
int j;
for (j = 0; j < size; j++)
{
char long_name[MAX_LENGTH];
int location;
sprintf (long_name, "%s[%i]", name, j);
location = glGetUniformLocation (shader_program, long_name);
fprintf(file, " %i) type:%s name:%s location:%i\n",
i, gl_type_to_string (type), long_name, location);
}
}
else
{
printf("\nin the else block\n");
int location = glGetUniformLocation (shader_program, name);
fprintf(file, " %i) type:%s name:%s location:%i\n",
i, gl_type_to_string (type), name, location);
}
}
print_program_info_log(file, shader_program);
}
我的负数时的日志:http://i.stack.imgur.com/Xu9nq.png
当值很大并导致程序崩溃时我的日志:http://i.stack.imgur.com/QBP1o.png
注意:我是运行来自无法找到文件的目录的应用程序。此外,我还没有在任何地方调用 glUseProgram() 。因此,我正在转储尚未成功链接或什至已附加任何成功编译的着色器的内容。
这是间歇性问题;大多数时候,日志会正确打印出 0 表示活动制服。到目前为止,我已经看到了三种结果; 1.) 数字太大而崩溃。 2.) 这个数字是疯狂的负数并且不会崩溃。 3.) 数字很大但不是太大并且在崩溃调用下面的循环中花费了大量时间,只是打印出垃圾。
这是驱动程序错误,还是我在做一些本质上未定义的事情?
编辑 1:
在调用glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, ¶ms)
returns 一个巨大的数字的情况下,对 glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)
的调用在它下面的 for 循环的第一次迭代中崩溃,值为零对于 i
。但是,当 glGetProgramiv
returns 0 用于主动制服时,调用 glGetActiveUniform
和 0 用于 i
不会崩溃(我将 for 循环硬编码为绕一圈)。这让我觉得这里发生的事情不仅仅是返回给我的未初始化数据。
编辑 2 根据要求,这是一个给出奇怪值的最小示例程序:
#include <stdio.h>
#undef main
int main(int argc, char ** argv)
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_Window *window = SDL_CreateWindow("Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
if(!window)
{
printf("no window created : %s\n", SDL_GetError());
}
SDL_GLContext context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, context);
SDL_GL_SetSwapInterval(1);
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if(err != GLEW_OK)
{
printf("glew failed to init: %s", glewGetErrorString(err));
return -1;
}
GLuint program = glCreateProgram();
glLinkProgram(program); // I can create and attach shaders, compile them, or whatever; I get the same result.
GLint num;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &num);
printf("NUM UNIFORMS: %d", num);
int run_loop = 1;
while(run_loop)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
SDL_Quit();
run_loop = 0;
break;
default:
break;
}
}
SDL_GL_SwapWindow(window);
}
return 0;
}
在无法 link 的程序上调用 glGetProgramiv(GL_ACTIVE_UNIFORMS) 是一个非常糟糕的主意。
很可能驱动程序从未到达进程中设置该值的位置,只是将未初始化的内存返回给您。
这是否构成驱动程序错误尚有争议,因为程序处于无效状态。
查询未能link的程序的制服数(或任何其他状态)在 GL 中有效。 OpenGL 4.3 core profile specification 在第 7.3.1 节中指出:
If a program is linked unsuccessfully, the link may have failed for a number of reasons, including cases where the program required more resources than supported by the implementation. Implementations are permitted, but not required, to record lists of resources that would have been considered active had the program linked successfully. If an implementation does not record information for any given interface, the corresponding list of active resources is considered empty. If a program has never been linked, all lists of active resources are considered empty.
连同第 7.13 节关于 glGetProgramiv():
If
pnam
e isACTIVE_ATTRIBUTES
, the number of active attributes (see section 7.3.1) in program is returned. If no active attributes exist, zero is returned. If pname isACTIVE_UNIFORMS
, the number of active uniforms in program is returned. If no active uniforms exist, zero is returned.
请注意,第 7.3.1 节在此引文中仅被引用一次,但它定义 "resources" 以包括制服和属性(以及其他)。
一般来说,GL 永远不会 return 向您提供未初始化的数据 - 除非您查询您自己负责填充的内容(例如缓冲区、帧缓冲区等)。 glGet*()
调用要么成功(和 returns 有效数据),要么因某些 GL 错误而失败(并且不向参数指向的内存写入任何内容)。
如果您看到一些未初始化的 GL 状态,那很可能是驱动程序错误。请注意,查询未能 link 的程序的资源是一个特别不寻常的(实际上也是非常无用的)操作(我从未尝试过这样做),所以这可能是一个未经充分测试的代码路径和我可以想象你可能会遇到一些驱动程序错误。
由于您有一个能够重现该问题的最小测试程序,我能给出的唯一建议是:更新您的驱动程序。如果这没有帮助,请提交错误报告。