EXCEPTION_ACCESS_VIOLATION when calling GL11.glDrawElements()

Started by Fataho, February 26, 2017, 17:06:48

Previous topic - Next topic

Fataho

Hello,
I try load 3D object on separate thread on separate OpenGL context. To do that I create separate worker window:
glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE);
terrainLoaderWindowID = glfwCreateWindow(1, 1, "", MemoryUtil.NULL, mainWindowID);
if (terrainLoaderWindowID == MemoryUtil.NULL)
    throw new RuntimeException("Failed to create the GLFW window");

In separate thread I make context current for that worker window:
glfwMakeContextCurrent(terrainLoaderWindowID);
GL.createCapabilities();

and load up the model. However when I call:
GL11.glDrawElements(GL11.GL_TRIANGLES, entity.getModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);

I get EXCEPTION_ACCESS_VIOLATION (0xc0000005), log file is attached.
Everything works if I do the same thing in one context. Can any one tell me what I am doing wrong? I can easily provide more info if it is necessary. Thank you.

spasi

The crash is in the Nvidia driver, which suggests a bug in your code. It would help if you could post a short program (without dependencies other than lwjgl) that reproduces the crash.

Fataho

Thank you for your replay. I tried to do what you ask. Everything works fine if I use one window, however when I use two windows, I get that VIOLATION, log file is attached. Please, let me know if you need more things from me. Here is the code:

import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.opengl.*;
import org.lwjgl.system.MemoryUtil;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;

public class FTH extends Thread{
	private long mainWindowID;
	private long secondWindowID;

	float[] vertices = {
		-0.5f,0.5f,-0.5f,-0.5f,-0.5f,-0.5f,0.5f,-0.5f,-0.5f,0.5f,0.5f,-0.5f,
		-0.5f,0.5f,0.5f,-0.5f,-0.5f,0.5f,0.5f,-0.5f,0.5f,0.5f,0.5f,0.5f,
		0.5f,0.5f,-0.5f,0.5f,-0.5f,-0.5f,0.5f,-0.5f,0.5f, 0.5f,0.5f,0.5f,
		-0.5f,0.5f,-0.5f,-0.5f,-0.5f,-0.5f,-0.5f,-0.5f,0.5f,-0.5f,0.5f,0.5f,
		-0.5f,0.5f,0.5f,-0.5f,0.5f,-0.5f,0.5f,0.5f,-0.5f,0.5f,0.5f,0.5f,
		-0.5f,-0.5f,0.5f,-0.5f,-0.5f,-0.5f,0.5f,-0.5f,-0.5f,0.5f,-0.5f,0.5f

	};

	int[] indices = {
		0,1,3,3,1,2,4,5,7,7,5,6,8,9,11,11, 9,10,12,13,15,
		15,13,14,16,17,19,19,17, 18,20,21,23,23,21,22

	};

	private int vaoId;
	private int verticesVBOId;
	private int indicesVboId;

	private boolean modelLoadedToVRAM = false;

	int vertexShaderID;
	int fragmentShaderID;
	int programID;

	public static void main(String[] args) {
		FTH fth = new FTH();
		fth.initialize();
		fth.loop();
		fth.cleanUp();
	}

	private void initialize(){
		GLFWErrorCallback.createPrint(System.err).set();

		if ( !glfwInit() )
			throw new IllegalStateException("Unable to initialize GLFW");

		glfwDefaultWindowHints(); // optional, the current window hints are already the default
		glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
		glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
		glfwWindowHint(GLFW_SAMPLES, 16); // For antialiasing

		// Create the main window
		mainWindowID = glfwCreateWindow(1024, 576, "Main Window", MemoryUtil.NULL, MemoryUtil.NULL);
		if (mainWindowID == MemoryUtil.NULL)
			throw new RuntimeException("Failed to create the GLFW window");

		glfwMakeContextCurrent(mainWindowID);

		// Second window
		glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE);
		secondWindowID = glfwCreateWindow(1, 1, "", MemoryUtil.NULL, mainWindowID);
		if (secondWindowID == MemoryUtil.NULL)
			throw new RuntimeException("Failed to create the GLFW window");

		glfwSwapInterval(1);
		glfwShowWindow(mainWindowID);
		GL.createCapabilities();

		// Initialize shaders
		vertexShaderID = loadShader("src/vertexShader.txt", GL20.GL_VERTEX_SHADER);
		fragmentShaderID = loadShader("src/fragmentShader.txt", GL20.GL_FRAGMENT_SHADER);
		programID = GL20.glCreateProgram();
		GL20.glAttachShader(programID, vertexShaderID);
		GL20.glAttachShader(programID, fragmentShaderID);
		GL20.glBindAttribLocation(programID, 0, "position");
		GL20.glLinkProgram(programID);
		GL20.glValidateProgram(programID);

