WGL: Failed to make context current


#1

Hi!

I really liked GLFW until I started to mess with multithreading. Everything works fine except when I try to resize the window. After that window starts to flicker (even tho its rendered in separate thread) and then some time later new errors pops up:

  1. WGL: Failed to make context current: The requested transformation operation is not supported.
  2. There is no OpenGL context current in the current thread.

Second error is probably caused by the first one. Basicaly all I have is Main class, Application class and Form class. Window creation, destruction and event handling is happening in main thread in Application class. Every window (form) loop is being processed in new thread. I made loop() function synchronized but that didn’t make any difference. I also must double click window close button to actually close it which is strange. I’m using LWJGL (java binding of GLFW) which I hope is not a problem, because everything should be the same. I do not know where the error might be and why the application crash after window resize. I also added video of the problem and my project if that helps.

Also sorry for my bad english, I’m not a native speaker. Any help is really appreciated! I am very desperate. Thanks in advance for any advice.

Video of the problem: https://www.youtube.com/watch?v=DrQWRtJeDdI
My project for download: https://nofile.io/f/fgaGDCHTejg/Project.rar

Main class:

import Core.Application;
import Core.Form;

// Main class
public class Main
{
    // Main function
    public static void main(String[] args)
    {
        // Create new Application object and run it
        new Application().run(new Form());
    }
}

Application class:

package Core;

import org.lwjgl.glfw.*;
import java.util.ArrayList;
import java.util.List;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;

/**
 * Framework main application entry class
 */
// Application class
public class Application
{
    // True if application (main loop) should end
    private static boolean exit = false;

    // List of all currently opened (instantiated) forms
    private static List<Form> forms = new ArrayList<Form>();

    /**
     * Creates new Application instance
     */
    // Constructor
    public Application()
    {
        // Initialize GLFW library
        init();
    }

    /**
     * Framework entry method.
     * Starts new application life cycle and opens passed form
     * @param form First form to open on application start
     */
    // Run function. Main framework entry
    public void run(Form form)
    {
        // Make passed form visible
        form.show();

        // Run main loop
        loop();

        // Terminate GLFW and free the error callback
        // Called after main loop ended with: exit = true
        glfwTerminate();
        glfwSetErrorCallback(null).free();
    }

    /**
     * Ends application and closes all currently opened forms if there are any.
     */
    // End application
    public static void exit()
    {
        // Close all forms
        for (Form form : forms)
        {
            destroyGlfwWindow(form.getHandle());
        }

        // Clear forms collection
        forms.clear();

        // End main while loop
        exit = true;
    }

    // Initialize GLWF library
    void init()
    {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();

        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if (!glfwInit())
        {
            throw new IllegalStateException("Unable to initialize GLFW");
        }

        // Configure GLFW
        glfwDefaultWindowHints();

        // Optional, the current window hints are already the default
        // The window will stay hidden after creation
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);

        // The window will be resizable
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
    }

    // Main loop function
    void loop()
    {
        // Main thread will process window events
        while (!exit)
        {
            glfwPollEvents();
        }
    }

    // Removes form from forms collection
    static void removeForm(Form form)
    {
        // If there is only one form remaining
        if (forms.size() == 1)
        {
            // No forms opened
            // Exit application (main while loop)
            exit = true;
        }

        // Add form to waiting queue
        forms.remove(form);
    }

    // Add form to forms collection
    static void addForm(Form form)
    {
        // Add form to waiting queue
        forms.add(form);
    }

    // Return new GLFW window
    // All windows must be created from main thread
    static long createGlfwWindow(int width, int height, String name)
    {
        // Create window
        return glfwCreateWindow(width, height, name, NULL, NULL);
    }

    // Destroy given GLFW window
    // All windows must be destroyed from main thread
    static void destroyGlfwWindow(long window)
    {
        // Close the window
        glfwSetWindowShouldClose(window, true);

        // Free the window callbacks and destroy the window
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);
    }
}

Form class:

package Core;

import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryStack;
import java.nio.IntBuffer;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose;
import static org.lwjgl.nanovg.NanoVG.*;
import static org.lwjgl.nanovg.NanoVGGL2.NVG_ANTIALIAS;
import static org.lwjgl.nanovg.NanoVGGL2.NVG_STENCIL_STROKES;
import static org.lwjgl.nanovg.NanoVGGL2.nvgCreate;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;

// Core.Form class
public class Form
{
    // The window handle
    private long window;

    // The window swap interval (default 0)
    private int swapInterval = 0;

    // True if window (form) is focused
    private boolean focused = false;

    // Current window state
    private WindowStates windowState;

    // Constructor
    public Form()
    {
        // Create GLWF window
        createGlfwWindow();

        // Create new thread which will hold window loop
        new Thread(() -> loop()).start();

        // Add form to forms collection
        Application.addForm(this);
    }

    // Loop function
    synchronized void loop()
    {
        // Check if user closed the window
        while (!glfwWindowShouldClose(window))
        {
            // Make context current
            glfwMakeContextCurrent(window);

            // This line is critical for LWJGL's inter-operation with GLFW's
            // OpenGL context, or any context that is managed externally.
            // LWJGL detects the context that is current in the current thread,
            // creates the GLCapabilities instance and makes the OpenGL
            // bindings available for use
            GL.createCapabilities();

            // Set v-sync
            glfwSwapInterval(swapInterval);

            // Set color
            glClearColor(0.6f, 1.0f, 0.0f, 0.0f);

            // Clear drawing area
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

            // Draw - just for demonstration purposes
            draw();

            // Swap the color buffers
            glfwSwapBuffers(window);

            // Detach the current context.
            glfwMakeContextCurrent(NULL);
        }

        // Destroy window
        destroy();
    }

