[LWJGL] 3 - Working with contexts

I feel as though I understand what a context is. I’m having trouble finding information on how to work with them if anyone can help my understanding or answer any of these questions.

I have a main window. From that main window I spawn a new thread. I cannot pass the context from the original window to the new thread?
Contexts cannot be altered by different threads? I feel like I’ve seen it stated as possible for newer versions?
So then I need a new context for the new thread, yes?

Can you say the conceptual steps I need to transition as I describe?
That is: same window, switch from main screen context to game context.
What I’m doing is: Main window, make new game in another thread, that game object is loaded as the game for the new game launch.
I want the main window to then have the context of the game. This approach would seem to dictate it being possible to use a context between threads?

My specific current problem:

java.lang.IllegalStateException: No GLCapabilities instance set for the current thread. Possible solutions:
	a) Call GL.createCapabilities() after making a context current in the current thread.
	b) Call GL.setCapabilities() if a GLCapabilities instance already exists for the current context.
	at org.lwjgl.opengl.GL.getCapabilities(GL.java:238)
	at org.lwjgl.opengl.GL11.nglGenTextures(GL11.java:3009)
	at org.lwjgl.opengl.GL11.glGenTextures(GL11.java:3035)

I tried to import the original context. No go. So I tried to make a new context (same launch method as first window/context), that still returned this error. Made a different method for trying to properly do it and that still returned this same error.

I also tried various combinations of the following to no avail.
//glfwMakeContextCurrent(window);
//GL.createCapabilities();
//GL.setCapabilities(null);
//window = GLFW.glfwGetCurrentContext();

I learn best by seeing working examples, but I’ve not see any. Thank you for any help!

The design of GLFW requires that all event handling happens on the main thread. So a loop that looks like:

while ( !glfwWindowShouldClose(window) ) {
    glfwPollEvents();

    // ...
}

should go on the main thread.

Rendering can happen on any thread, main or secondary. You can do rendering on a secondary thread by passing the window handle to that thread and calling glfwMakeContextCurrent(window). This makes the OpenGL context associated with that window current in the secondary thread. GL.createCapabilities() will work after that and you can use the LWJGL OpenGL bindings to do rendering.

In addition to what Spasi said:
Make sure you detach the OpenGL context from the first thread by calling glfwMakeContextCurrent(0L) before attaching the OpenGL context in another thread. Otherwise, attaching the OpenGL context in the other thread will not work.
See the JavaDoc of GLFW.glfwMakeContextCurrent() or the GLFW documentation of glfwMakeContextCurrent(). Especially the “Parameters” section.

Thank you. I believe I understand. I’m now getting this error and haven’t been able to figure it out either.

Exception in thread "Thread-4" java.util.MissingFormatArgumentException: Format specifier '%X'
	at java.util.Formatter.format(Unknown Source)
	at java.util.Formatter.format(Unknown Source)
	at java.lang.String.format(Unknown Source)
	at org.lwjgl.opengl.GL.createCapabilities(GL.java:326)
	at org.lwjgl.opengl.GL.createCapabilities(GL.java:296)
	at GameMakingThread.run(MainClass.java:4877)

That comes from the second line in:

glfwMakeContextCurrent(windowIn);
		GL.createCapabilities();

That’s a bug in 3.0.0. Please upgrade to the latest nightly build (which also happens to be a release candidate for 3.1.0).

Forgive me, I’m really not sure how newbish this is at this point. I may not fully yet have working with libraries like this down. I’m trying to tho. Could you possibly clear up why the download folder for a nightly build vs a stable is so different?

But I hooked up the jar for 3.1.0 and it appears to be missing several packages. I see that there are .jars with names similar to the missing elements. What am I supposed to be doing here? I’m in Eclipse if that matters.

LWJGL 3.1.0 is modular, meaning that each binding is now a separate artifact. The build configurator on the site can be used to include only the bindings you’re going to use.

Setting up your project in Eclipse should be as simple as adding the JARs you downloaded to the project classpath.

I made a new project so I could test this all out. I got as far as setting up my VM arguments, but it is still upset. So the code I’m using is that HelloWorld from the lwjgl site. All the code is happy but won’t run.

I get:


[LWJGL] Loading library (system): lwjgl
[LWJGL] 	lwjgl.dll not found in org.lwjgl.librarypath=<path>\lwjgl-3.1.0-custom
[LWJGL] [TLS] Failed to initialize unsafe implementation.
[LWJGL] ThreadLocalUtil state: TLState
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.lwjgl.system.MemoryAccess
	at org.lwjgl.system.Pointer.<clinit>(Pointer.java:22)
	at org.lwjgl.system.Platform.mapLibraryNameBundled(Platform.java:76)
	at org.lwjgl.glfw.GLFW.<clinit>(GLFW.java:562)
	at HelloWorld.run(Test.java:27)
	at HelloWorld.main(Test.java:103)

My VM arguments:
It had been a sole .dll before, but no .dll came with this package so I assumed this here.

-Djava.library.path=<path>\lwjgl-3.1.0-custom\lwjgl-natives-windows.jar 
-Djava.library.path=<path>\lwjgl-3.1.0-custom\lwjgl-glfw-natives-windows.jar 
-Dorg.lwjgl.librarypath=<path>\lwjgl-3.1.0-custom 
-Dorg.lwjgl.util.Debug=true

