LWJGL调用glDrawArrays时JVM触发EXCEPTION_ACCESS_VIOLATION崩溃
调用glDrawArrays()时JVM触发EXCEPTION_ACCESS_VIOLATION崩溃问题排查与修复
问题背景
原问题为LWJGL渲染画面不显示,搭建最小复现示例后出现JVM崩溃,触发EXCEPTION_ACCESS_VIOLATION错误。已尝试每帧初始化顶点属性指针、设置GLFW错误回调与OpenGL调试消息回调,但未生成任何调试信息或错误日志。
环境信息
- 运行环境:IntelliJ直接运行JDK17
- LWJGL依赖:标准LWJGL3配置
复现代码
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GLUtil; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class MinimalExample { private static String readResource(String res) { try { InputStream is = MinimalExample.class.getResourceAsStream(res); String s = new String(is.readAllBytes(), StandardCharsets.UTF_8); is.close(); return s; } catch (Exception e) { throw new IllegalStateException(e); } } // vertex data buffer private static final ByteBuffer buf = ByteBuffer.allocateDirect(4096); // shader program static int program; // render objects static int vao; static int vbo; public static void main(String[] args) { // set buffer limit buf.limit(4096); // init glfw and create window GLFW.glfwInit(); long window = GLFW.glfwCreateWindow(500, 500, "Hello", 0, 0); // create GL GLFW.glfwMakeContextCurrent(window); GL.createCapabilities(); GLUtil.setupDebugMessageCallback(System.out); GLFW.glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.out)); // create vertex objects vao = GL30.glGenVertexArrays(); vbo = GL30.glGenBuffers(); GL30.glBindVertexArray(vao); GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 7 * 4, 0); GL30.glVertexAttribPointer(1, 4, GL30.GL_FLOAT, false, 7 * 4, 0); GL30.glEnableVertexAttribArray(0); GL30.glEnableVertexAttribArray(1); GL30.glBindVertexArray(0); // compile and link shaders int vertexShader = GL30.glCreateShader(GL30.GL_VERTEX_SHADER); int fragmentShader = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER); GL30.glShaderSource(vertexShader, readResource("/test.vsh")); GL30.glShaderSource(fragmentShader, readResource("/test.fsh")); GL30.glCompileShader(vertexShader); GL30.glCompileShader(fragmentShader); program = GL30.glCreateProgram(); GL30.glAttachShader(program, vertexShader); GL30.glAttachShader(program, fragmentShader); GL30.glLinkProgram(program); // render loop while (!GLFW.glfwWindowShouldClose(window)) { // poll events GLFW.glfwPollEvents(); // clear screen GL30.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); GL30.glClear(GL30.GL_COLOR_BUFFER_BIT); // render render(); // swap buffers GLFW.glfwSwapBuffers(window); } } static void render() { // put vertex data // manual to simulate graphics library putVec3(0.25f, 0.25f, 1f); putVec4(1.0f, 0.0f, 0.0f, 1.0f); putVec3(0.75f, 0.25f, 1f); putVec4(0.0f, 1.0f, 0.0f, 1.0f); putVec3(0.50f, 0.75f, 1f); putVec4(0.0f, 0.0f, 1.0f, 1.0f); // bind program GL30.glUseProgram(program); // bind vertex array GL30.glBindVertexArray(vao); GL30.glEnableVertexAttribArray(0); GL30.glEnableVertexAttribArray(1); // upload graphics data and draw GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo); GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buf, GL30.GL_STATIC_DRAW); GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, 3); GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0); GL30.glBindVertexArray(0); // reset vertex data buffer buf.position(0); } ////////////////////////////////////////// static void putVec3(float x, float y, float z) { buf.putFloat(x).putFloat(y).putFloat(z); } static void putVec4(float x, float y, float z, float w) { buf.putFloat(x).putFloat(y).putFloat(z).putFloat(z); } }
问题根源
- 顶点属性指针偏移错误:初始化VAO时,颜色属性的偏移量设为0,而颜色数据实际紧跟在3个float的位置数据之后,正确偏移量应为
3 * 4(12字节),GPU读取越界内存直接触发访问违规。 - ByteBuffer位置未提前重置:
render()方法中写入顶点数据前未重置ByteBuffer的position,第一次写入后position停在有效数据末尾,后续循环会继续往buffer后方写入非法数据,导致glBufferData读取超出有效范围的内存。 - putVec4方法笔误:最后一个参数错误使用
z而非w,虽不直接导致崩溃,但会造成颜色数据异常。 - 缺少Shader编译/链接检查:代码未验证Shader编译和Program链接是否成功,若Shader存在语法错误,无效的Program会引发未知行为。
修复方案
1. 修正顶点属性偏移量
修改VAO初始化代码:
GL30.glVertexAttribPointer(1, 4, GL30.GL_FLOAT, false, 7 * 4, 3 * 4);
2. 写入顶点数据前重置ByteBuffer位置
在render()方法开头添加:
buf.position(0);
3. 修复putVec4方法笔误
static void putVec4(float x, float y, float z, float w) { buf.putFloat(x).putFloat(y).putFloat(z).putFloat(w); }
4. 添加Shader编译/链接错误检查
在编译Shader和链接Program后添加检查逻辑:
// 检查顶点Shader编译 if (GL30.glGetShaderi(vertexShader, GL30.GL_COMPILE_STATUS) == GL30.GL_FALSE) { throw new RuntimeException("Vertex shader compile failed: " + GL30.glGetShaderInfoLog(vertexShader)); } // 检查片段Shader编译 if (GL30.glGetShaderi(fragmentShader, GL30.GL_COMPILE_STATUS) == GL30.GL_FALSE) { throw new RuntimeException("Fragment shader compile failed: " + GL30.glGetShaderInfoLog(fragmentShader)); } // 检查Program链接 if (GL30.glGetProgrami(program, GL30.GL_LINK_STATUS) == GL30.GL_FALSE) { throw new RuntimeException("Program link failed: " + GL30.glGetProgramInfoLog(program)); }
修复后的完整代码
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GLUtil; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class MinimalExample { private static String readResource(String res) { try { InputStream is = MinimalExample.class.getResourceAsStream(res); String s = new String(is.readAllBytes(), StandardCharsets.UTF_8); is.close(); return s; } catch (Exception e) { throw new IllegalStateException(e); } } // vertex data buffer private static final ByteBuffer buf = ByteBuffer.allocateDirect(4096); // shader program static int program; // render objects static int vao; static int vbo; public static void main(String[] args) { // set buffer limit buf.limit(4096); // init glfw and create window GLFW.glfwInit(); long window = GLFW.glfwCreateWindow(500, 500, "Hello", 0, 0); // create GL GLFW.glfwMakeContextCurrent(window); GL.createCapabilities(); GLUtil.setupDebugMessageCallback(System.out); GLFW.glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.out)); // create vertex objects vao = GL30.glGenVertexArrays(); vbo = GL30.glGenBuffers(); GL30.glBindVertexArray(vao); GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 7 * 4, 0); // 修正颜色属性偏移量 GL30.glVertexAttribPointer(1, 4, GL30.GL_FLOAT, false, 7 * 4, 3 * 4); GL30.glEnableVertexAttribArray(0); GL30.glEnableVertexAttribArray(1); GL30.glBindVertexArray(0); // compile and link shaders int vertexShader = GL30.glCreateShader(GL30.GL_VERTEX_SHADER); int fragmentShader = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER); GL30.glShaderSource(vertexShader, readResource("/test.vsh")); GL30.glShaderSource(fragmentShader, readResource("/test.fsh")); GL30.glCompileShader(vertexShader); GL30.glCompileShader(fragmentShader); // 检查Shader编译错误 if (GL30.glGetShaderi(vertexShader, GL30.GL_COMPILE_STATUS) == GL30.GL_FALSE) { throw new RuntimeException("Vertex shader compile failed: " + GL30.glGetShaderInfoLog(vertexShader)); } if (GL30.glGetShaderi(fragmentShader, GL30.GL_COMPILE_STATUS) == GL30.GL_FALSE) { throw new RuntimeException("Fragment shader compile failed: " + GL30.glGetShaderInfoLog(fragmentShader)); } program = GL30.glCreateProgram(); GL30.glAttachShader(program, vertexShader); GL30.glAttachShader(program, fragmentShader); GL30.glLinkProgram(program); // 检查Program链接错误 if (GL30.glGetProgrami(program, GL30.GL_LINK_STATUS) == GL30.GL_FALSE) { throw new RuntimeException("Program link failed: " + GL30.glGetProgramInfoLog(program)); } // 删除Shader,链接后不再需要 GL30.glDeleteShader(vertexShader); GL30.glDeleteShader(fragmentShader); // render loop while (!GLFW.glfwWindowShouldClose(window)) { // poll events GLFW.glfwPollEvents(); // clear screen GL30.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); GL30.glClear(GL30.GL_COLOR_BUFFER_BIT); // render render(); // swap buffers GLFW.glfwSwapBuffers(window); } // 清理资源 GL30.glDeleteVertexArrays(vao); GL30.glDeleteBuffers(vbo); GL30.glDeleteProgram(program); GLFW.glfwDestroyWindow(window); GLFW.glfwTerminate(); } static void render() { // 重置buffer位置,准备写入新数据 buf.position(0); // put vertex data // manual to simulate graphics library putVec3(0.25f, 0.25f, 1f); putVec4(1.0f, 0.0f, 0.0f, 1.0f); putVec3(0.75f, 0.25f, 1f); putVec4(0.0f, 1.0f, 0.0f, 1.0f); putVec3(0.50f, 0.75f, 1f); putVec4(0.0f, 0.0f, 1.0f, 1.0f); // 刷新buffer,确保数据写入完成 buf.flip(); // bind program GL30.glUseProgram(program); // bind vertex array GL30.glBindVertexArray(vao); // upload graphics data and draw GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo); GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buf, GL30.GL_STATIC_DRAW); GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, 3); // 解绑资源 GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0); GL30.glBindVertexArray(0); GL30.glUseProgram(0); } ////////////////////////////////////////// static void putVec3(float x, float y, float z) { buf.putFloat(x).putFloat(y).putFloat(z); } static void putVec4(float x, float y, float z, float w) { buf.putFloat(x).putFloat(y).putFloat(z).putFloat(w); } }
内容的提问来源于stack exchange,提问作者Orbyfied
相关产品推荐
相关产品推荐

