1
votes

I'm using OpenTK, an OpenGL library for C#. I started a project on my main PC(Nvidia video card) and everything was fine. Then I continued it on my laptop(AMD video card) and I got an exception when calling GL.EnableVertexArrayAttrib.

Minimal code for reproduction:

// Create a shader with a `test` attribute
int vertexShaderID = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertexShaderID, "in vec3 test; void main() { gl_Position = vec4(0, 0, 0, 0); }");
GL.CompileShader(vertexShaderID);
int fragmentShaderID = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragmentShaderID, "void main() { gl_FragColor = vec4(0, 0, 0, 0); }");
GL.CompileShader(fragmentShaderID);
int programID = GL.CreateProgram();
GL.UseProgram(programID);
GL.AttachShader(programID, vertexShaderID);
GL.AttachShader(programID, fragmentShaderID);
GL.LinkProgram(programID);

// Make a VAO, get the `test` attribute location and enable it
int vao = GL.GenVertexArray();
int attrib = GL.GetAttribLocation(programID, "test");
GL.EnableVertexArrayAttrib(vao, attrib); // Throws AccessViolationException on AMD, but not on NVIDIA

I have the latest GPU drivers and latest OpenTK on the Windows 8.1 AMD laptop.

1
This doesn't seems your whole real code. Where's the fragment shader? Why don't you call GL.UseProgram before retrieving the attribute? Let's suppose these questions are solved in a right way. Then it may happen that the attribute "test" can not be retrieved due to the GL linker optimized it out because it's not used.Ripi2
@Ripi2 My whole real code is long with multiple classes and most of it are not important for this question, thats just the minimal code to reproduce the bug. You're right about the fragment shader and GL.UseProgram, those are important, I added them. But that doesn't change anything, it still crashes on the AMD laptop.Gergő Gutyina
Are OpenGL versions same on both the machines?codetiger

1 Answers

1
votes

You are mixing 4.5 direct state access(DSA) calls with 3.3 calls, don't do that, it will only lead to bugs like this one.

The problem lies in a subtle difference in behaviour between 3.3 glGen* and 4.5 glCreate* functions. glGen* version only reserves a place for the object but defers its creation to the first time the object is bound using glBind*. I'm not sure about why it was decided that way. It usually does not matter anyway because one has to bind the object first to do anything with it. At least it was the case until DSA became a thing. So, your VAO is never bound thus never created and there is nothing to enable.

There are two ways to fix your program:

  1. Commit to use OpenGL 4.5 functions. They are more comfortable to use, there's no need for glBind* calls in order to only to call the next function, it's more method-like. Plus there are few new functions that give you more fine-grained control - explicit uniform locations(4.3+) are also nice. It's not that new and all modern GPUs support it. Not sure about older consoles or mobiles though, also I know that VirtualBox VMs are limited to 3.3 .

    In that case, all you need to do is create the VAO with the CreateVertexArrayCreateVertexArrays function.

    int vao;
    GL.CreateVertexArrays(1,out vao);
    int attrib = GL.GetAttribLocation(programID, "test");
    GL.EnableVertexArrayAttrib(vao, attrib);
    

    You could have also just called glBind* at least once, but don't rely on that, it won't be obvious to the future reader.

  2. Stay with 3.3 and use EnableVertexAttribArray instead of EnableVertexArrayAttrib. Yes, it's very horrible naming decision and there's a good chance code-completion betrayed you and then you've just filled the appropriate parameters. This function changes the state of currently bound VAO.

     int vao = GL.GenVertexArray();
     int attrib = GL.GetAttribLocation(programID, "test");
     GL.BindVertexArray(vao); //Only here is the actual VAO with 'vao' handle created.
     GL.EnableVertexAttribArray(attrib); // Changes currently the bound VAO's state.
    

About the reason why it works on NVIDIA? OpenGL drivers are a mess. They try to anticipate common mistakes and fix them - e.g. drawing without bound VAOs in core, missing index buffers, out of bounds draw calls, reasonable default locations, samplers, or some subtle mistakes in shaders. Heavy reliance on the hidden global context state does not help at all. My personal experience is that NVIDIA tends to be the most forgiving, AMD is 50-50, Intel is the most strict. I recommend to really develop on both integrated and dedicated cards simultaneously if you can. There's nothing pleasant about getting black screen on one card while it works on the other and wondering which change in the last X hours could be the reason.