All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lwjgl.opengles.GLES Maven / Gradle / Ivy

Go to download

A royalty-free, cross-platform API for full-function 2D and 3D graphics on embedded systems - including consoles, phones, appliances and vehicles.

There is a newer version: 3.3.4
Show newest version
/*
 * Copyright LWJGL. All rights reserved.
 * License terms: https://www.lwjgl.org/license
 */
package org.lwjgl.opengles;

import org.lwjgl.egl.*;
import org.lwjgl.system.*;

import javax.annotation.*;
import java.nio.*;
import java.util.*;

import static java.lang.Math.*;
import static org.lwjgl.opengles.GLES30.*;
import static org.lwjgl.system.APIUtil.*;
import static org.lwjgl.system.Checks.*;
import static org.lwjgl.system.JNI.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;

/**
 * This class must be used before any OpenGL ES function is called. It has the following responsibilities:
 * 
    *
  • Loads the OpenGL ES native library into the JVM process.
  • *
  • Creates instances of {@link GLESCapabilities} classes. A {@code GLESCapabilities} instance contains flags for functionality that is available in an * OpenGL ES context. Internally, it also contains function pointers that are only valid in that specific OpenGL ES context.
  • *
  • Maintains thread-local state for {@code GLESCapabilities} instances, corresponding to OpenGL ES contexts that are current in those threads.
  • *
* *

Library lifecycle

*

The OpenGL ES library is loaded automatically when this class is initialized. Set the {@link Configuration#OPENGLES_EXPLICIT_INIT} option to override * this behavior. Manual loading/unloading can be achieved with the {@link #create} and {@link #destroy} functions. The name of the library loaded can * be overridden with the {@link Configuration#OPENGLES_LIBRARY_NAME} option. The maximum OpenGL ES version loaded can be set with the * {@link Configuration#OPENGLES_MAXVERSION} option. This can be useful to ensure that no functionality above a specific version is used during development.

* *

GLESCapabilities creation

*

Instances of {@code GLESCapabilities} can be created with the {@link #createCapabilities} method. An OpenGL ES context must be current in the current * thread before it is called. Calling this method is expensive, so the {@code GLESCapabilities} instance should be associated with the OpenGL ES context and * reused as necessary.

* *

Thread-local state

*

Before a function for a given OpenGL ES context can be called, the corresponding {@code GLESCapabilities} instance must be passed to the {@link * #setCapabilities} method. The user is also responsible for clearing the current {@code GLESCapabilities} instance when the context is destroyed or made * current in another thread.

* *

Note that the {@link #createCapabilities} method implicitly calls {@link #setCapabilities} with the newly created instance.

*/ public final class GLES { @Nullable private static final APIVersion MAX_VERSION; @Nullable private static FunctionProvider functionProvider; private static final ThreadLocal capabilitiesTLS = new ThreadLocal<>(); private static ICD icd = new ICDStatic(); static { Library.loadSystem(System::load, System::loadLibrary, GLES.class, Platform.mapLibraryNameBundled("lwjgl_opengles")); MAX_VERSION = apiParseVersion(Configuration.OPENGLES_MAXVERSION); if (!Configuration.OPENGLES_EXPLICIT_INIT.get(false)) { create(); } } private GLES() {} /** Ensures that the lwjgl_opengles shared library has been loaded. */ static void initialize() { // intentionally empty to trigger static initializer } /** Loads the OpenGL ES native library, using the default library name. */ public static void create() { SharedLibrary GLES; switch (Platform.get()) { case LINUX: GLES = Library.loadNative(GLES.class, Configuration.OPENGLES_LIBRARY_NAME, "libGLESv2.so.2"); break; case MACOSX: GLES = Library.loadNative(GLES.class, Configuration.OPENGLES_LIBRARY_NAME, "GLESv2"); break; case WINDOWS: GLES = Library.loadNative(GLES.class, Configuration.OPENGLES_LIBRARY_NAME, "libGLESv2", "GLESv2"); break; default: throw new IllegalStateException(); } create(GLES); } /** * Loads the OpenGL ES native library, using the specified library name. * * @param libName the native library name */ public static void create(String libName) { create(Library.loadNative(GLES.class, libName)); } private static void create(SharedLibrary GLES) { try { FunctionProvider egl = EGL.getFunctionProvider(); if (egl == null) { throw new IllegalStateException("The EGL function provider is not available."); } create((FunctionProvider)new SharedLibrary.Delegate(GLES) { @Override public long getFunctionAddress(ByteBuffer functionName) { long address = egl.getFunctionAddress(functionName); if (address == NULL) { address = library.getFunctionAddress(functionName); if (address == NULL && DEBUG_FUNCTIONS) { apiLog("Failed to locate address for GLES function " + functionName); } } return address; } }); } catch (RuntimeException e) { GLES.free(); throw e; } } /** * Initializes OpenGL ES with the specified {@link FunctionProvider}. This method can be used to implement custom OpenGL ES library loading. * * @param functionProvider the provider of OpenGL ES function addresses */ public static void create(FunctionProvider functionProvider) { if (GLES.functionProvider != null) { throw new IllegalStateException("OpenGL ES has already been created."); } GLES.functionProvider = functionProvider; ThreadLocalUtil.setFunctionMissingAddresses(GLESCapabilities.class, 3); } /** Unloads the OpenGL ES native library. */ public static void destroy() { if (functionProvider == null) { return; } ThreadLocalUtil.setFunctionMissingAddresses(null, 3); if (functionProvider instanceof NativeResource) { ((NativeResource)functionProvider).free(); } functionProvider = null; } /** Returns the {@link FunctionProvider} for the OpenGL ES native library. */ @Nullable public static FunctionProvider getFunctionProvider() { return functionProvider; } /** * Sets the {@link GLESCapabilities} of the OpenGL ES context that is current in the current thread. * *

This {@code GLESCapabilities} instance will be used by any OpenGL ES call in the current thread, until {@code setCapabilities} is called again with * a different value.

*/ public static void setCapabilities(@Nullable GLESCapabilities caps) { capabilitiesTLS.set(caps); ThreadLocalUtil.setEnv(caps == null ? NULL : memAddress(caps.addresses), 3); icd.set(caps); } /** * Returns the {@link GLESCapabilities} of the OpenGL ES context that is current in the current thread. * * @throws IllegalStateException if {@link #setCapabilities} has never been called in the current thread or was last called with a {@code null} value */ public static GLESCapabilities getCapabilities() { return checkCapabilities(capabilitiesTLS.get()); } private static GLESCapabilities checkCapabilities(@Nullable GLESCapabilities caps) { if (caps == null) { throw new IllegalStateException( "No GLESCapabilities instance set for the current thread. Possible solutions:\n" + "\ta) Call GLES.createCapabilities() after making a context current in the current thread.\n" + "\tb) Call GLES.setCapabilities() if a GLESCapabilities instance already exists for the current context." ); } return caps; } /** * Creates a new {@link GLESCapabilities} instance for the OpenGL ES context that is current in the current thread. * *

This method calls {@link #setCapabilities(GLESCapabilities)} with the new instance before returning.

* * @return the {@code GLESCapabilities} instance * * @throws IllegalStateException if no OpenGL ES context is current in the current thread */ public static GLESCapabilities createCapabilities() { FunctionProvider functionProvider = GLES.functionProvider; if (functionProvider == null) { throw new IllegalStateException("OpenGL ES library not been loaded."); } GLESCapabilities caps = null; try { // We don't have a current GLESCapabilities when this method is called // so we have to use the native bindings directly. long GetError = functionProvider.getFunctionAddress("glGetError"); long GetString = functionProvider.getFunctionAddress("glGetString"); long GetIntegerv = functionProvider.getFunctionAddress("glGetIntegerv"); if (GetError == NULL || GetString == NULL || GetIntegerv == NULL) { throw new IllegalStateException( "Core OpenGL ES functions could not be found. Make sure that the OpenGL ES library has been loaded correctly." ); } int errorCode = callI(GetError); if (errorCode != GL_NO_ERROR) { apiLog(String.format("An OpenGL ES context was in an error state before the creation of its capabilities instance. Error: [0x%X]", errorCode)); } int majorVersion; int minorVersion; try (MemoryStack stack = stackPush()) { IntBuffer pi = stack.ints(0); // Try the 3.0+ version query first callPV(GetIntegerv, GL_MAJOR_VERSION, memAddress(pi)); if (callI(GetError) == GL_NO_ERROR && 3 <= (majorVersion = pi.get(0))) { // We're on an 3.0+ context. callPV(GetIntegerv, GL_MINOR_VERSION, memAddress(pi)); minorVersion = pi.get(0); } else { // Fallback to the string query. String versionString = memUTF8Safe(callP(GetString, GL_VERSION)); if (versionString == null || callI(GetError) != GL_NO_ERROR) { throw new IllegalStateException("There is no OpenGL ES context current in the current thread."); } APIVersion version = apiParseVersion(versionString, "OpenGL ES"); majorVersion = version.major; minorVersion = version.minor; } } if (majorVersion < 2) { throw new IllegalStateException("OpenGL ES 2.0 is required."); } int[] GL_VERSIONS = { -1, // OpenGL ES 1.0 not supported 0, // OpenGL ES 2.0 2 // OpenGL ES 3.0 to 3.2 }; Set supportedExtensions = new HashSet<>(128); int maxMajor = min(majorVersion, GL_VERSIONS.length); if (MAX_VERSION != null) { maxMajor = min(MAX_VERSION.major, maxMajor); } for (int M = 2; M <= maxMajor; M++) { int maxMinor = GL_VERSIONS[M - 1]; if (M == majorVersion) { maxMinor = min(minorVersion, maxMinor); } if (MAX_VERSION != null && M == MAX_VERSION.major) { maxMinor = min(MAX_VERSION.minor, maxMinor); } for (int m = 0; m <= maxMinor; m++) { supportedExtensions.add(String.format("GLES%d%d", M, m)); } } if (majorVersion < 3) { // Parse EXTENSIONS string String extensionsString = memASCIISafe(callP(GetString, GL_EXTENSIONS)); if (extensionsString != null) { StringTokenizer tokenizer = new StringTokenizer(extensionsString); while (tokenizer.hasMoreTokens()) { supportedExtensions.add(tokenizer.nextToken()); } } } else { // Use indexed EXTENSIONS int extensionCount; try (MemoryStack stack = stackPush()) { IntBuffer pi = stack.ints(0); callPV(GetIntegerv, GL_NUM_EXTENSIONS, memAddress(pi)); extensionCount = pi.get(0); } long GetStringi = apiGetFunctionAddress(functionProvider, "glGetStringi"); for (int i = 0; i < extensionCount; i++) { supportedExtensions.add(memASCII(callP(GetStringi, GL_EXTENSIONS, i))); } } caps = new GLESCapabilities(functionProvider, supportedExtensions); } finally { setCapabilities(caps); } return caps; } // Only used by array overloads static GLESCapabilities getICD() { return checkCapabilities(icd.get()); } /** Function pointer provider. */ private interface ICD { default void set(@Nullable GLESCapabilities caps) {} @Nullable GLESCapabilities get(); } /** * Write-once {@link ICD}. * *

This is the default implementation that skips the thread-local lookup. When a new GLESCapabilities is set, we compare it to the write-once * capabilities. If different function pointers are found, we fall back to the expensive lookup.

*/ private static class ICDStatic implements ICD { @Nullable private static GLESCapabilities tempCaps; @Override public void set(@Nullable GLESCapabilities caps) { if (tempCaps == null) { tempCaps = caps; } else if (caps != null && caps != tempCaps && ThreadLocalUtil.areCapabilitiesDifferent(tempCaps.addresses, caps.addresses)) { apiLog("[WARNING] Incompatible context detected. Falling back to thread-local lookup for GLES contexts."); icd = GLES::getCapabilities; // fall back to thread/process lookup } } @Override @Nullable public GLESCapabilities get() { return WriteOnce.caps; } private static final class WriteOnce { // This will be initialized the first time get() above is called @Nullable private static final GLESCapabilities caps = ICDStatic.tempCaps; static { if (caps == null) { throw new IllegalStateException("No GLESCapabilities instance has been set"); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy