diff --git a/.gitignore b/.gitignore index 8faf6026a8..36a2e3989e 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ javadoc_deploy.pub !.vscode/settings.json !.vscode/JME_style.xml !.vscode/extensions.json +joysticks-*.txt +hs_err_pid*.log diff --git a/common.gradle b/common.gradle index 042d88e3b5..829e823ef1 100644 --- a/common.gradle +++ b/common.gradle @@ -34,6 +34,9 @@ repositories { flatDir { dirs rootProject.file('lib') } + maven { + url "https://maven.rblb.it/riccardobl/angle-natives" + } } dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b56daa8a7..8d2a501179 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,7 @@ lwjgl3-opencl = { module = "org.lwjgl:lwjgl-opencl", version.ref = "lwjgl3" lwjgl3-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl3" } lwjgl3-openvr = { module = "org.lwjgl:lwjgl-openvr", version.ref = "lwjgl3" } lwjgl3-ovr = { module = "org.lwjgl:lwjgl-ovr", version.ref = "lwjgl3" } +lwjgl3-opengles = { module = "org.lwjgl:lwjgl-opengles", version.ref = "lwjgl3" } mokito-core = "org.mockito:mockito-core:3.12.4" diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java index 701da81518..4afe3002dd 100644 --- a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java +++ b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java @@ -348,7 +348,7 @@ public String glGetProgramInfoLog(int program, int maxLength) { @Override public long glGetQueryObjectui64(int query, int pname) { - IntBuffer buff = IntBuffer.allocate(1); + IntBuffer buff = tmpBuff.clear(); //FIXME This is wrong IMO should be glGetQueryObjectui64v with a LongBuffer but it seems the API doesn't provide it. GLES30.glGetQueryObjectuiv(query, pname, buff); return buff.get(0); @@ -356,8 +356,8 @@ public long glGetQueryObjectui64(int query, int pname) { @Override public int glGetQueryObjectiv(int query, int pname) { - IntBuffer buff = IntBuffer.allocate(1); - GLES30.glGetQueryiv(query, pname, buff); + IntBuffer buff = tmpBuff.clear(); + GLES30.glGetQueryObjectuiv(query, pname, buff); return buff.get(0); } @@ -758,5 +758,10 @@ public void glGenVertexArrays(IntBuffer arrays) { } + @Override + public String glGetString(int name, int index) { + return GLES30.glGetStringi(name, index); + } + } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java index 755e076cac..b70a5123f4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java @@ -42,10 +42,14 @@ public interface GLES_30 extends GL { public static final int GL_RGB10_A2 = 0x8059; public static final int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368; - + public static final int GL_NUM_EXTENSIONS = 0x821D; + public void glBindVertexArray(int array); public void glDeleteVertexArrays(IntBuffer arrays); public void glGenVertexArrays(IntBuffer arrays); + + public String glGetString(final int name, final int index); + } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index f4ae6fe0e1..6281ce3e7b 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -186,7 +186,15 @@ private HashSet loadExtensions() { gl3.glGetInteger(GL3.GL_NUM_EXTENSIONS, intBuf16); int extensionCount = intBuf16.get(0); for (int i = 0; i < extensionCount; i++) { - String extension = gl3.glGetString(GL.GL_EXTENSIONS, i); + String extension = gl3.glGetString(GL3.GL_EXTENSIONS, i); + extensionSet.add(extension); + } + } else if (caps.contains(Caps.OpenGLES30)) { + GLES_30 gles = (GLES_30) gl; + gles.glGetInteger(GLES_30.GL_NUM_EXTENSIONS, intBuf16); + int extensionCount = intBuf16.get(0); + for (int i = 0; i < extensionCount; i++) { + String extension = gles.glGetString(GLES_30.GL_EXTENSIONS, i); extensionSet.add(extension); } } else { diff --git a/jme3-core/src/main/java/com/jme3/system/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java index e159586f2f..2a33903b17 100644 --- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java +++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java @@ -219,6 +219,8 @@ public final class AppSettings extends HashMap { */ public static final String LWJGL_OPENAL = "LWJGL"; + public static final String ANGLE_GLES3 = "ANGLE_GLES3"; + /** * Use the Android MediaPlayer / SoundPool based renderer for Android audio capabilities. *

@@ -282,7 +284,7 @@ public final class AppSettings extends HashMap { defaults.put("Samples", 0); defaults.put("Fullscreen", false); defaults.put("Title", JmeVersion.FULL_NAME); - defaults.put("Renderer", LWJGL_OPENGL32); + defaults.put("Renderer", ANGLE_GLES3); defaults.put("AudioRenderer", LWJGL_OPENAL); defaults.put("DisableJoysticks", true); defaults.put("UseInput", true); diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java index 6ef6272ea0..7354ded511 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java @@ -72,6 +72,14 @@ public final class TGALoader implements AssetLoader { // 11 - run-length encoded, black and white image public static final int TYPE_BLACKANDWHITE_RLE = 11; + private static void convertBGRtoRGB(byte[] data, int dl){ + for (int i = 0; i < data.length; i += dl) { + byte tmp = data[i]; + data[i] = data[i + 2]; + data[i + 2] = tmp; + } + } + @Override public Object load(AssetInfo info) throws IOException { if (!(info.getKey() instanceof TextureKey)) { @@ -261,7 +269,8 @@ public static Image load(InputStream in, boolean flip) throws IOException { // rawData[rawDataIndex++] = blue; // } } - format = Format.BGR8; + convertBGRtoRGB(rawData, dl); + format = Format.RGB8; } else if (pixelDepth == 32) { for (int i = 0; i <= (height - 1); i++) { if (!flip) { diff --git a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java index 81c197a3c5..042e8ccf48 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java +++ b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java @@ -216,7 +216,7 @@ public JmeContext newContext(AppSettings settings, Type contextType) { || contextType == JmeContext.Type.Headless) { ctx = new NullContext(); ctx.setSettings(settings); - } else if (settings.getRenderer().startsWith("LWJGL")) { + } else if (settings.getRenderer().startsWith("LWJGL") || settings.getRenderer().startsWith("ANGLE")) { ctx = newContextLwjgl(settings, contextType); ctx.setSettings(settings); } else if (settings.getRenderer().startsWith("JOGL")) { diff --git a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java index c0eb8613e6..1c769d6257 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java +++ b/jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java @@ -44,6 +44,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.jme3.system.NativeLibraries.LibraryInfo; import com.jme3.util.res.Resources; /** @@ -94,6 +95,16 @@ public static void registerNativeLibrary(NativeLibrary library) { nativeLibraryMap.put(library.getKey(), library); } + /** + * Register a new native library. + * + * This simply registers a known library, the actual extraction and loading is performed by calling + * {@link #loadNativeLibrary(java.lang.String, boolean) }. + */ + public static void registerNativeLibrary(LibraryInfo library) { + library.getNativeVariants().forEach(NativeLibraryLoader::registerNativeLibrary); + } + /** * Register a new native library. * @@ -428,11 +439,15 @@ public static void extractNativeLibrary(Platform platform, String name, File tar /** * First extracts the native library and then loads it. * - * @param name The name of the library to load. - * @param isRequired If true and the library fails to load, throw exception. If - * false, do nothing if it fails to load. + * @param name + * The name of the library to load. + * @param isRequired + * If true and the library fails to load, throw exception. If false, do nothing if it fails to + * load. + * + * @return The absolute path of the loaded library. */ - public static void loadNativeLibrary(String name, boolean isRequired) { + public static String loadNativeLibrary(String name, boolean isRequired) { if (JmeSystem.isLowPermissions()) { throw new UnsupportedOperationException("JVM is running under " + "reduced permissions. Cannot load native libraries."); @@ -453,7 +468,7 @@ public static void loadNativeLibrary(String name, boolean isRequired) { " is not available for your OS: {1}", new Object[]{name, platform}); } - return; + return null; } } @@ -461,7 +476,7 @@ public static void loadNativeLibrary(String name, boolean isRequired) { if (pathInJar == null) { // This platform does not require the native library to be loaded. - return; + return null; } URL url = Resources.getResource(pathInJar); @@ -476,7 +491,7 @@ public static void loadNativeLibrary(String name, boolean isRequired) { " was not found in the classpath via ''{1}''.", new Object[]{library.getName(), pathInJar}); } - return; + return null; } // The library has been found and is ready to be extracted. @@ -526,6 +541,8 @@ public static void loadNativeLibrary(String name, boolean isRequired) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded native library {0}.", library.getName()); } + + return targetFile.getAbsolutePath(); } catch (IOException ex) { /*if (ex.getMessage().contains("used by another process")) { return; diff --git a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java index 926e7305af..c9f55f5311 100644 --- a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java +++ b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java @@ -101,6 +101,27 @@ private void flipImage(short[] img, int width, int height, int bpp){ } } + private static void convertBGRtoRGB(byte[] data){ + for (int i = 0; i < data.length; i += 3) { + byte tmp = data[i]; + data[i] = data[i + 2]; + data[i + 2] = tmp; + } + } + + private static void convertABGRtoRGBA(byte[] data){ + for (int i = 0; i < data.length; i += 4) { + byte a = data[i]; + byte b = data[i + 1]; + byte g = data[i + 2]; + byte r = data[i + 3]; + data[i] = r; + data[i + 1] = g; + data[i + 2] = b; + data[i + 3] = a; + } + } + public Image load(BufferedImage img, boolean flipY){ int width = img.getWidth(); int height = img.getHeight(); @@ -110,18 +131,22 @@ public Image load(BufferedImage img, boolean flipY){ byte[] dataBuf1 = (byte[]) extractImageData(img); if (flipY) flipImage(dataBuf1, width, height, 32); + + convertABGRtoRGBA(dataBuf1); ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); data1.put(dataBuf1); - return new Image(Format.ABGR8, width, height, data1, null, com.jme3.texture.image.ColorSpace.sRGB); + return new Image(Format.RGBA8, width, height, data1, null, com.jme3.texture.image.ColorSpace.sRGB); case BufferedImage.TYPE_3BYTE_BGR: // most common in JPEG images byte[] dataBuf2 = (byte[]) extractImageData(img); if (flipY) flipImage(dataBuf2, width, height, 24); + + convertBGRtoRGB(dataBuf2); ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3); data2.put(dataBuf2); - return new Image(Format.BGR8, width, height, data2, null, com.jme3.texture.image.ColorSpace.sRGB); + return new Image(Format.RGB8, width, height, data2, null, com.jme3.texture.image.ColorSpace.sRGB); case BufferedImage.TYPE_BYTE_GRAY: // grayscale fonts byte[] dataBuf3 = (byte[]) extractImageData(img); if (flipY) diff --git a/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java b/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java index 82f03a410d..507498e2dc 100644 --- a/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java +++ b/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java @@ -183,6 +183,16 @@ public DecodeParams(int bpp, int rm, int rs, int im, int is){ private ImageToAwt() { } + private static Format toGLESFormat(Format format){ + if (format == Format.BGR8) { + return Format.RGB8; + } + if (format == Format.BGRA8 || format == Format.ABGR8 || format == Format.ARGB8) { + return Format.RGBA8; + } + return format; + } + private static int Ix(int x, int y, int w){ return y * w + x; } @@ -214,6 +224,8 @@ private static void writePixel(ByteBuffer buf, int idx, int pixel, int bpp){ * @param buf the output buffer (not null, modified) */ public static void convert(BufferedImage image, Format format, ByteBuffer buf) { + format = toGLESFormat(format); + DecodeParams p = params.get(format); if (p == null) throw new UnsupportedOperationException("Image format " + format + " is not supported"); diff --git a/jme3-examples/build.gradle b/jme3-examples/build.gradle index 4f5d41b501..03745333f1 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -19,8 +19,8 @@ dependencies { implementation project(':jme3-effects') implementation project(':jme3-jbullet') implementation project(':jme3-jogg') - implementation project(':jme3-lwjgl') -// implementation project(':jme3-lwjgl3') + // implementation project(':jme3-lwjgl') + implementation project(':jme3-lwjgl3') implementation project(':jme3-networking') implementation project(':jme3-niftygui') implementation project(':jme3-plugins') diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java index 4070c2a401..54f2a025f5 100644 --- a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java +++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRSimple.java @@ -39,6 +39,7 @@ import com.jme3.math.FastMath; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; +import com.jme3.system.AppSettings; import com.jme3.util.SkyFactory; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; @@ -49,7 +50,13 @@ public class TestPBRSimple extends SimpleApplication { private boolean REALTIME_BAKING = false; public static void main(String[] args) { - new TestPBRSimple().start(); + AppSettings settings = new AppSettings(true); + settings.setX11PlatformPreferred(true); + settings.setRenderer(AppSettings.ANGLE_GLES3); + settings.setGammaCorrection(true); + TestPBRSimple app = new TestPBRSimple(); + app.setSettings(settings); + app.start(); } @Override diff --git a/jme3-examples/src/main/java/jme3test/material/TestUnshadedModel.java b/jme3-examples/src/main/java/jme3test/material/TestUnshadedModel.java index 659a447a48..47c036a81e 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestUnshadedModel.java +++ b/jme3-examples/src/main/java/jme3test/material/TestUnshadedModel.java @@ -8,12 +8,17 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Sphere; +import com.jme3.system.AppSettings; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; public class TestUnshadedModel extends SimpleApplication { public static void main(String[] args){ + AppSettings settings = new AppSettings(true); + settings.setX11PlatformPreferred(true); + settings.setRenderer(AppSettings.ANGLE_GLES3); TestUnshadedModel app = new TestUnshadedModel(); + app.setSettings(settings); app.start(); } diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java index b65224d6eb..b2628c77f5 100644 --- a/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java +++ b/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java @@ -809,5 +809,9 @@ public void glGenVertexArrays(IntBuffer arrays) { throw new UnsupportedOperationException("Unimplemented method 'glGenVertexArrays'"); } + @Override + public String glGetString(int name, int index) { + return JmeIosGLES.glGetStringi(name, index); + } } diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/JmeIosGLES.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/JmeIosGLES.java index a0a19d0a08..0b4aec6084 100644 --- a/jme3-ios/src/main/java/com/jme3/renderer/ios/JmeIosGLES.java +++ b/jme3-ios/src/main/java/com/jme3/renderer/ios/JmeIosGLES.java @@ -201,6 +201,8 @@ private JmeIosGLES() { public static native String glGetShaderInfoLog(int shader); public static native void glGetShaderiv(int shader, int pname, int[] params, int offset); public static native String glGetString(int name); + + public static native String glGetStringi(int name, int index); public static native int glGetUniformLocation(int program, String name); public static native boolean glIsEnabled(int cap); public static native boolean glIsFramebuffer(int framebuffer); diff --git a/jme3-lwjgl3/build.gradle b/jme3-lwjgl3/build.gradle index 5e929e8c2f..9e19451c22 100644 --- a/jme3-lwjgl3/build.gradle +++ b/jme3-lwjgl3/build.gradle @@ -13,6 +13,10 @@ dependencies { api libs.lwjgl3.opencl api libs.lwjgl3.opengl + api libs.lwjgl3.opengles + api("io.github.riccardobl:angle-natives:2026-01-24-2") + + runtimeOnly("io.github.riccardobl:angle-natives:2026-01-24-2") runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-windows') }) runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-windows-x86') }) runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-linux') }) @@ -52,6 +56,15 @@ dependencies { runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-linux-arm64') }) runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-macos') }) runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-macos-arm64') }) + + + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-windows') }) + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-windows-x86') }) + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-linux') }) + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-linux-arm32') }) + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-linux-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-macos') }) + runtimeOnly(variantOf(libs.lwjgl3.opengles){ classifier('natives-macos-arm64') }) } javadoc { diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLES.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLES.java new file mode 100644 index 0000000000..95e604d6dd --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLES.java @@ -0,0 +1,805 @@ + +package com.jme3.renderer.lwjgl; + +import com.jme3.renderer.RendererException; +import com.jme3.renderer.opengl.GL; +import com.jme3.renderer.opengl.GL2; +import com.jme3.renderer.opengl.GLES_30; +import com.jme3.renderer.opengl.GLExt; +import com.jme3.renderer.opengl.GLFbo; +import com.jme3.util.BufferUtils; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import org.lwjgl.opengles.GLES20; +import org.lwjgl.opengles.GLES30; +import org.lwjgl.opengles.GLES31; + + +public class LwjglGLES extends LwjglRender implements GL, GL2, GLES_30, GLExt, GLFbo { + + private final IntBuffer tmpBuff = BufferUtils.createIntBuffer(1); + private final IntBuffer tmpBuff16 = BufferUtils.createIntBuffer(16); + + @Override + public void resetStats() { + } + + + public static void checkLimit(Buffer buffer) { + if (buffer == null) return; + if (buffer.limit() == 0) { + throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error"); + } + if (buffer.remaining() == 0) { + throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error"); + } + } + + private static int getLimitBytes(ByteBuffer buffer) { + checkLimit(buffer); + return buffer.remaining(); + } + + private static int getLimitBytes(ShortBuffer buffer) { + checkLimit(buffer); + return buffer.remaining() * 2; + } + + private static int getLimitBytes(IntBuffer buffer) { + checkLimit(buffer); + return buffer.remaining() * 4; + } + + private static int getLimitBytes(FloatBuffer buffer) { + checkLimit(buffer); + return buffer.remaining() * 4; + } + + private static int getLimitCount(Buffer buffer, int elementSize) { + checkLimit(buffer); + return buffer.remaining() / elementSize; + } + + + @Override + public void glActiveTexture(int texture) { + GLES20.glActiveTexture(texture); + } + + @Override + public void glAttachShader(int program, int shader) { + GLES20.glAttachShader(program, shader); + } + + @Override + public void glBindBuffer(int target, int buffer) { + GLES20.glBindBuffer(target, buffer); + } + + @Override + public void glBindTexture(int target, int texture) { + GLES20.glBindTexture(target, texture); + } + + @Override + public void glBlendFunc(int sfactor, int dfactor) { + GLES20.glBlendFunc(sfactor, dfactor); + } + + @Override + public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) { + GLES20.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); + } + + @Override + public void glBufferData(int target, FloatBuffer data, int usage) { + checkLimit(data); + GLES20.glBufferData(target, data, usage); + } + + @Override + public void glBufferData(int target, ShortBuffer data, int usage) { + checkLimit(data); + GLES20.glBufferData(target, data, usage); + } + + @Override + public void glBufferData(int target, ByteBuffer data, int usage) { + checkLimit(data); + GLES20.glBufferData(target, data, usage); + } + + @Override + public void glBufferData(int target, IntBuffer data, int usage) { + checkLimit(data); + GLES20.glBufferData(target, data, usage); + } + + @Override + public void glBufferData(int target, long dataSize, int usage) { + GLES20.glBufferData(target, dataSize, usage); + } + + @Override + public void glBufferSubData(int target, long offset, FloatBuffer data) { + checkLimit(data); + GLES20.glBufferSubData(target, offset, data); + } + + @Override + public void glBufferSubData(int target, long offset, ShortBuffer data) { + checkLimit(data); + GLES20.glBufferSubData(target, offset, data); + } + + @Override + public void glBufferSubData(int target, long offset, ByteBuffer data) { + checkLimit(data); + GLES20.glBufferSubData(target, offset, data); + } + + @Override + public void glBufferSubData(int target, long offset, IntBuffer data) { + checkLimit(data); + GLES20.glBufferSubData(target, offset, data); + } + + @Override + public void glGetBufferSubData(int target, long offset, ByteBuffer data) { + throw new UnsupportedOperationException("OpenGL ES does not support glGetBufferSubData"); + } + + @Override + public void glClear(int mask) { + GLES20.glClear(mask); + } + + @Override + public void glClearColor(float red, float green, float blue, float alpha) { + GLES20.glClearColor(red, green, blue, alpha); + } + + @Override + public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) { + GLES20.glColorMask(red, green, blue, alpha); + } + + @Override + public void glCompileShader(int shader) { + GLES20.glCompileShader(shader); + } + + @Override + public void glCompressedTexImage2D(int target, int level, int internalformat, int width, int height, + int border, ByteBuffer data) { + checkLimit(data); + GLES20.glCompressedTexImage2D(target, level, internalformat, width, height, border, data); + } + + @Override + public void glCompressedTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, + int height, int format, ByteBuffer data) { + checkLimit(data); + GLES20.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, data); + } + + @Override + public int glCreateProgram() { + return GLES20.glCreateProgram(); + } + + @Override + public int glCreateShader(int shaderType) { + return GLES20.glCreateShader(shaderType); + } + + @Override + public void glCullFace(int mode) { + GLES20.glCullFace(mode); + } + + @Override + public void glDeleteBuffers(IntBuffer buffers) { + checkLimit(buffers); + GLES20.glDeleteBuffers(buffers); + } + + @Override + public void glDeleteProgram(int program) { + GLES20.glDeleteProgram(program); + } + + @Override + public void glDeleteShader(int shader) { + GLES20.glDeleteShader(shader); + } + + @Override + public void glDeleteTextures(IntBuffer textures) { + checkLimit(textures); + GLES20.glDeleteTextures(textures); + } + + @Override + public void glDepthFunc(int func) { + GLES20.glDepthFunc(func); + } + + @Override + public void glDepthMask(boolean flag) { + GLES20.glDepthMask(flag); + } + + @Override + public void glDepthRange(double nearVal, double farVal) { + GLES20.glDepthRangef((float) nearVal, (float) farVal); + } + + @Override + public void glDetachShader(int program, int shader) { + GLES20.glDetachShader(program, shader); + } + + @Override + public void glDisable(int cap) { + GLES20.glDisable(cap); + } + + @Override + public void glDisableVertexAttribArray(int index) { + GLES20.glDisableVertexAttribArray(index); + } + + @Override + public void glDrawArrays(int mode, int first, int count) { + GLES20.glDrawArrays(mode, first, count); + } + + @Override + public void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) { + GLES20.glDrawElements(mode, count, type, indices); + } + + @Override + public void glEnable(int cap) { + GLES20.glEnable(cap); + } + + @Override + public void glEnableVertexAttribArray(int index) { + GLES20.glEnableVertexAttribArray(index); + } + + @Override + public void glGenBuffers(IntBuffer buffers) { + checkLimit(buffers); + GLES20.glGenBuffers(buffers); + } + + @Override + public void glGenTextures(IntBuffer textures) { + checkLimit(textures); + GLES20.glGenTextures(textures); + } + + @Override + public int glGetAttribLocation(int program, String name) { + return GLES20.glGetAttribLocation(program, name); + } + + @Override + public void glGetBoolean(int pname, ByteBuffer params) { + checkLimit(params); + GLES20.glGetBooleanv(pname, params); + } + + @Override + public int glGetError() { + return GLES20.glGetError(); + } + + @Override + public void glGetFloat(int parameterId, FloatBuffer storeValues) { + checkLimit(storeValues); + GLES20.glGetFloatv(parameterId, storeValues); + } + + @Override + public void glGetInteger(int pname, IntBuffer params) { + checkLimit(params); + GLES20.glGetIntegerv(pname, params); + } + + @Override + public void glGetProgram(int program, int pname, IntBuffer params) { + checkLimit(params); + GLES20.glGetProgramiv(program, pname, params); + } + + @Override + public String glGetProgramInfoLog(int program, int maxLength) { + String s = GLES20.glGetProgramInfoLog(program); + if (s == null) return ""; + return s.length() > maxLength ? s.substring(0, maxLength) : s; + } + + @Override + public void glGetShader(int shader, int pname, IntBuffer params) { + checkLimit(params); + GLES20.glGetShaderiv(shader, pname, params); + } + + @Override + public String glGetShaderInfoLog(int shader, int maxLength) { + String s = GLES20.glGetShaderInfoLog(shader); + if (s == null) return ""; + return s.length() > maxLength ? s.substring(0, maxLength) : s; + } + + @Override + public String glGetString(int name) { + return GLES20.glGetString(name); + } + + @Override + public int glGetUniformLocation(int program, String name) { + return GLES20.glGetUniformLocation(program, name); + } + + @Override + public boolean glIsEnabled(int cap) { + return GLES20.glIsEnabled(cap); + } + + @Override + public void glLineWidth(float width) { + GLES20.glLineWidth(width); + } + + @Override + public void glLinkProgram(int program) { + GLES20.glLinkProgram(program); + } + + @Override + public void glPixelStorei(int pname, int param) { + GLES20.glPixelStorei(pname, param); + } + + @Override + public void glPolygonOffset(float factor, float units) { + GLES20.glPolygonOffset(factor, units); + } + + @Override + public void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) { + checkLimit(data); + GLES20.glReadPixels(x, y, width, height, format, type, data); + } + + @Override + public void glScissor(int x, int y, int width, int height) { + GLES20.glScissor(x, y, width, height); + } + + @Override + public void glShaderSource(int shader, String[] string, IntBuffer length) { + if (string == null || string.length != 1) { + throw new UnsupportedOperationException("Expected exactly one shader source string"); + } + GLES20.glShaderSource(shader, string[0]); + } + + @Override + public void glStencilFuncSeparate(int face, int func, int ref, int mask) { + GLES20.glStencilFuncSeparate(face, func, ref, mask); + } + + @Override + public void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) { + GLES20.glStencilOpSeparate(face, sfail, dpfail, dppass); + } + + @Override + public void glTexImage2D(int target, int level, int internalFormat, int width, int height, int border, + int format, int type, ByteBuffer data) { + if (data != null) checkLimit(data); + GLES20.glTexImage2D(target, level, internalFormat, width, height, border, format, type, data); + } + + @Override + public void glTexParameterf(int target, int pname, float param) { + GLES20.glTexParameterf(target, pname, param); + } + + @Override + public void glTexParameteri(int target, int pname, int param) { + GLES20.glTexParameteri(target, pname, param); + } + + @Override + public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, ByteBuffer data) { + checkLimit(data); + GLES20.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data); + } + + @Override + public void glUniform1(final int location, final FloatBuffer value) { + checkLimit(value); + GLES20.glUniform1fv(location, value); + } + + @Override + public void glUniform1(final int location, final IntBuffer value) { + checkLimit(value); + GLES20.glUniform1iv(location, value); + } + + @Override + public void glUniform1f(final int location, final float v0) { + GLES20.glUniform1f(location, v0); + } + + @Override + public void glUniform1i(final int location, final int v0) { + GLES20.glUniform1i(location, v0); + } + + @Override + public void glUniform2(final int location, final IntBuffer value) { + checkLimit(value); + GLES20.glUniform2iv(location, value); + } + + @Override + public void glUniform2(final int location, final FloatBuffer value) { + checkLimit(value); + GLES20.glUniform2fv(location, value); + } + + @Override + public void glUniform2f(final int location, final float v0, final float v1) { + GLES20.glUniform2f(location, v0, v1); + } + + @Override + public void glUniform3(final int location, final IntBuffer value) { + checkLimit(value); + GLES20.glUniform3iv(location, value); + } + + @Override + public void glUniform3(final int location, final FloatBuffer value) { + checkLimit(value); + GLES20.glUniform3fv(location, value); + } + + @Override + public void glUniform3f(final int location, final float v0, final float v1, final float v2) { + GLES20.glUniform3f(location, v0, v1, v2); + } + + @Override + public void glUniform4(final int location, final FloatBuffer value) { + checkLimit(value); + GLES20.glUniform4fv(location, value); + } + + @Override + public void glUniform4(final int location, final IntBuffer value) { + checkLimit(value); + GLES20.glUniform4iv(location, value); + } + + @Override + public void glUniform4f(final int location, final float v0, final float v1, final float v2, final float v3) { + GLES20.glUniform4f(location, v0, v1, v2, v3); + } + + @Override + public void glUniformMatrix3(final int location, final boolean transpose, final FloatBuffer value) { + checkLimit(value); + GLES20.glUniformMatrix3fv(location, transpose, value); + } + + @Override + public void glUniformMatrix4(final int location, final boolean transpose, final FloatBuffer value) { + checkLimit(value); + GLES20.glUniformMatrix4fv(location, transpose, value); + } + + + @Override + public void glUseProgram(int program) { + GLES20.glUseProgram(program); + } + + @Override + public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, + long pointer) { + GLES20.glVertexAttribPointer(index, size, type, normalized, stride, pointer); + } + + @Override + public void glViewport(int x, int y, int width, int height) { + GLES20.glViewport(x, y, width, height); + } + + + @Override + public void glBeginQuery(int target, int query) { + GLES30.glBeginQuery(target, query); + } + + @Override + public void glEndQuery(int target) { + GLES30.glEndQuery(target); + } + + @Override + public void glGenQueries(int num, IntBuffer buff) { + checkLimit(buff); + int oldLimit = buff.limit(); + int pos = buff.position(); + buff.limit(pos + num); + GLES30.glGenQueries(buff); + buff.limit(oldLimit); + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + IntBuffer b = tmpBuff.clear(); + GLES30.glGetQueryObjectuiv(query, pname, b); + return b.get(0); + } + + @Override + public long glGetQueryObjectui64(int query, int pname) { + IntBuffer b = tmpBuff.clear(); + GLES30.glGetQueryObjectuiv(query, pname, b); + return b.get(0) & 0xFFFFFFFFL; + } + + @Override + public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) { + GLES30.glDrawArraysInstanced(mode, first, count, primcount); + } + + @Override + public void glDrawElementsInstancedARB(int mode, int indicesCount, int type, long indicesBufferOffset, + int primcount) { + GLES30.glDrawElementsInstanced(mode, indicesCount, type, indicesBufferOffset, primcount); + } + + @Override + public void glVertexAttribDivisorARB(int index, int divisor) { + GLES30.glVertexAttribDivisor(index, divisor); + } + + @Override + public void glDrawBuffers(IntBuffer bufs) { + checkLimit(bufs); + GLES30.glDrawBuffers(bufs); + } + + @Override + public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, + int dstX1, int dstY1, int mask, int filter) { + GLES30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + + @Override + public void glReadBuffer(int mode) { + GLES30.glReadBuffer(mode); + } + + @Override + public void glReadPixels(int x, int y, int width, int height, int format, int type, long offset) { + GLES30.glReadPixels(x, y, width, height, format, type, offset); + } + + + @Override + public void glBindFramebufferEXT(int target, int framebuffer) { + GLES20.glBindFramebuffer(target, framebuffer); + } + + @Override + public void glBindRenderbufferEXT(int target, int renderbuffer) { + GLES20.glBindRenderbuffer(target, renderbuffer); + } + + @Override + public int glCheckFramebufferStatusEXT(int target) { + return GLES20.glCheckFramebufferStatus(target); + } + + @Override + public void glDeleteFramebuffersEXT(IntBuffer framebuffers) { + checkLimit(framebuffers); + GLES20.glDeleteFramebuffers(framebuffers); + } + + @Override + public void glDeleteRenderbuffersEXT(IntBuffer renderbuffers) { + checkLimit(renderbuffers); + GLES20.glDeleteRenderbuffers(renderbuffers); + } + + @Override + public void glFramebufferRenderbufferEXT(int target, int attachment, int renderbuffertarget, + int renderbuffer) { + GLES20.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); + } + + @Override + public void glFramebufferTexture2DEXT(int target, int attachment, int textarget, int texture, int level) { + GLES20.glFramebufferTexture2D(target, attachment, textarget, texture, level); + } + + @Override + public void glGenFramebuffersEXT(IntBuffer framebuffers) { + checkLimit(framebuffers); + GLES20.glGenFramebuffers(framebuffers); + } + + @Override + public void glGenRenderbuffersEXT(IntBuffer renderbuffers) { + checkLimit(renderbuffers); + GLES20.glGenRenderbuffers(renderbuffers); + } + + @Override + public void glGenerateMipmapEXT(int target) { + GLES20.glGenerateMipmap(target); + } + + @Override + public void glRenderbufferStorageEXT(int target, int internalformat, int width, int height) { + GLES20.glRenderbufferStorage(target, internalformat, width, height); + } + + @Override + public void glFramebufferTextureLayerEXT(int target, int attachment, int texture, int level, int layer) { + GLES30.glFramebufferTextureLayer(target, attachment, texture, level, layer); + } + + @Override + public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, + int height) { + GLES30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height); + } + + + @Override + public void glGetMultisample(int pname, int index, FloatBuffer val) { + checkLimit(val); + GLES31.glGetMultisamplefv(pname, index, val); + } + + @Override + public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, + boolean fixedSampleLocations) { + GLES31.glTexStorage2DMultisample(target, samples, internalformat, width, height, + fixedSampleLocations); + } + + + @Override + public void glCompressedTexImage3D(int target, int level, int internalFormat, int width, int height, + int depth, int border, ByteBuffer data) { + checkLimit(data); + GLES30.glCompressedTexImage3D(target, level, internalFormat, width, height, depth, border, data); + } + + @Override + public void glCompressedTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, + int width, int height, int depth, int format, ByteBuffer data) { + checkLimit(data); + GLES30.glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, + format, data); + } + + @Override + public void glTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, + int border, int format, int type, ByteBuffer data) { + if (data != null) checkLimit(data); + GLES30.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, data); + } + + @Override + public void glTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, + int height, int depth, int format, int type, ByteBuffer data) { + checkLimit(data); + GLES30.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, + data); + } + + + @Override + public void glBindVertexArray(int array) { + GLES30.glBindVertexArray(array); + } + + @Override + public void glDeleteVertexArrays(IntBuffer arrays) { + checkLimit(arrays); + GLES30.glDeleteVertexArrays(arrays); + } + + @Override + public void glGenVertexArrays(IntBuffer arrays) { + checkLimit(arrays); + GLES30.glGenVertexArrays(arrays); + } + + + @Override + public void glAlphaFunc(int func, float ref) { + // Not in GLES + } + + @Override + public void glPointSize(float size) { + // Not core in GLES2 + } + + @Override + public void glPolygonMode(int face, int mode) { + // Not in GLES + } + + @Override + public void glBlendEquationSeparate(int colorMode, int alphaMode) { + GLES20.glBlendEquationSeparate(colorMode, alphaMode); + } + + @Override + public void glDrawBuffer(int mode) { + int nBuffers = (mode - GLFbo.GL_COLOR_ATTACHMENT0_EXT) + 1; + if (nBuffers <= 0 || nBuffers > 16) { + throw new IllegalArgumentException("Draw buffer outside range: " + Integer.toHexString(mode)); + } + tmpBuff16.clear(); + for (int i = 0; i < nBuffers - 1; i++) { + tmpBuff16.put(GL.GL_NONE); + } + tmpBuff16.put(mode); + tmpBuff16.flip(); + glDrawBuffers(tmpBuff16); + } + + + @Override + public int glClientWaitSync(Object sync, int flags, long timeout) { + throw new UnsupportedOperationException("Sync fences not wired in this GLES wrapper yet"); + } + + @Override + public void glDeleteSync(Object sync) { + throw new UnsupportedOperationException("Sync fences not wired in this GLES wrapper yet"); + } + + @Override + public Object glFenceSync(int condition, int flags) { + throw new UnsupportedOperationException("Sync fences not wired in this GLES wrapper yet"); + } + + + @Override + public String glGetString(int name, int index) { + return GLES30.glGetStringi(name, index); + } + + + @Override + public void glGetBufferSubData(int target, long offset, IntBuffer data) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'glGetBufferSubData'"); + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index 0a0a6bcd54..089df9ede0 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -46,6 +46,7 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.RendererException; import com.jme3.renderer.lwjgl.LwjglGL; +import com.jme3.renderer.lwjgl.LwjglGLES; import com.jme3.renderer.lwjgl.LwjglGLExt; import com.jme3.renderer.lwjgl.LwjglGLFboEXT; import com.jme3.renderer.lwjgl.LwjglGLFboGL3; @@ -78,13 +79,15 @@ import static org.lwjgl.opengl.GL.createCapabilities; import static org.lwjgl.opengl.GL11.glGetInteger; import org.lwjgl.opengl.GLCapabilities; +import org.lwjgl.opengles.GLES; +import org.lwjgl.opengles.GLESCapabilities; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.Platform; - /** * A LWJGL implementation of a graphics context. */ public abstract class LwjglContext implements JmeContext { + protected boolean useAngle = false; private static final Logger logger = Logger.getLogger(LwjglContext.class.getName()); @@ -112,7 +115,7 @@ public abstract class LwjglContext implements JmeContext { AppSettings.LWJGL_OPENGL42, AppSettings.LWJGL_OPENGL43, AppSettings.LWJGL_OPENGL44, - AppSettings.LWJGL_OPENGL45 + AppSettings.LWJGL_OPENGL45, AppSettings.ANGLE_GLES3 )); public static final boolean CL_GL_SHARING_POSSIBLE = true; @@ -208,53 +211,86 @@ protected void initContextFirstTime() { private void initContext(boolean first) { final String renderer = settings.getRenderer(); - final GLCapabilities capabilities = createCapabilities(!renderer.equals(AppSettings.LWJGL_OPENGL2)); - - if (!capabilities.OpenGL20) { - throw new RendererException("OpenGL 2.0 or higher is required for jMonkeyEngine"); - } else if (!SUPPORTED_RENDERS.contains(renderer)) { - throw new UnsupportedOperationException("Unsupported renderer: " + renderer); - } if (first) { - GL gl = new LwjglGL(); - GLExt glext = new LwjglGLExt(); + GL gl; + GLExt glext; GLFbo glfbo; + if (!useAngle) { + + final GLCapabilities capabilities = createCapabilities( + !renderer.equals(AppSettings.LWJGL_OPENGL2)); + + if (!capabilities.OpenGL20) { + throw new RendererException("OpenGL 2.0 or higher is required for jMonkeyEngine"); + } else if (!SUPPORTED_RENDERS.contains(renderer)) { + throw new UnsupportedOperationException("Unsupported renderer: " + renderer); + } + + gl = new LwjglGL(); + glext = new LwjglGLExt(); + + if (capabilities.OpenGL30) { + glfbo = new LwjglGLFboGL3(); + } else { + glfbo = new LwjglGLFboEXT(); + } + + if (settings.isGraphicsDebug()) { + gl = (GL) GLDebug.createProxy(gl, gl, GL.class, GL2.class, GL3.class, GL4.class); + glext = (GLExt) GLDebug.createProxy(gl, glext, GLExt.class); + glfbo = (GLFbo) GLDebug.createProxy(gl, glfbo, GLFbo.class); + } + + if (settings.isGraphicsTiming()) { + GLTimingState timingState = new GLTimingState(); + gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, + GL4.class); + glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class); + glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class); + } + + if (settings.isGraphicsTrace()) { + gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class); + glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class); + glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class); + } + + if (capabilities.GL_ARB_debug_output && settings.isGraphicsDebug()) { + ARBDebugOutput.glDebugMessageCallbackARB(new LwjglGLDebugOutputHandler(), 0); + } - if (capabilities.OpenGL30) { - glfbo = new LwjglGLFboGL3(); } else { - glfbo = new LwjglGLFboEXT(); - } + final GLESCapabilities capabilities = GLES.createCapabilities(); + LwjglGLES gles = new LwjglGLES(); - if (settings.isGraphicsDebug()) { - gl = (GL) GLDebug.createProxy(gl, gl, GL.class, GL2.class, GL3.class, GL4.class); - glext = (GLExt) GLDebug.createProxy(gl, glext, GLExt.class); - glfbo = (GLFbo) GLDebug.createProxy(gl, glfbo, GLFbo.class); - } + if (settings.isGraphicsDebug()) { + gles = (LwjglGLES) GLDebug.createProxy(gles, gles, GL.class, GLES_30.class, GLFbo.class, + GLExt.class); - if (settings.isGraphicsTiming()) { - GLTimingState timingState = new GLTimingState(); - gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class); - glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class); - glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class); - } + } - if (settings.isGraphicsTrace()) { - gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class); - glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class); - glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class); - } + if (settings.isGraphicsTiming()) { + GLTimingState timingState = new GLTimingState(); + gles = (LwjglGLES) GLTiming.createGLTiming(gles, timingState, GL.class, GLES_30.class, + GLFbo.class, GLExt.class); + } + if (settings.getBoolean("GraphicsTrace")) { + gles = (LwjglGLES) GLTracer.createGlesTracer(gles, GL.class, GLES_30.class, GLFbo.class, + GLExt.class); + } + + gl = gles; + glext = gles; + glfbo = gles; + + } this.renderer = new GLRenderer(gl, glext, glfbo); if (this.settings.isGraphicsDebug()) ((GLRenderer)this.renderer).setDebugEnabled(true); } this.renderer.initialize(); - if (capabilities.GL_ARB_debug_output && settings.isGraphicsDebug()) { - ARBDebugOutput.glDebugMessageCallbackARB(new LwjglGLDebugOutputHandler(), 0); - } - this.renderer.setMainFrameBufferSrgb(settings.isGammaCorrection()); this.renderer.setLinearizeSrgbImages(settings.isGammaCorrection()); @@ -272,6 +308,7 @@ private void initContext(boolean first) { joyInput.initialize(); } + GLFW.glfwSetJoystickCallback(new GLFWJoystickCallback() { @Override public void invoke(int jid, int event) { @@ -326,6 +363,7 @@ private static long[] getPlatforms() { @SuppressWarnings("unchecked") protected void initOpenCL(final long window) { + if (useAngle) return; logger.info("Initialize OpenCL with LWJGL3"); // try { @@ -541,6 +579,7 @@ public boolean isRenderable() { @Override public void setSettings(AppSettings settings) { this.settings.copyFrom(settings); + useAngle = settings.getRenderer().equals(AppSettings.ANGLE_GLES3); } @Override diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index 0cc59b07fc..8f6e5b2901 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -48,6 +48,9 @@ import com.jme3.system.JmeContext; import com.jme3.system.JmeSystem; import com.jme3.system.NanoTimer; +import com.jme3.system.NativeLibraryLoader; +import com.jme3.system.NativeLibraries.LibraryInfo; +import com.jme3.system.Platform; import com.jme3.util.BufferUtils; import com.jme3.util.SafeArrayList; import java.awt.Graphics2D; @@ -66,10 +69,11 @@ import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.glfw.GLFWNativeEGL; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWWindowFocusCallback; import org.lwjgl.glfw.GLFWWindowSizeCallback; -import org.lwjgl.system.Platform; +import org.lwjgl.system.Configuration; /** * A wrapper class over the GLFW framework in LWJGL 3. @@ -77,6 +81,41 @@ * @author Daniel Johansson */ public abstract class LwjglWindow extends LwjglContext implements Runnable { + private static final LibraryInfo angleEGL = new LibraryInfo("angleEGL") + .addNativeVariant(Platform.Windows64, "native/angle/windows/x86_64/libEGL.dll", "libEGL.dll") + .addNativeVariant(Platform.Windows_ARM64, "native/angle/windows/arm64/libEGL.dll", "libEGL.dll") + .addNativeVariant(Platform.Linux64, "native/angle/linux/x86_64/libEGL.so", "libEGL.so") + .addNativeVariant(Platform.Linux_ARM64, "native/angle/linux/arm64/libEGL.so", "libEGL.so") + .addNativeVariant(Platform.MacOSX64, "native/angle/osx/x86_64/libEGL.dylib", "libEGL.dylib") + .addNativeVariant(Platform.MacOSX_ARM64, "native/angle/osx/arm64/libEGL.dylib", "libEGL.dylib"); + // private static final LibraryInfo angleEGL_1 = new LibraryInfo("angleEGL_1") + // .addNativeVariant(Platform.Linux64, "native/angle/linux/x86_64/libEGL.so.1", "libEGL.so.1") + // .addNativeVariant(Platform.Linux_ARM64, "native/angle/linux/arm64/libEGL.so.1", "libEGL.so.1"); + private static final LibraryInfo angleGLESv2 = new LibraryInfo("angleGLESv2") + .addNativeVariant(Platform.Windows64, "native/angle/windows/x86_64/libGLESv2.dll", + "libGLESv2.dll") + .addNativeVariant(Platform.Windows_ARM64, "native/angle/windows/arm64/libGLESv2.dll", + "libGLESv2.dll") + .addNativeVariant(Platform.Linux64, "native/angle/linux/x86_64/libGLESv2.so", "libGLESv2.so") + .addNativeVariant(Platform.Linux_ARM64, "native/angle/linux/arm64/libGLESv2.so", "libGLESv2.so") + .addNativeVariant(Platform.MacOSX64, "native/angle/osx/x86_64/libGLESv2.dylib", "libGLESv2.dylib") + .addNativeVariant(Platform.MacOSX_ARM64, "native/angle/osx/arm64/libGLESv2.dylib", + "libGLESv2.dylib"); + // private static final LibraryInfo angleGLESv2_2 = new LibraryInfo("angleGLESv2_2") + // .addNativeVariant(Platform.Linux64, "native/angle/linux/x86_64/libGLESv2.so.2", "libGLESv2.so.2") + // .addNativeVariant(Platform.Linux_ARM64, "native/angle/linux/arm64/libGLESv2.so.2", + // "libGLESv2.so.2"); + private static final LibraryInfo d3dcompiler_47 = new LibraryInfo("d3dcompiler_47") + .addNativeVariant(Platform.Windows64, "native/d3dcompiler/windows/x86_64/d3dcompiler_47.dll") + .addNativeVariant(Platform.Windows_ARM64, "native/d3dcompiler/windows/arm64/d3dcompiler_47.dll"); + + static { + NativeLibraryLoader.registerNativeLibrary(angleEGL); + NativeLibraryLoader.registerNativeLibrary(angleGLESv2); + // NativeLibraryLoader.registerNativeLibrary(angleEGL_1); + // NativeLibraryLoader.registerNativeLibrary(angleGLESv2_2); + NativeLibraryLoader.registerNativeLibrary(d3dcompiler_47); + } private static final Logger LOGGER = Logger.getLogger(LwjglWindow.class.getName()); @@ -89,82 +128,76 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { private static final Map RENDER_CONFIGS = new HashMap<>(); static { - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL30, - () -> { - // Based on GLFW docs for OpenGL version below 3.2, - // GLFW_OPENGL_ANY_PROFILE must be used. - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL31, - () -> { - // Based on GLFW docs for OpenGL version below 3.2, - // GLFW_OPENGL_ANY_PROFILE must be used. - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL32, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL33, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL40, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL41, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL42, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL43, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL44, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); - } - ); - RENDER_CONFIGS.put( - AppSettings.LWJGL_OPENGL45, - () -> { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); - } - ); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL30, () -> { + // Based on GLFW docs for OpenGL version below 3.2, + // GLFW_OPENGL_ANY_PROFILE must be used. + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL31, () -> { + // Based on GLFW docs for OpenGL version below 3.2, + // GLFW_OPENGL_ANY_PROFILE must be used. + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL32, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL33, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL40, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL41, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL42, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL43, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL44, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); + }); + RENDER_CONFIGS.put(AppSettings.LWJGL_OPENGL45, () -> { + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); + }); + RENDER_CONFIGS.put(AppSettings.ANGLE_GLES3, () -> { + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + }); } protected final AtomicBoolean needClose = new AtomicBoolean(false); @@ -263,6 +296,29 @@ public void restart() { * @param settings the settings to apply when creating the context. */ protected void createContext(final AppSettings settings) { + + if (useAngle) { + + String angleEGLPath = NativeLibraryLoader.loadNativeLibrary("angleEGL", true); + String angleGLESv2Path = NativeLibraryLoader.loadNativeLibrary("angleGLESv2", true); + // String angleEGL1Path = NativeLibraryLoader.loadNativeLibrary("angleEGL_1", true); + // String angleGLESv2_2Path = NativeLibraryLoader.loadNativeLibrary("angleGLESv2_2", true); + NativeLibraryLoader.loadNativeLibrary("d3dcompiler_47", false); // windows only + + // force debug build + // angleEGLPath = "/home/riccardobl/Desktop/debugANGLE/native/angle/linux/x86_64/libEGL.so"; + // angleGLESv2Path = "/home/riccardobl/Desktop/debugANGLE/native/angle/linux/x86_64/libGLESv2.so"; + + // if (angleEGL1Path != null) angleEGLPath = angleEGL1Path; + // if (angleGLESv2_2Path != null) angleGLESv2Path = angleGLESv2_2Path; + + GLFWNativeEGL.setEGLPath(angleEGLPath); + GLFWNativeEGL.setGLESPath(angleGLESv2Path); + + Configuration.OPENGLES_LIBRARY_NAME.set(angleGLESv2Path); + Configuration.EGL_LIBRARY_NAME.set(angleEGLPath); + } + glfwSetErrorCallback( errorCallback = new GLFWErrorCallback() { @@ -297,8 +353,6 @@ public void invoke(int error, long description) { final String renderer = settings.getRenderer(); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); RENDER_CONFIGS .computeIfAbsent( @@ -645,7 +699,7 @@ public void create(boolean waitFor) { return; } - if (Platform.get() == Platform.MACOSX) { + if (org.lwjgl.system.Platform.get() == org.lwjgl.system.Platform.MACOSX) { // NOTE: this is required for Mac OS X! mainThread = Thread.currentThread(); mainThread.setName("jME3 Main");