0
\$\begingroup\$

Introduction:

I am currently designing a game in Java using the LWJGL 3.0, with Gradle. I have quite an advanced knowledge on multi-threading, and I am aware how GLFW does/doesn't implement multi-threading from this guide. Currently, I have one class Updater with a loop for updating, and another class Renderer for rendering, each extending Runnable with their own loops and time calculations. Below is a diagram showing the structure of the classes and update and render methods. I have also set up a lock system, using the Java Lock class (again below).

enter image description here

My Problem:

Despite setting each thread as the current context before each loop executes its window call, and setting the current context to null at the end, I still get the error Exception in thread "RENDERER" java.lang.IllegalStateException: GLFW error [0x10008]: WGL: Failed to make context current: The requested resource is in use. Any idea as to why? Been at this for a few hours now.

Loop class:

private void threadLoop() {
    try {
        lock.lock();
        locked = true;
        loop();
        TPS++;
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
        locked = false;
    }
}

@Override
public void run() {
    init();
    long lastTime = System.nanoTime();
    double ns = SECOND / (MAX_TPS/2);
    double delta = 0;
    long timer = System.currentTimeMillis();
    while (running) {
        long now = System.nanoTime();
        delta += (now - lastTime) / ns;
        lastTime = now;
        while(delta >= 1) {
            threadLoop();
            TPS++;
            delta--;
        }
        if(System.currentTimeMillis() - timer > 1000) {
            finalTPS = TPS;
            timer += 1000;
            TPS = 0;
        }
    }
    stop();
}

Thread Manager:

public void update() {
    if (window == null) return;
    window.setThread();
    window.update();
    window.nullThread();
}

public void render() {
    if (window == null) return;
    window.setThread();
    window.render();
    window.nullThread();
}

Renderer:

@Override
protected void loop() {
    if (state != null) state.render();
    threadManager.intermediateCode();
    threadManager.render();
}

Updater:

@Override
protected void loop() {
    if (state != null) state.update();
    threadManager.intermediateCode();
    threadManager.update();
}

Window:

public void setThread() {
    glfwMakeContextCurrent(getID());
}

public void nullThread() {
    glfwMakeContextCurrent(NULL);
}

public void render() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(0.0f, 0.3f, 0.8f, 0.0f);
    glfwSwapBuffers(ID);
    System.out.println("Rendered window");
}

public void update() {
    glfwPollEvents();
    System.out.println("Updated window");
}
\$\endgroup\$
1
\$\begingroup\$

After doing some more research, I added a lock system over the access to the window.update() and window.render() calls, like below. This ensured that only one thread was accessing the window at any time, hence the context resource could not be in use. Not sure why my lock system in the main loop didn't prevent this, but it's fixed now.

New update and render methods in ThreadManager class

public synchronized void update() {
    if (window == null) return;
    if (rendering) {
        try {
            wait();
        } catch (InterruptedException e) {
            System.out.println("InterruptedException caught.");
        }
    }
    stateManager.update();
    window.setThread();
    window.update();
    window.nullThread();
    rendering = true;
    notify();
}

public synchronized void render() {
    if (window == null) return;
    if (!rendering) {
        try {
            wait();
        } catch (InterruptedException e) {
            System.out.println("InterruptedException caught.");
        }
    }
    stateManager.render();
    window.setThread();
    window.render();
    window.nullThread();
    rendering = false;
    notify();
}

EDIT: Upon further research, I found that the glfwPollEvents() method must be called from the same thread that the window is created in. Therefor I just added threadManager.init() to the Updater init method, and it fixed the issue without the need of the second lock above.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.