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:
- WGL: Failed to make context current: The requested transformation operation is not supported.
- 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
}