Skip to content

Commit

Permalink
feat(GL) configurable context management API
Browse files Browse the repository at this point in the history
* Added Configuration.OPENGL_CONTEXT_API option.
* Added explicit support for libOSMesa.
* Added Wayland detection that changes the default context management
  API to EGL.
  • Loading branch information
Spasi committed Dec 11, 2024
1 parent a4e94b6 commit 05ef628
Show file tree
Hide file tree
Showing 31 changed files with 279 additions and 151 deletions.
4 changes: 4 additions & 0 deletions doc/notes/3.3.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ This build includes the following changes:
* The `org.jspecify:jspecify` dependency remains optional.
- Core: Added `Configuration.DISABLE_HASH_CHECKS`, a dynamic option that disables shared library hash checks. (#1007)
- FreeBSD: LWJGL now requires FreeBSD 13.3 or later. (up from 13.2)
- OpenGL: Added `Configuration.OPENGL_CONTEXT_API` option to control the default OpenGL context management API.
- OpenGL: The `Configuration.OPENGL(ES)_CONTEXT_API` options now support `"OSMesa"` as a value.
* Similar to `GLFW_OSMESA_CONTEXT_API`, LWJGL will try to load libOSMesa when enabled, without having to change `Configuration.OPENGL_LIBRARY_NAME`.
- OpenGL: The default context management API is now EGL when Wayland is detected.
- msdfgen: Added support for glyph index based msdfgen-ext APIs. (#1002)

#### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,20 @@ public class Configuration<T> {
/** Similar to {@link #LIBRARY_NAME} for the EGL library (<b>org.lwjgl.egl.libname</b>). */
public static final Configuration<String> EGL_LIBRARY_NAME = new Configuration<>("org.lwjgl.egl.libname", StateInit.STRING);

/** Returns the default EGL library names for the current platform. */
public static String[] EGL_LIBRARY_NAME_DEFAULTS() {
switch (Platform.get()) {
case FREEBSD:
case LINUX:
return new String[] {"libEGL.so.1"};
case MACOSX:
return new String[] {"EGL"};
case WINDOWS:
return new String[] {"libEGL", "EGL"};
}
throw new IllegalStateException();
}

/** Similar to {@link #OPENGL_EXTENSION_FILTER} for the EGL library (<b>org.lwjgl.egl.extensionFilter</b>). */
public static final Configuration<Object> EGL_EXTENSION_FILTER = new Configuration<>("org.lwjgl.egl.extensionFilter", StateInit.STRING);

Expand Down Expand Up @@ -576,6 +590,37 @@ public class Configuration<T> {
/** Similar to {@link #LIBRARY_NAME} for the OpenGL library (<b>org.lwjgl.opengl.libname</b>). */
public static final Configuration<String> OPENGL_LIBRARY_NAME = new Configuration<>("org.lwjgl.opengl.libname", StateInit.STRING);

/** Returns the default OpenGL library names for the current platform. */
public static String[] OPENGL_LIBRARY_NAME_DEFAULTS() {
switch (Platform.get()) {
case FREEBSD:
case LINUX:
return new String[] {"libGLX.so.0", "libGL.so.1", "libGL.so"};
case MACOSX:
return new String[] {"/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL"};
case WINDOWS:
return new String[] {"opengl32"};
}
throw new IllegalStateException();
}

/** Similar to {@link #LIBRARY_NAME} for the OSMesa library (<b>org.lwjgl.opengl.osmesa.libname</b>). */
public static final Configuration<String> OPENGL_OSMESA_LIBRARY_NAME = new Configuration<>("org.lwjgl.opengl.osmesa.libname", StateInit.STRING);

/** Returns the default OSMEsa library names for the current platform. */
public static String[] OPENGL_OSMESA_LIBRARY_NAME_DEFAULTS() {
switch (Platform.get()) {
case FREEBSD:
case LINUX:
return new String[] {"libOSMesa.so.8", "libOSMesa.so.6", "libOSMesa.so"};
case MACOSX:
return new String[] {"libOSMesa.8.dylib"};
case WINDOWS:
return new String[] {"libOSMesa", "OSMesa"};
}
throw new IllegalStateException();
}

/**
* Can be used to limit the maximum available OpenGL version.
*
Expand Down Expand Up @@ -606,6 +651,29 @@ public class Configuration<T> {
*/
public static final Configuration<Object> OPENGL_EXTENSION_FILTER = new Configuration<>("org.lwjgl.opengl.extensionFilter", StateInit.STRING);

/**
* Defines the API that manages OpenGL contexts.
*
* <p>Supported values:</p>
* <ul>
* <li><em>native</em> - context management is provided by the native platform.<br>
* <li><em>EGL</em> - context management is provided by EGL.</li>
* <li><em>OSMesa</em> - context management is provided by OSMesa.</li>
* </ul>
*
* <p>If this option is not set, LWJGL will attempt to use the native platform API. If the native platform API is not available, it will attempt to use EGL
* and then OSMesa.</p>
*
* <p>If this option is not set and Wayland is detected ({@code XDG_SESSION_TYPE == "wayland"} and {@code WAYLAND_DISPLAY} is defined) on Linux and
* FreeBSD, then EGL becomes the default choice.</p>
*
* <p style="font-family: monospace">
* Property: <b>org.lwjgl.opengl.contextAPI</b><br>
* &nbsp; &nbsp; Type: String<br>
* &nbsp; &nbsp;Usage: Dynamic</p>
*/
public static final Configuration<String> OPENGL_CONTEXT_API = new Configuration<>("org.lwjgl.opengl.contextAPI", StateInit.STRING);

// -- OPENGL ES

/** Similar to {@link #EGL_EXPLICIT_INIT} for the OpenGL ES library (<b>org.lwjgl.opengles.explicitInit</b>). */
Expand All @@ -614,6 +682,20 @@ public class Configuration<T> {
/** Similar to {@link #LIBRARY_NAME} for the OpenGL ES library (<b>org.lwjgl.opengles.libname</b>). */
public static final Configuration<String> OPENGLES_LIBRARY_NAME = new Configuration<>("org.lwjgl.opengles.libname", StateInit.STRING);

/** Returns the default OpenGL ES library names for the current platform. */
public static String[] OPENGLES_LIBRARY_NAME_DEFAULTS() {
switch (Platform.get()) {
case FREEBSD:
case LINUX:
return new String[] {"libGLESv2.so.2"};
case MACOSX:
return new String[] {"GLESv2"};
case WINDOWS:
return new String[] {"libGLESv2", "GLESv2"};
}
throw new IllegalStateException();
}

/** Similar to {@link #OPENGL_MAXVERSION} for the OpenGL ES library (<b>org.lwjgl.opengles.maxVersion</b>). */
public static final Configuration<Object> OPENGLES_MAXVERSION = new Configuration<>("org.lwjgl.opengles.maxVersion", StateInit.STRING);

Expand All @@ -627,9 +709,11 @@ public class Configuration<T> {
* <ul>
* <li><em>EGL</em> - context management is provided by EGL.</li>
* <li><em>native</em> - context management is provided by the native platform.<br>
* <li><em>OSMesa</em> - context management is provided by OSMesa.<br>
* </ul>
*
* <p>If this option is not set, LWJGL will first attempt to use EGL. If EGL is not available, it will attempt to use the native platform API.</p>
* <p>If this option is not set, LWJGL will first attempt to use EGL. If EGL is not available, it will attempt to use the native platform API and then
* OSMesa.</p>
*
* <p style="font-family: monospace">
* Property: <b>org.lwjgl.opengl.contextAPI</b><br>
Expand Down
17 changes: 1 addition & 16 deletions modules/lwjgl/egl/src/main/java/org/lwjgl/egl/EGL.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,7 @@ private EGL() {}

/** Loads the EGL native library, using the default library name. */
public static void create() {
SharedLibrary EGL;
switch (Platform.get()) {
case FREEBSD:
case LINUX:
EGL = Library.loadNative(EGL.class, "org.lwjgl.egl", Configuration.EGL_LIBRARY_NAME, "libEGL.so.1");
break;
case MACOSX:
EGL = Library.loadNative(EGL.class, "org.lwjgl.egl", Configuration.EGL_LIBRARY_NAME, "EGL");
break;
case WINDOWS:
EGL = Library.loadNative(EGL.class, "org.lwjgl.egl", Configuration.EGL_LIBRARY_NAME, "libEGL", "EGL");
break;
default:
throw new IllegalStateException();
}
create(EGL);
create(Library.loadNative(EGL.class, "org.lwjgl.egl", Configuration.EGL_LIBRARY_NAME, Configuration.EGL_LIBRARY_NAME_DEFAULTS()));
}

/**
Expand Down
78 changes: 66 additions & 12 deletions modules/lwjgl/opengl/src/main/java/org/lwjgl/opengl/GL.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,73 @@ static void initialize() {

/** Loads the OpenGL native library, using the default library name. */
public static void create() {
SharedLibrary GL;
SharedLibrary GL = null;

String contextAPI = Configuration.OPENGL_CONTEXT_API.get();

boolean tryEGL = "EGL".equals(contextAPI) || (contextAPI == null && isWayland());
if (tryEGL) {
GL = loadEGL();
} else if ("OSMesa".equals(contextAPI)) {
GL = loadOSMesa();
}

if (GL == null) {
GL = loadNative();
if (GL == null && !"native".equals(contextAPI)) {
if (!tryEGL) {
GL = loadEGL();
}
if (GL == null && !"OSMesa".equals(contextAPI)) {
GL = loadOSMesa();
}
}
}

if (GL == null) {
throw new IllegalStateException("There is no OpenGL context management API available.");
}

create(GL);
}

private static boolean isWayland() {
switch (Platform.get()) {
case FREEBSD:
case LINUX:
GL = Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.OPENGL_LIBRARY_NAME, "libGLX.so.0", "libGL.so.1", "libGL.so");
break;
case MACOSX:
GL = Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.OPENGL_LIBRARY_NAME, "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL");
break;
case WINDOWS:
GL = Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.OPENGL_LIBRARY_NAME, "opengl32");
break;
default:
throw new IllegalStateException();
// The following matches the test GLFW does to enable the Wayland backend.
if ("wayland".equals(System.getenv("XDG_SESSION_TYPE")) && System.getenv("WAYLAND_DISPLAY") != null) {
return true;
}
}
return false;
}

private static @Nullable SharedLibrary loadNative() {
try {
return Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.OPENGL_LIBRARY_NAME, Configuration.OPENGL_LIBRARY_NAME_DEFAULTS());
} catch (Throwable ignored) {
apiLog("[GL] Failed to initialize context management based on native OpenGL platform API");
return null;
}
}

private static @Nullable SharedLibrary loadEGL() {
try {
return Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.EGL_LIBRARY_NAME, Configuration.EGL_LIBRARY_NAME_DEFAULTS());
} catch (Throwable ignored) {
apiLog("[GL] Failed to initialize context management based on EGL");
return null;
}
}

private static @Nullable SharedLibrary loadOSMesa() {
try {
return Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.OPENGL_OSMESA_LIBRARY_NAME, Configuration.OPENGL_OSMESA_LIBRARY_NAME_DEFAULTS());
} catch (Throwable ignored) {
apiLog("[GL] Failed to initialize context management based on OSMesa");
return null;
}
create(GL);
}

/**
Expand Down Expand Up @@ -136,6 +187,9 @@ private static void create(SharedLibrary OPENGL) {
GetProcAddress = library.getFunctionAddress("wglGetProcAddress");
break;
}
if (GetProcAddress == NULL) {
GetProcAddress = library.getFunctionAddress("eglGetProcAddress");
}
if (GetProcAddress == NULL) {
GetProcAddress = library.getFunctionAddress("OSMesaGetProcAddress");
}
Expand Down
Loading

0 comments on commit 05ef628

Please sign in to comment.