【问题标题】:intermittent GLSL vertex shader compile errors间歇性 GLSL 顶点着色器编译错误
【发布时间】:2014-03-01 01:44:50
【问题描述】:

我在编译顶点着色器时遇到间歇性错误,为新创建的 OpenGL 上下文的第一次渲染做准备。它与通常在相同硬件上工作的顶点着色器相同。失败后glGetShaderInfoLog返回的info log一般是这样的:

Vertex shader failed to compile with the following errors:

是的,就是这样。它发生在带有 ATI 和 NVidia GPU 的 Windows 上,尽管我主要是在 ATI 上进行测试。如果我在 Visual Studio 调试器下运行,可能会在 glCompileShader 或随后的 glGetShaderiv 调用中检测到访问冲突。

我没有在 Mac 上看到这个错误,但由于重现它并不总是那么容易,我不确定它不会发生。

我注意到,如果我在创建上下文时不调用 wglShareLists,错误就会消失(或者至少我无法轻松重现它)。我认为这意味着一些不良信息正在从先前创建(并且可能先前已销毁)的 OpenGL 上下文流向新的上下文。然而,我一直在删减一些东西,直到没有太多应该共享的东西:没有纹理对象、显示列表、VBO、PBO。据我所知,剩下的唯一可以共享的东西是着色器和程序对象。

在为窗口创建 OpenGL 上下文后,我大致如下初始化着色:

// Program initialization
GLint vertexShader = glCreateShader( GL_VERTEX_SHADER );
glShaderSource( vertexShader, 1, &vertexShaderSource, NULL );
glCompileShader( vertexShader );
GLint   status;
glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );
if (status == GL_TRUE)
{
    GLint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
    glShaderSource( fragmentShader, 1, &fragShaderSource, NULL );
    glCompileShader( fragmentShader );
    glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );
    if (status == GL_TRUE)
    {
        GLint program = glCreateProgram();
        if (program != 0)
        {
            glAttachShader( program, vertexShader );
            glAttachShader( program, fragmentShader );
            glLinkProgram( program );
            glDetachShader( program, fragmentShader );
            glDetachShader( program, vertexShader );
            glDeleteShader( fragmentShader );
            // check for link success...
        }
    }
}
else
{
    char logBuf[1024];
    glGetShaderInfoLog( vertexShader, sizeof(logBuf), NULL, (GLchar*)logBuf );
    LogToFile( (char*)logBuf );
}

然后我渲染多个帧,并在某个时候清理

glDeleteProgram( program );
glDeleteShader( vertexShader );

并销毁 OpenGL 上下文。后来我在同一个窗口新建了一个OpenGL上下文,用同样的方法初始化,但是顶点着色器编译失败。

如果我不渲染任何几何体,我无法重现错误,但渲染一个点就足够了。

简化的顶点着色器仍然会发生这种情况:

#version 120
void main()
{
    gl_Position = ftransform();
    gl_FrontColor = gl_Color;
    gl_BackColor = gl_Color;
}

还有一个简化的片段着色器:

#version 120
void main()
{
    gl_FragColor = gl_Color;
}

我尝试了 AMD 的 CodeXL OpenGL 调试器,将其设置为在出错时中断。它只是告诉我编译失败,我已经知道了。

失败的 OpenGL 上下文位于先前创建、成功渲染和销毁不同上下文的窗口上。重复使用这样的窗口没有什么问题,是吗? (我知道一旦你调用了SetPixelFormat,你就不能改变窗口的像素格式。我确保我每次都使用相同的像素格式。)

添加:在修剪渲染代码时,我发现如果我注释掉了该行

glEnable( GL_VERTEX_PROGRAM_TWO_SIDE );

然后错误消失了。但是随着更多真实世界的渲染(例如纹理)的恢复,它似乎并没有什么不同。

ADDED 3/7/2014: 我终于做了一个自包含的 Visual C++ 项目,可以在某些情况下重现该错误,以便任何有兴趣的人可以尝试:ShaderBugTest project 要使错误可重现,您需要将 Microsoft 的“应用程序验证程序”设置为监视堆错误。随附的自述文件包含更详细的重现步骤。

【问题讨论】:

  • 您提到“如果我不渲染任何几何图形,我将无法重现该错误”。渲染代码中是否有可能导致稍后出现错误?
  • @GuyRT,我会愚蠢地说渲染代码中没有出错的机会。我正在简化渲染代码,但在这里发布仍然太多。我想我是希望有人能告诉我要寻找什么。
  • 如何加载vertexShaderSource?
  • @Goz,vertexShaderSource 是一个硬编码的 C 字符串常量。

标签: c++ windows opengl glsl


【解决方案1】:

您的示例有很多 GL 对象的分配和释放,但几乎没有错误检查。

vertexShader = glCreateShader( GL_VERTEX_SHADER );

需要检查返回值vertexShader,以确保其有效。

glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );

你得到这个值,但不表明你正在检查状态。

program = glCreateProgram();

再次,在开始使用之前需要检查返回值。

glAttachShader( program, vertexShader );
fragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource( fragmentShader, 1, &fragShaderSource, NULL );
glCompileShader( fragmentShader );

您应该先创建片段着色器和顶点着色器,然后再将它们附加到程序中。如果出现故障,它可以让您减少清理工作。

 glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &status );