		// CREATE EMPTY VABO IN MAIN THREAD
		vaoId = GL30.glGenVertexArrays();
		GL30.glBindVertexArray(vaoId);
		verticesVBOId = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, verticesVBOId);
		FloatBuffer vertices = BufferUtils.createFloatBuffer(0);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
		GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		GL30.glBindVertexArray(0);

		// When empty VABO is created, start the thread that loads the model, see run() method below
		start();

	}

    // THE LOOP
	private void loop(){
		while(!glfwWindowShouldClose(mainWindowID)) {

			if(isModelLoadedToVRAM()){
				GL11.glEnable(GL11.GL_DEPTH_TEST);
				GL11.glClearColor(0.1f, 0.3f, 0.5f, 0.0f);
				GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

				GL20.glUseProgram(programID);
				GL30.glBindVertexArray(vaoId);
				GL20.glEnableVertexAttribArray(0);

				GL11.glDrawElements(GL11.GL_TRIANGLES, indices.length, GL11.GL_UNSIGNED_INT, 0);

				GL20.glDisableVertexAttribArray(0);
				GL30.glBindVertexArray(0);
				GL20.glUseProgram(0);
			}

			glfwSwapBuffers(mainWindowID);
			glfwPollEvents();
		}
	}

	private void cleanUp(){
		GL30.glDeleteVertexArrays(vaoId);
		GL15.glDeleteBuffers(verticesVBOId);
		GL15.glDeleteBuffers(indicesVboId);

		GL20.glUseProgram(0);
		GL20.glDetachShader(programID, vertexShaderID);
		GL20.glDetachShader(programID, fragmentShaderID);
		GL20.glDeleteShader(vertexShaderID);
		GL20.glDeleteShader(fragmentShaderID);
		GL20.glDeleteProgram(programID);
        // Free the window callbacks and destroy the window
		glfwFreeCallbacks(mainWindowID); // Release the key callback
		glfwDestroyWindow(mainWindowID);
		glfwDestroyWindow(secondWindowID);

		// Terminate GLFW and free the error callback
		glfwTerminate();
		glfwSetErrorCallback(null).free();
	}

    // THREAD CODE
	public void run(){
		glfwMakeContextCurrent(secondWindowID);
		GL.createCapabilities();
		GL30.glBindVertexArray(vaoId);

		// Store data into VBO
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, verticesVBOId);
		FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
		verticesBuffer.put(vertices);
		verticesBuffer.flip();
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

		// Bind indices
		indicesVboId = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesVboId);
		IntBuffer indicesBuffer = BufferUtils.createIntBuffer(indices.length);
		indicesBuffer.put(indices);
		indicesBuffer.flip(); //
		GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);

		GL30.glBindVertexArray(0);
		glfwMakeContextCurrent(MemoryUtil.NULL);

		// Inform that model is loaded
		setModelLoadedToVRAM(true);
	}

	public int loadShader(String file, int type){
		StringBuilder shaderSource = new StringBuilder();
		try{
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line;
			while((line = reader.readLine())!=null){
				shaderSource.append(line).append("\n");
			}
			reader.close();
		}
		catch(IOException e){
			System.err.println("Could not read file!");
			e.printStackTrace();
			System.exit(-1);
		}
		int shaderID = GL20.glCreateShader(type);
		GL20.glShaderSource(shaderID, shaderSource);
		GL20.glCompileShader(shaderID);
		if(GL20.glGetShaderi(shaderID,GL20.GL_COMPILE_STATUS)== GL11.GL_FALSE){
			System.out.println(GL20.glGetShaderInfoLog(shaderID, 500));
			System.out.println("Could not compile shader!");
			System.exit(-1);
		}
		return shaderID;
	}

	public synchronized boolean isModelLoadedToVRAM() {
		return modelLoadedToVRAM;
	}

	public synchronized void setModelLoadedToVRAM(boolean modelLoadedToVRAM) {
		this.modelLoadedToVRAM = modelLoadedToVRAM;
	}
}


Vertex Shader code:
#version 400 core
in vec3 position;
out vec3 colour;
void main (void){
    gl_Position = vec4(position, 1.0);
    colour = vec3(position.x + 0.5, 1.0, position.y + 0.5);
}


Fragment Shader code:
#version 400 core
in vec3 colour;
out vec4 out_Color;
void main(void){
    out_Color = vec4(colour, 1.0);
}

spasi

The call GL30.glBindVertexArray(vaoId); in run() triggers an OpenGL error. You cannot share VAOs between OpenGL contexts. This means that indicesVboId, which is created in the secondary thread and was supposed to be part of the VAO state, is effectively never bound in the main thread, so glDrawElements crashes.

Using glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE) and GLUtil.setupDebugMessageCallback() (after the call to GL.createCapabilities()) is highly recommended when developing OpenGL programs.

Fataho

Now it works perfectly and it has no lag in main thread while the model is being loaded. I have done very stupid mistake. Thank you very much for showing me the solution and for your tip to use glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE) and GLUtil.setupDebugMessageCallback().