为什么 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 从未被绑定,因此从未被创建,也没有任何可启用的东西。
有两种方法可以修复您的程序:
承诺使用 OpenGL 4.5 功能。它们使用起来更舒适,不需要 glBind*
调用来调用下一个函数,它更像方法。此外,还有一些新功能可以为您提供更精细的控制 - explicit uniform locations(4.3+) 也很不错。它不是那么新,所有现代 GPU 都支持它。虽然不确定较旧的游戏机或手机,但我也知道 VirtualBox VM 仅限于 3.3。
在这种情况下,您需要做的就是使用 CreateVertexArray
CreateVertexArrays
函数创建 VAO。
int vao;
GL.CreateVertexArrays(1,out vao);
int attrib = GL.GetAttribLocation(programID, "test");
GL.EnableVertexArrayAttrib(vao, attrib);
您也可以只调用 glBind*
至少一次,但不要依赖它,它对未来来说并不明显 reader。
继续使用 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 小时内的哪个变化可能是原因,这没有什么令人愉快的。
我正在使用 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 从未被绑定,因此从未被创建,也没有任何可启用的东西。
有两种方法可以修复您的程序:
承诺使用 OpenGL 4.5 功能。它们使用起来更舒适,不需要
glBind*
调用来调用下一个函数,它更像方法。此外,还有一些新功能可以为您提供更精细的控制 - explicit uniform locations(4.3+) 也很不错。它不是那么新,所有现代 GPU 都支持它。虽然不确定较旧的游戏机或手机,但我也知道 VirtualBox VM 仅限于 3.3。在这种情况下,您需要做的就是使用
CreateVertexArray
CreateVertexArrays
函数创建 VAO。int vao; GL.CreateVertexArrays(1,out vao); int attrib = GL.GetAttribLocation(programID, "test"); GL.EnableVertexArrayAttrib(vao, attrib);
您也可以只调用
glBind*
至少一次,但不要依赖它,它对未来来说并不明显 reader。继续使用 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 小时内的哪个变化可能是原因,这没有什么令人愉快的。