为什么 GL.EnableVertexArrayAttrib 在一台机器上崩溃但在另一台机器上不崩溃?

Why does GL.EnableVertexArrayAttrib crash on a machine but not on an other?

我正在使用 OpenTK,这是​​一个用于 C# 的 OpenGL 库。我在我的主 PC(Nvidia 视频卡)上开始了一个项目,一切都很好。然后我在我的笔记本电脑(AMD 视频卡)上继续它,在调用 GL.EnableVertexArrayAttrib.

时出现异常

最小复制代码:

// 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

我在 Windows 8.1 AMD 笔记本电脑上安装了最新的 GPU 驱动程序和最新的 OpenTK。

您将 4.5 直接状态访问 (DSA) 调用与 3.3 调用混合在一起,不要这样做,它只会导致像这样的错误。

问题在于 3.3 glGen* 和 4.5 glCreate* 函数之间存在细微的行为差异。 glGen* 版本只为对象保留一个位置,但将其创建推迟到第一次使用 glBind* 绑定对象时。我不确定为什么会这样决定。无论如何,这通常并不重要,因为必须先绑定对象才能对其进行任何操作。至少在 DSA 成为现实之前是这样。 因此,您的 VAO 从未被绑定,因此从未被创建,也没有任何可启用的东西。

有两种方法可以修复您的程序:

  1. 承诺使用 OpenGL 4.5 功能。它们使用起来更舒适,不需要 glBind* 调用来调用下一个函数,它更像方法。此外,还有一些新功能可以为您提供更精细的控制 - explicit uniform locations(4.3+) 也很不错。它不是那么新,所有现代 GPU 都支持它。虽然不确定较旧的游戏机或手机,但我也知道 VirtualBox VM 仅限于 3.3。

    在这种情况下,您需要做的就是使用 CreateVertexArrayCreateVertexArrays 函数创建 VAO。

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

    您也可以只调用 glBind* 至少一次,但不要依赖它,它对未来来说并不明显 reader。

  2. 继续使用 3.3 并使用 EnableVertexAttribArray 而不是 EnableVertexArrayAttrib。是的,这是一个非常可怕的命名决定,代码完成很有可能出卖了你,然后你就填充了适当的参数。此函数更改当前绑定的 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.
    

关于它在NVIDIA上运行的原因? OpenGL 驱动程序一团糟。他们试图预测常见的错误并修复它们——例如核心中未绑定 VAO、缺少索引缓冲区、越界绘制调用、合理的默认位置、采样器或着色器中的一些细微错误。严重依赖隐藏的全局上下文状态根本无济于事。我个人的经验是,NVIDIA 最宽容,AMD 50-50,Intel 最严。如果可以的话,我建议真正同时在集成卡和专用卡上进行开发。在一张卡上出现黑屏而在另一张卡上正常工作并想知道最近 X 小时内的哪个变化可能是原因,这没有什么令人愉快的。