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.
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.
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);
}
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.
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().