再次,在获取它之后检查编译状态。您编译失败,但到目前为止我们不知道哪个着色器无法编译。是片段着色器还是顶点着色器。

 glAttachShader( program, fragmentShader );
 glDeleteShader( fragmentShader );

在这里删除你的着色器很奇怪。是的,它应该只标记为删除,因为它实际上附加到程序中,但这似乎仍然是一种危险的方式。此外,您在管理片段着色器时采用了与顶点着色器不同的方法,原因不明。

 glLinkProgram( program );
 glDetachShader( program, fragmentShader );
 glDetachShader( program, vertexShader );

再一次,这对我来说是一种有点令人费解的着色器管理方法。再次,同样对待片段着色器和顶点着色器。

 glDeleteProgram( program );
 glDeleteShader( vertexShader );

为什么对片段着色器进行不同的处理?

在诊断问题时,您应该使用glGetError() 并在每次 GL 调用后报告任何异常情况时停止程序,这样您就可以隔离第一个错误的位置发生。编译失败完全有可能是以前发生的事情的级联效应。

【讨论】:

  • 感谢您的笔记。我确实说过我给出的是“粗略的大纲”,而不是完整的代码清单。实际上,我确实进行了大量的错误检查,而且我确实知道编译失败的是顶点着色器,而不是片段着色器。另外,我提到我尝试在 CodeXL 下运行,设置为在任何 OpenGL 错误时中断,这应该与每次调用后检查 glGetError 具有相同的效果。
  • 我之所以区别对待顶点着色器和片段着色器,是因为在更现实的条件下,片段着色器会不时发生变化(例如,取决于灯光的数量),而顶点着色器永远不会变化,因此可能与多个程序相关联。
  • 我怀疑您的 GL 实现正在泄漏资源,因为您处理它们的方式:当您删除程序时依靠删除标记机制进行清理。第一次通过时是否发生过故障?
  • 除非您实际上加载了数百或数千个着色器,否则没有真正的理由在编译着色器对象后释放它们。将它们放入由您加载源的位置索引的持久存储中,然后通过从存储中获取它们并将它们附加到您创建的程序来创建程序。您还可以通过使用一对着色器键作为程序键以相同的方式持久化程序。所以:在程序开始时编译所有的着色器,永远不要再碰它们。
  • GlUtils::getProgram()的实现在这里:github.com/OculusRiftInAction/OculusRiftInAction/blob/master/…
【解决方案2】:

使用 glGetShaderInfoLog 查看编译错误信息。示例见http://www.opengl.org/wiki/Shader_Compilation#Shader_error_handling

【讨论】:

  • 我确实收到了编译错误消息,并在我的问题的早期告诉了你。
  • “顶点着色器无法编译并出现以下错误:”是实际错误,还是您的代码打印的前缀?介意发布您用于打印错误的代码吗?
  • 这是从驱动程序而不是我的代码中检索到的实际信息日志文本。我会将代码添加到我的问题中。
  • 顺便说一句,我可以理解为什么您可能认为“顶点着色器无法编译并出现以下错误:”可能是我的代码提供的前缀,但我认为这实际上是典型的开始来自驱动程序的编译错误消息。
【解决方案3】:

您似乎没有向gl_Color 写任何内容。

gl_Color 属性在不同的地方可以表示不同的东西。

在您的顶点着色器中,gl_Color 表示用户使用glColor*glColorPointer 调用传递的逐顶点颜色属性。

在您的片段着色器中,gl_Color 表示正在渲染的三角形面的插值颜色。

但是,这是一个固定管道功能,gl_Color 在现代 OpenGL 中已被弃用。这可能是您的错误背后的原因。您需要使用以下代码获取编译器错误消息:

GLint   SuccessFlag = 0;
GLsizei Length      = 0;
GLsizei MaxLength   = 0;

glGetShaderiv( ShaderID, GL_COMPILE_STATUS,  &SuccessFlag );
glGetShaderiv( ShaderID, GL_INFO_LOG_LENGTH, &MaxLength   );

char* Log = ( char* )alloca( MaxLength );

glGetShaderInfoLog( ShaderID, MaxLength, &Length, Log );

将日志添加到您的问题将有助于更好地诊断您的问题。

您使用什么版本的 OpenGL?是核心还是兼容性配置文件?

【讨论】:

  • 我认为我不应该给gl_Color 写任何东西。根据橙皮书,gl_Color 是顶点着色器和片段着色器的内置输入,而不是输出。
  • 我确实得到了着色器信息日志。您将在我的问题的第一段中找到文本,并在最后找到获取它的代码。 GL_VERSION 字符串是“3.3.11554 Compatibility Profile Context FireGL”。
  • 如果你使用这些着色器:void main() { gl_Position = ftransform(); }void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.5 ); },你还有错误吗?
  • @JWWalker:这意味着错误代码不在着色器中,而是在其他 GL 调用中。尝试在每次 OpenGL 调用之后调用 glGetError()
  • 使用 AMD 的 CodeXL 并设置中断 OpenGL 错误应该等同于在每次调用后调用 glGetError。但是当我这样做时,它直到glCompileShader 才检测到任何错误。
猜你喜欢
  • 1970-01-01
  • 2017-08-11
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多