9

I've been getting intermittent errors compiling a vertex shader, preparing for the first render of a newly created OpenGL context. It's the same vertex shader that usually works, on the same hardware. After the failure, the info log returned by glGetShaderInfoLog usually shows this:

Vertex shader failed to compile with the following errors:

Yes, that's the whole thing. It happens on Windows, with both ATI and NVidia GPUs, though I've mostly been testing on ATI. If I'm running under the Visual Studio debugger, an access violation may be detected in glCompileShader or in the subsequent glGetShaderiv call.

I haven't seen the bug on the Mac, but since it's not always easy to reproduce it, I'm not absolutely sure it doesn't happen.

I have noticed that if I don't call wglShareLists when I create my contexts, the error goes away (or at least I can't reproduce it easily). I take that to mean that some bad information is bleeding through from a previously created (and perhaps previously destroyed) OpenGL context to the new one. However, I've been trimming out stuff until there is not much that should be shared: No texture objects, display lists, VBOs, PBOs. The only things left that would be shared, as far as I know, would be shader and program objects.

After creating an OpenGL context for a window, I initialize shading roughly like this:

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

Then I render multiple frames, and at some point clean up

glDeleteProgram( program );
glDeleteShader( vertexShader );

and destroy the OpenGL context. Later, I create a new OpenGL context in the same window, initialize it in the same way, but the vertex shader fails to compile.

If I don't render any geometry, I can't get the bug to reproduce, but rendering a single point suffices.

It still happens with a simplified vertex shader:

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

and a simplified fragment shader:

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

I tried AMD's CodeXL OpenGL debugger, setting it to break on errors. It just tells me that the compile is failing, which I already knew.

The OpenGL context that fails is on a window where a different context was previously created, successfully rendered, and destroyed. There's nothing wrong with re-using a window like that, is there? (I am aware that once you've called SetPixelFormat, you can't change the pixel format of the window. I made sure that I'm using the same pixel format each time.)

ADDED: While trimming down the rendering code, I found that if I commented out the line

glEnable( GL_VERTEX_PROGRAM_TWO_SIDE );

then the error went away. But with more real-world rendering (e.g., textures) reinstated, it doesn't seem to make a difference.

ADDED 3/7/2014: I finally made a self-contained Visual C++ project that can reproduce the bug under certain circumstances, so that any interested parties can try it: ShaderBugTest project To make the error reproducible, you need to have Microsoft's "Application Verifier" set to watch for Heap errors. An enclosed Read Me has more detailed steps to reproduce.

JWWalker
  • 22,385
  • 6
  • 55
  • 76
  • You mention "If I don't render any geometry, I can't get the bug to reproduce". Is there any chance of a mistake in the rendering code which is causing an error to show up later? – GuyRT Mar 05 '14 at 10:46
  • @GuyRT, I'd be foolish to say there's no chance of a mistake in the rendering code. I'm in the process of simplifying the rendering code, but it's still too much to post here. I guess I was hoping that someone could give me a clue of what to look for. – JWWalker Mar 05 '14 at 18:48
  • related: http://stackoverflow.com/questions/9113154/proper-way-to-delete-glsl-shader – jozxyqk Mar 06 '14 at 06:50
  • How do you load vertexShaderSource? – Goz Mar 07 '14 at 21:36
  • @Goz, vertexShaderSource is a hard-coded C string constant. – JWWalker Mar 08 '14 at 02:27
  • @JWWalker: An access violation indicates that you are trying to read or write from memory that doesn't belong to you. If you inspect vertexShader in the debugger does it look ok? Or is it corrupt in some way? – Goz Mar 08 '14 at 09:01
  • @Goz, the vertex shader source looks fine in the debugger. (Aren't string literals stored in read-only memory anyway? They would be on the Mac, where I do more of my programming.) – JWWalker Mar 08 '14 at 22:18
  • @JWWalker: TBH I'm not sure whether it is stored in readonly memory ... In the end heap corruption indicates the guard blocks around an allocation are being overwritten. This CAN lead to an access violation. You have updated your driver haven't you? You wouldn't believe how often you can come across driver bugs. – Goz Mar 09 '14 at 08:06
  • This link might prove useful to you in tracking down the problem: http://www.informit.com/articles/article.aspx?p=1081496 – Goz Mar 09 '14 at 08:15

3 Answers3

5

You're example has a lot of allocation and deallocation of GL objects, but little to no error checking.

vertexShader = glCreateShader( GL_VERTEX_SHADER );

Need to check the return value, vertexShader, to make sure it's valid.

glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );

You get this value, but don't show that you're checking status.

program = glCreateProgram();

Again, need to check the return value before you start using it.

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

You should create your fragment shader and vertex shader both before you attach them to the program. It gives you less to clean up if something fails.

 glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &status );

Again, check the compile status after fetching it. You compile is failing, but so far we don't know which shader is failing to compile. Is it the fragment shader or the vertex shader.

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

Deleting your shader here is weird. Yes, it should be only flagged for deleting, since it's actually attached to the program, but this still seems like a dangerous way of doing this. Also, you're taking a different approach to managing the fragment shader than the vertex shader, for no apparent reason.

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

Again, this is a somewhat baffling approach to shader management to me. And again, treat the fragment shader and vertex shader similarly.

 glDeleteProgram( program );
 glDeleteShader( vertexShader );

Why the different treatment of the fragment shader?

