为啥 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会返回垃圾并使我的程序崩溃?
Posted
技术标签:
【中文标题】为啥 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会返回垃圾并使我的程序崩溃?【英文标题】:Why does glGetProgramiv GL_ACTIVE_UNIFORMS occasionally return garbage and crash my program?为什么 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会返回垃圾并使我的程序崩溃? 【发布时间】:2015-03-07 21:32:07 【问题描述】:我已经从 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, &params)
将放置一个非常负的垃圾值(并且由于 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, &params)
的调用返回一个巨大的数字,则对glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)
的调用在它下面的for 循环的第一次迭代中崩溃,i
的值为零。但是,当glGetProgramiv
为活动制服返回 0 时,使用 0 为i
调用glGetActiveUniform
不会崩溃(我将 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;
【问题讨论】:
【参考方案1】:查询链接失败的程序的制服数量(或任何其他状态)在 GL 中有效。 7.3.1 节中的OpenGL 4.3 core profile specification 状态:
如果一个程序链接不成功,链接可能已经失败了一段时间 原因的数量,包括程序需要更多的情况 资源比实施支持。实现是 允许但不要求记录资源清单 如果程序成功链接,则被认为是活动的。如果 实现不记录任何给定接口的信息, 相应的活动资源列表被认为是空的。如果一个 程序从未被链接,所有活动资源列表都是 被认为是空的。
连同第 7.13 节关于glGetProgramiv():
如果
pnam
e 是ACTIVE_ATTRIBUTES
,则活动属性的数量(参见 返回程序中的第 7.3.1 节)。如果不存在活动属性, 返回零。如果 pname 为ACTIVE_UNIFORMS
,则活动的数量 程序中的制服被退回。如果不存在活动制服,则为零 返回。
请注意,第 7.3.1 节在此引用中仅引用一次,但它定义了“资源”以包括制服和属性(以及其他)。
一般来说,GL 永远不会向您返回未初始化的数据 - 除非您查询您自己负责填充的内容(如缓冲区、帧缓冲区等)。 glGet*()
调用要么成功(并返回有效数据),要么因某些 GL 错误而失败(并且不向参数指向的内存写入任何内容)。
如果您看到一些未初始化的 GL 状态,那很可能是驱动程序错误。请注意,查询无法链接的程序的资源是一个特别不寻常的(实际上也是非常无用的)操作(我从未尝试过这样做),所以这可能是一个未经充分测试的代码路径,我可以想象您可能会遇到一些驱动程序错误。
由于您有一个能够重现该问题的最小测试程序,因此我可以给出的唯一建议是:更新您的驱动程序。如果这没有帮助,请提交错误报告。
【讨论】:
我想补充一点,我对驱动程序不太了解,我认为如果 Microsoft 设备管理器说我的驱动程序是最新的,我就没事了。这显然根本不正确。像大家建议的那样与供应商核实。【参考方案2】:在链接失败的程序上调用 glGetProgramiv(GL_ACTIVE_UNIFORMS) 是一个非常糟糕的主意。
很可能驱动程序从未到达设置该值的进程点,而只是将未初始化的内存返回给您。
这是否构成驱动程序错误尚待商榷,因为程序处于无效状态。
【讨论】:
好吧,没什么好争论的。 GL 规范明确指出,查询链接失败的程序的活动属性是完全合法的。 返回未初始化的内存?为什么 0 应该是 20 次中的 17 次?对我来说有趣的是,当返回的值非常大时,在 for 循环的第一次迭代中(当 i 为零时)对 glGetActiveUniform() 的调用会崩溃。但是,当我在对 glGetProgramiv() 的调用返回 0 时手动为该参数输入 0 时,调用正常并且没有崩溃。这向我表明,它不仅仅是返回未初始化的内存。想法? PS:@derhass 这就是我要说的!大声笑 @noobEqualsBlake:嗯,很难说到底发生了什么。它仍然可能是您这边未定义的行为。例如,如果您设法破坏了堆或堆栈,这也可能会影响驱动程序。您可能会创建一个最小的示例,它只是创建一个 GL 上下文和一些不链接的程序,并尝试在那里重现该行为。另外,您使用的是什么 GL 实现? @derhass:弗兰克说的似乎是对的。当我使用最小但成功的链接程序运行程序时,它不会崩溃并且调用始终返回 0。您确定 GL 规范说这是有效的做法吗?如果有,在哪里?如果这就是你的意思,我正在使用 opengl 4.3 顺便说一句。如果你挖掘出一些提到这是有效的东西,如果你愿意,我可以在某个地方发布最小的例子。 @noobEqualsBlake: 4.3 core 7.3.1: “如果一个程序链接不成功,链接可能由于多种原因而失败,包括程序需要的资源多于实现支持的情况。允许但不要求实现记录在程序成功链接的情况下将被视为活动的资源列表。如果实现不记录任何给定接口的信息,则相应的活动资源列表被认为是空的。如果程序从未链接过,所有活动资源列表都被认为是空的。”以上是关于为啥 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会返回垃圾并使我的程序崩溃?的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用 glTranslatef?为啥不直接更改渲染坐标?
为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?