    int xx = 0;

    // Draw function
    public void draw()
    {
        xx++;

        if (xx > 100) { xx = 0;}

        int width = 300;
        int height = 200;

        // GL projection
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GL11.glOrtho(0, width, height, 0, 1, -1);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);

        // NanoVG handle
        long vg = nvgCreate(NVG_ANTIALIAS | NVG_STENCIL_STROKES);

        // NanoVG fail
        if (vg == NULL)
        {
            throw new RuntimeException("Failed to init NanoVG");
        }

        // Calculate pixel ration for hi-dpi devices.
        float pxRatio = (float)width / (float)height;

        nvgBeginFrame(vg, width, height, pxRatio);

        nvgBeginPath(vg);
        nvgRect(vg, 20 + xx, 20 + xx, 50, 50);
        nvgFill(vg);

        nvgEndFrame(vg);
    }

    // Destroys window
    void destroy()
    {
        // Close the window
        // Must be called from main thread
        Application.destroyGlfwWindow(window);

        // Remove form from collection
        Application.removeForm(this);
    }

    // Creates new GLFW window
    void createGlfwWindow()
    {
        // Create the window
        window = Application.createGlfwWindow(300, 200, "Window");

        // Window fail
        if (window == NULL)
        {
            throw new RuntimeException("Failed to create the GLFW window");
        }

        // Callback: Setup a key callback.
        // It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, (handle, key, scancode, action, mods) ->
        {
            if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
            {
                destroy();
            }

            if (key == GLFW_KEY_N && action == GLFW_RELEASE)
            {
                Form myForm = new Form();
                myForm.show();
            }

            if (key == GLFW_KEY_X && action == GLFW_RELEASE)
            {
                Application.exit();
            }
        });

        // Callback: Setup focus callback. Update focused variable
        glfwSetWindowFocusCallback(window, (handle, focus) ->
        {
            // Update focused var
            focused = focus;
        });

        // Callback: Setup iconify (minimize / maximize) callback.
        glfwSetWindowIconifyCallback(window, (handle, value) ->
        {
            // Update window state var
            windowState = (value) ? WindowStates.MINIMIZED : WindowStates.NORMAL;
        });

        // Get the thread stack and push a new frame
        // The stack frame is popped automatically
        try (MemoryStack stack = stackPush())
        {
            IntBuffer pWidth = stack.mallocInt(1);
            IntBuffer pHeight = stack.mallocInt(1);

            // Get the window size passed to glfwCreateWindow
            glfwGetWindowSize(window, pWidth, pHeight);

            // Get the resolution of the primary monitor
            GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());

            // Center the window
            glfwSetWindowPos(window, (videoMode.width() - pWidth.get(0)) / 2, (videoMode.height() - pHeight.get(0)) / 2);
        }
    }

    // Returns window handle
    long getHandle()
    {
        return window;
    }

    /**
     * Returns form buffer swap interval
     * @return Window buffer swap interval
     */
    // Returns window swapInterval
    public int getSwapInterval()
    {
        return swapInterval;
    }

    /**
     * Set form buffer swap interval
     * @param interval Swap buffer interval
     */
    // Set window swapInterval
    public void setSwapInterval(int interval)
    {
        swapInterval = interval;
    }

    /**
     * Returns true if form is focused. Otherwise false
     * @return Boolean
     */
    // Returns if GLFW window (form) is focused
    public boolean isFocused()
    {
        return focused;
    }

    /**
     * Shows form
     */
    // Show GLFW window (form)
    public void show()
    {
        // Update window state
        windowState = WindowStates.NORMAL;

        // Make the window visible
        glfwShowWindow(window);
    }

    /**
     * Hides form
     */
    // Hides GLFW window (form)
    public void hide()
    {
        // Update window state
        windowState = WindowStates.HIDDEN;

        // Hide the window
        glfwHideWindow(window);
    }

    /**
     * Closes form
     */
    // Closes GLFW window (form)
    public void close()
    {
        destroy();
    }

    /**
     * Gives focus to form
     */
    // Gives window (form) focus
    public void focus()
    {
        glfwFocusWindow(window);
    }

    /**
     * Returns forms's current window state
     * @return Form current window state
     */
    // Returns current windowState
    public WindowStates getWindowState()
    {
        return windowState;
    }
}

WindowStates enum class (not really important now):

package Core;

// Window states
public enum WindowStates
{
    NORMAL,
    MAXIMIZED,
    MINIMIZED,
    HIDDEN
}

#2

This is somewhat convoluted Java code, so my ability to help is limited.

I suspect the issue is LWJGL related. On the second thread you have the code:

        // Make context current
        glfwMakeContextCurrent(window);

        // This line is critical for LWJGL's inter-operation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the GLCapabilities instance and makes the OpenGL
        // bindings available for use
        GL.createCapabilities();

I suspect you may need to call GL.createCapabilities() on the main thread first before you run your second loop, but I don’t know LWJGL.

I’d also change the glfwPollEvents() to glfwWaitEvents() when using multithreading, so that thread doesn’t use up CPU time for no purpose.


#3

Thanks for your time man!
Both tweaks definitely improved performance but unfortunately calling createCapabilities() from main thread did not solve the problem…


#4

Well, I found that glfwMakeContextCurrent (wglMakeCurrent) function is not reentrant and I must create spin-lock around glfwMakeContextCurrent(). I have no idea what it means because I’m new in Java and multithreading… However, my problem is caused by java and multithreading issues and not by GLFW itself …


#5

glfwMakeContextCurrent is safe to call from any thread in C, see the GLFW thread safety docs

You may want to try the LWJGL3 thread example as a starting point:


#6

Thanks a lot that’s a great start!