While you're diagnosing the problem, you should be using glGetError() and stop the program if it reports anything out of the ordinary after every GL call so that you can isolate where the first error occurs. It's entirely possible that the compile failure is a cascade effect from something that happened previously.

Jherico
  • 28,584
  • 8
  • 61
  • 87
  • Thanks for your notes. I did say I was giving a "rough outline", not a full code listing. I actually do have a fair amount of error checking, and I do know for sure that it's the vertex shader, not the fragment shader, that fails to compile. Also, I mentioned that I tried running under CodeXL, set to break at any OpenGL error, which should have the same effect as checking glGetError after every call. – JWWalker Mar 05 '14 at 23:05
  • The reason that I treat the vertex shader and fragment shader differently is that under more realistic conditions, the fragment shader changes from time to time (e.g., depending on the number of lights) while the vertex shader never changes, and hence may be linked to more than one program. – JWWalker Mar 05 '14 at 23:09
  • I suspect that your GL implementation is leaking resources because of the way you're handling them: relying on the deletion flagging mechanism to clean up when you delete the program. Does the failure ever occur on the first pass? – Jherico Mar 05 '14 at 23:10
  • Unless you're actually loading hundreds or thousands of shaders there's no real reason to deallocate the shader objects once you've compiled them. Put them into a persistent store indexed by the location from which you're loading the sources, and then create programs by fetching them from the store and attaching them to a program that you create. You can also persist programs the same way by using a pair of shader keys as the program key. So: compile all your shaders at program start and never touch them again. – Jherico Mar 05 '14 at 23:12
  • See for instance the implementation of `GlUtils::getProgram()` here: https://github.com/OculusRiftInAction/OculusRiftInAction/blob/master/source/common/GlUtils.cpp#L1043 – Jherico Mar 05 '14 at 23:14
  • Re "Deleting your shader here is weird.": Seems like I saw that recommended in some book. Anyway, I did try it the other way, deleting the fragment shader after linking the program and detaching the shaders, and that didn't fix the problem. – JWWalker Mar 05 '14 at 23:15
  • Yeah, that comment is opinion based, since your approach seems valid based on the spec. But the behavior still sounds like resource exhaustion of some kind (sometimes the vertex shader fails, sometimes the fragment shader) – Jherico Mar 05 '14 at 23:16
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/49101/discussion-between-jwwalker-and-jherico) – JWWalker Mar 05 '14 at 23:36
2

Use glGetShaderInfoLog to see the compile error message. For an example, see http://www.opengl.org/wiki/Shader_Compilation#Shader_error_handling

  • I did get the compile error message, and I told you what it was early in my question. – JWWalker Mar 01 '14 at 05:04
  • Is the "Vertex shader failed to compile with the following errors:" the actual error, or is that a prefix printed from your code? Mind to post the code you used to print the error? –  Mar 03 '14 at 00:20
  • That's the actual info log text retrieved from the driver, not from my code. I'll add the code to my question. – JWWalker Mar 03 '14 at 00:44
  • By the way, I can understand why you might think that "Vertex shader failed to compile with the following errors:" might be a prefix provided by my code, but I think it's actually the typical start to a compile error message from a driver. – JWWalker Mar 03 '14 at 19:33
2

It looks like you are not writing anything to gl_Color.

gl_Color attribute can mean different things in different places.

In your vertex shader, gl_Color represents the per-vertex color attribute passed by the user using glColor* or glColorPointer calls.

In your fragment shader, gl_Color represents the interpolated color for the facing of the triangle being rendered.

However, this is a fixed-pipeline functionality and gl_Color was deprecated in the modern OpenGL. This may be the reason behind your error. You need to get the compiler error message using the following code:

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

Adding the log to your question will help to diagnose your problem better.

What version of OpenGL do you use? Is it core or compatibility profile?

Sergey K.
  • 24,894
  • 13
  • 106
  • 174
  • I don't think I should be writing anything to `gl_Color`. According to the orange book, `gl_Color` is a built-in input to both the vertex and fragment shader, not an output. – JWWalker Mar 05 '14 at 18:37
  • I did get the shader info log. You will find the text in the first paragraph of my question, and the code to get it at the end. The `GL_VERSION` string is "3.3.11554 Compatibility Profile Context FireGL". – JWWalker Mar 05 '14 at 18:41
  • If you use these shaders: ``void main() { gl_Position = ftransform(); }`` and ``void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 0.5 ); }``, do you still have the error? – Sergey K. Mar 06 '14 at 06:58
  • @JWWalker: this means the error code is not in the shaders, but in the other GL calls. Try calling ``glGetError()`` after each and every your OpenGL call. – Sergey K. Mar 07 '14 at 10:12
  • Using AMD's CodeXL with the setting to break on OpenGL errors should be equivalent to calling `glGetError` after every call. But when I do that, it doesn't detect any error until the `glCompileShader`. – JWWalker Mar 08 '14 at 02:43
  • @JWWalker: if the situation is like you say it is, you should submit a driver bug. – Sergey K. Mar 08 '14 at 16:16
  • The computers involved are a few years old, so they'll probably say that no longer support the drivers for those cards. I tried updating the driver on one, and now I get a crash in `ChoosePixelFormat`, before I ever get to any OpenGL code. – JWWalker Mar 08 '14 at 23:35
  • @JWWalker: "I tried updating the driver on one, and now I get a crash" this explains a lot. – Sergey K. Mar 09 '14 at 12:42