I reckon it does want a .dll tho since it is looking for one? I didn’t change anything when I downloaded this bundle, but no .dll inside.

Setting java.library.path or org.lwjgl.librarypath is not required. These two settings are still supported and used internally, but you shouldn’t need them unless you’re doing some customization (e.g. creating a platform-specific installer for your application).

LWJGL has something called the SharedLibraryLoader which takes care of extracting the native libraries from the natives JARs and loading them automatically. The only thing you need to do is add the JARs to the classpath. So, if for example you’re making a GLFW+OpenGL application and are running on Windows, you’ll have to set the classpath to include:

lwjgl.jar
lwjgl-natives-windows.jar
lwjgl-opengl.jar
lwjgl-glfw.jar
lwjgl-glfw-natives-windows.jar

That’s it. From the command line it’s simple; if you have all LWJGL JARs in a subfolder called “lwjgl”, you can do:

java -cp lwjgl/*; my.application.Main

and it should just work. In Eclipse, or any other IDE, configure your project to include all the LWJGL JARs in the classpath.

That’s a cool feature. Everything regarding setup is working now. Running from the jars you mentioned plus 2 for stb. No VM arguments.

I’m back to my original problem I think. This may be a conceptual flaw on my part now? I’m porting from a prior (non-LWJGL) build so I’d not be surprised to need such adjustments. I send the context to the game making thread so that it can work on that context.

Problems:

  • I no longer draw my loading screen. I understand why though (I think).
  • I can’t seem to get my context back to the window? The window freezes once I do glfwMakeContextCurrent(0L) (as I’d presume it should). Once the game making thread finishes, it runs through code that I would expect would at least send the context back to the window. However the window remains at “Not Responding”. But I see that it passes that code, so I think my flaw is with passing the context around.

Conceptually, there is no way to get around using 2 contexts here, right? I should either:
A) Use 1 context for everything except the loading screen. Loading screen context draws, then back to main context with game.
B) 1 context for the opening part up to and including the loading screen, then a completely new context is created and passed back with the game.

Technical question:
How do I properly pass this context around? The three statements: glfwMakeContextCurrent(windowIn); GL.createCapabilities(); and glfwMakeContextCurrent(0L); at their appropriate, reciprocal locations?

Thank you!

There are two things which are different:

  • handling window events via glfwPollEvents/glfwWaitEvents()
  • calling OpenGL methods / swapping buffers

The former MUST happen on the main thread.
The latter may happen on any thread, but only one thread at a time.

When the window freezes it means that you do not process the former: handling window events.

Also note that an OpenGL context is directly 1:1 associated with a window. So when you talk about passing a context “back to a window”, this is wrong. The context has always been associated with that single window.
The only thing that may change is the thread which calls OpenGL functions on behalf of that context.
So you can only attach/detach a thread to/from an OpenGL context. You cannot detach/attach an OpenGL context to/from a window.

You were right, I had misplaced glfwPollEvents(). They have been neutralized.

However my window still says (not responding) and draws nothing. I can tell it is doing stuff though (doing “more” I think). If I press esc it at least kills something in the window, but it still hangs. Could it hang like that if I had design flaws and it wasn’t getting back to the main loop? But I’d think not since the esc key worked.

What you say does dictate 2 contexts then, yes? One for showing and one for operating on. What is the appropriate procedure for that? Kill first window and make new window?

Hmm. Reckon the way to do this is to just forget the business about passing the context, set all “images” in main thread? I don’t care about openngl in the thread for anything other than loading images.

[quote=“SolidGold,post:13,topic:57819”]
Doing everything on the main thread is a legitimate solution. Reasons for using secondary thread(s) include: a) decoupling the event loop from rendering and b) decoupling loading OpenGL assets from rendering (asynchronous loading of models, textures etc).

For the specific issue you’re having with the window not responding, I would recommend simplifying your code and posting a sample that reproduces the freeze.

You can conceptualize an OpenGL context as a data structure deep in the OS/driver layer that maintains information about the currently available OpenGL capabilities. When you create a window through GLFW, it uses the system APIs to set up the OpenGL context, specifying which version of OpenGL is desired, how big the depth buffer should be (if any), whether to allow deprecated functions, and so on. If it helps, think of it as an interface, GLContext, with multiple implementations, one of which is created and associated with your window (conceptually it’s the same, anyway). That context is associated with your window and only your window and will remain associated with that window until it is destroyed. There is only ever one OpenGL context per window.

EDIT: I should add that this is what allows OpenGL to draw to your window. Without an OpenGL context, a window is rendered to using whatever the default mechanism is on the host system, be it GDI, Direct3D, Metal, or whatever. The concept of the context is the mechanism that allows the OpenGL API a means of integration with a variety of window managers across multiple systems. The details of creating and managing the context are system-specific, but the principles behind it remain the same.

From the driver’s perspective, only one OpenGL context can be “active” on a thread at any given time. So when you call glfwMakeCurrent, you are making your context the active context for the current thread. That means that all OpenGL calls on that thread will be through that particular context. If you have two windows, you can draw to them on the same thread first calling glfwMakeCurrent on one window’s context, then on the other’s. Moreover, a single context should never be active from multiple threads at the same time. If your context is active in thread1 and you want to draw through it in thread2, then you need to first disable it in thread1.

Thank you all very much! Lots of good info. Set it up with one context.

I still have issues now that the game loads and draws but nothing in the scope of this question I think. Thanks again!