LWJGL/lwjgl3

GLES and GL classes can't be used together with LWJGL 3.3.3 anymore

Berstanio opened this issue · 7 comments

Version

3.3.3

Platform

Windows x64

JDK

all

Module

core/OpenGL(ES)

Bug description

In LWJGL 3.3.3 ThreadLocalUtil#setFunctionMissingAddresses can't be called multiple times anymore.

throw new IllegalStateException("setFunctionMissingAddresses has been called already");

However, both the GL and GLES classes call that method in their static initializer:
ThreadLocalUtil.setFunctionMissingAddresses(GLESCapabilities.ADDRESS_BUFFER_SIZE);

ThreadLocalUtil.setFunctionMissingAddresses(GLCapabilities.ADDRESS_BUFFER_SIZE);

Therefor utlizing both classes in any way together is now not possible anymore. This used to work in LWJGL 3.3.2.

I'm not fully sure whether this can/should be considered a bug, but I thought I should open a issue just in case.

Stacktrace or crash log output

This is a stacktrace from a libGDX application.

Exception in thread "main" java.lang.ExceptionInInitializerError
	at org.lwjgl.opengl.GL11.<clinit>(GL11.java:38)
	at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.createWindow(Lwjgl3Application.java:467)
	at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.createWindow(Lwjgl3Application.java:448)
	at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.<init>(Lwjgl3Application.java:162)
	at com.badlogic.gdx.tests.lwjgl3.Lwjgl3TestStarter.main(Lwjgl3TestStarter.java:91)
Caused by: java.lang.IllegalStateException: setFunctionMissingAddresses has been called already
	at org.lwjgl.system.ThreadLocalUtil.setFunctionMissingAddresses(ThreadLocalUtil.java:202)
	at org.lwjgl.opengl.GL.create(GL.java:182)
	at org.lwjgl.opengl.GL.create(GL.java:126)
	at org.lwjgl.opengl.GL.create(GL.java:112)
	at org.lwjgl.opengl.GL.<clinit>(GL.java:85)
	... 5 more

Hey @Berstanio,

What's the use-case for using GL and GLES at the same time?

As a disclaimer, I'm not really a expert with the libGDX rendering stack, this is just how I understand it.

OpenGL calls happen usually through the GL classes, because by default the OpenGL driver are used, e.g. here: https://github.com/libgdx/libgdx/blob/6b419518b029aad223f14515ccb9d26ca22fbfd6/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java#L467
However, libGDX also supports rendering through ANGLE which is an OpenGL ES implementation. This is why I think the OpenGL ES class is needed for, to properly create the capability array.
https://github.com/libgdx/libgdx/blob/6b419518b029aad223f14515ccb9d26ca22fbfd6/backends/gdx-backend-lwjgl3/src/com/badlogic/gdx/backends/lwjgl3/Lwjgl3Application.java#L561-L570

As I said, I sadly can't give you more details on the "why" if you need them, I would have to ask others that are more familiar with ANGLE.

Hey @Berstanio,

I had some time to look into this today. Cloned libGDX, built it and ran the tests (gradlew launchTestsLwjgl3, let me know if there's something else I could try).

Afaict, libGDX working with the Angle backend on LWJGL 3.3.2 and 3.3.3 with your fix is a happy accident. It is not a supported way to use the LWJGL bindings. In fact, the GL11.glClearColor and GL11.glClear calls in createWindow do not even work, since GL.createCapabilities is never called. With the way the LWJGL bindings are implemented (specifically, the "special" thread-local handling of GL & GLES calls), libGDX actually ends up calling GLES20.glBufferData and GLES20.glCheckFramebufferStatus respectively. It's a miracle that the JVM doesn't crash, because the corresponding function signatures are close enough.

libGDX should replace the two direct GL11 calls in createWindow with an if/else block that calls the corresponding GLES20 functions when Angle is enabled. I tried it (used reflection, like in initiateGL) and all demos worked just fine.

In general, when Angle is enabled, libGDX should only make EGL & GLES calls. There's no reason to touch the GL class and load the standard OpenGL library (Angle uses it internally).

Thank you very much on the insights!
As I understand it, the issue also applies to com.badlogic.gdx.backends.lwjgl.LwjglGL20/LwjglGL30 classes, since they too call GLXX functions directly, right? So they should be all guarded too, I would assume?

No, with the Angle backend enabled OpenGL calls go through com.badlogic.gdx.backends.lwjgl3.angle.Lwjgl3GLES20. This is already abstracted away nicely and demos work correctly.

The only demos that do not work are those using GL 3.0-3.2 features. LWJGL has GLES 3.0-3.2 bindings and I don't think there's any functionality missing, so libGDX should be able to support these versions too.

See the com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics constructor for details.

No, with the Angle backend enabled OpenGL calls go through com.badlogic.gdx.backends.lwjgl3.angle.Lwjgl3GLES20. This is already abstracted away nicely and demos work correctly.

You are correct, I oversaw that.
I will move forward and try to fix the outstanding two GL calls, thank you!

The only demos that do not work are those using GL 3.0-3.2 features. LWJGL has GLES 3.0-3.2 bindings and I don't think there's any functionality missing, so libGDX should be able to support these versions too.

I don't know why it is missing, I wasn't involved in the development of this. I guess at the time GLES was added to libGDX, ANGLE didn't supported GLES 3

Your suggested fix works great, thank you!
Closing this as completed.