【问题标题】:Why does GL.EnableVertexArrayAttrib crash on a machine but not on an other?为什么 GL.EnableVertexArrayAttrib 在一台机器上崩溃,而在另一台机器上却没有?
【发布时间】:2020-06-29 21:49:11
【问题描述】:

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

最少的复制代码:

// 创建一个带有 `test` 属性的着色器
int vertexShaderID = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertexShaderID, "在 vec3 测试中; 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);

// 创建一个 VAO,获取 `test` 属性位置并启用它
int vao = GL.GenVertexArray();
int attrib = GL.GetAttribLocation(programID, "test");
GL.EnableVertexArrayAttrib(vao, attrib); // 在 AMD 上抛出 AccessViolationException,但在 NVIDIA 上不抛出

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

【问题讨论】:

  • 这似乎不是您的全部真实代码。片段着色器在哪里?为什么不在检索属性之前调用GL.UseProgram?让我们假设这些问题以正确的方式得到解决。然后可能会发生由于 GL 链接器优化了属性“test”而无法检索到它,因为它没有被使用。
  • @Ripi2 我的整个真实代码很长,包含多个类,其中大部分对于这个问题并不重要,这只是重现错误的最少代码。片段着色器和GL.UseProgram 是对的,这些很重要,我添加了它们。但这并没有改变任何东西,它仍然会在 AMD 笔记本电脑上崩溃。
  • 两台机器上的 OpenGL 版本是否相同?

标签: c# opengl opentk


【解决方案1】:

您将 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 虚拟机仅限于 3.3。

    在这种情况下,您只需使用 CreateVertexArrayCreateVertexArrays 函数创建 VAO。

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

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

  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 小时内的哪个变化可能是原因,这并不令人愉快。

【讨论】:

  • 感谢您的回答,真的很有帮助!我想切换到 OpenGL 4.5,哪个命名空间包含所有这些功能?我试过OpenTK.Graphics.OpenGL4,但没有GL.CreateVertexArray,只有GL.CreateVertexArrays
  • 抱歉,很久没用OpenTK了。我依赖于官方规范,是的,您应该使用大小为 1 的 CreateVertexArrays。严格来说,OpenGL 中也没有 GenVertexArray,但 OpenTK 选择将其添加为助手,它可能也调用 GenVertexArrays。我会更新代码。
  • @GergőGutyina GL.CreateVertexArrays(1,out vao); 为我工作,但我只是快速下载并修改了一个 OpenTK 三角形示例。看起来他们使用单个 out int 添加了重载。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 2017-07-30
  • 1970-01-01
  • 2023-03-29
  • 2018-01-06
相关资源
最近更新 更多