diff options
Diffstat (limited to 'platform/android/java/lib')
15 files changed, 2626 insertions, 54 deletions
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 120a40a31d..c806de1ded 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -18,14 +18,13 @@ def pathToRootDir = "../../../../"  android {      compileSdkVersion versions.compileSdk      buildToolsVersion versions.buildTools -      ndkVersion versions.ndkVersion      defaultConfig {          minSdkVersion versions.minSdk          targetSdkVersion versions.targetSdk -        manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()] +        manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersionName()]      }      namespace = "org.godotengine.godot" @@ -35,6 +34,18 @@ android {          targetCompatibility versions.javaVersion      } +    buildTypes { +        dev { +            initWith debug +        } +    } + +    flavorDimensions "products" +    productFlavors { +        editor {} +        template {} +    } +      lintOptions {          abortOnError false          disable 'MissingTranslation', 'UnusedResources' @@ -58,24 +69,50 @@ android {              aidl.srcDirs = ['aidl']              assets.srcDirs = ['assets']          } +          debug.jniLibs.srcDirs = ['libs/debug'] +        dev.jniLibs.srcDirs = ['libs/dev']          release.jniLibs.srcDirs = ['libs/release'] + +        // Editor jni library +        editorDebug.jniLibs.srcDirs = ['libs/tools/debug'] +        editorDev.jniLibs.srcDirs = ['libs/tools/dev'] +    } + +    // Disable 'editorRelease'. +    // The editor can't be used with target=release as debugging tools are then not +    // included, and it would crash on errors instead of reporting them. +    variantFilter { variant -> +        if (variant.name == "editorRelease") { +            setIgnore(true) +        }      }      libraryVariants.all { variant -> -        variant.outputs.all { output -> -            output.outputFileName = "godot-lib.${variant.name}.aar" +        def flavorName = variant.getFlavorName() +        if (flavorName == null || flavorName == "") { +            throw new GradleException("Invalid product flavor: $flavorName")          } -        def buildType = variant.buildType.name.capitalize() +        boolean toolsFlag = flavorName == "editor" -        def releaseTarget = buildType.toLowerCase() -        if (releaseTarget == null || releaseTarget == "") { -            throw new GradleException("Invalid build type: " + buildType) +        def buildType = variant.buildType.name +        if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) { +            throw new GradleException("Invalid build type: $buildType")          } -        if (!supportedAbis.contains(defaultAbi)) { -            throw new GradleException("Invalid default abi: " + defaultAbi) +        def sconsTarget = supportedTargetsMap[buildType] +        if (sconsTarget == null || sconsTarget == "") { +            throw new GradleException("Invalid scons target: $sconsTarget") +        } + +        // Update the name of the generated library +        def outputSuffix = "${buildType}.aar" +        if (toolsFlag) { +            outputSuffix = "tools.$outputSuffix" +        } +        variant.outputs.all { output -> +            output.outputFileName = "godot-lib.${outputSuffix}"          }          // Find scons' executable path @@ -88,13 +125,11 @@ android {          for (ext in sconsExts) {              String sconsNameExt = sconsName + ext              logger.lifecycle("Checking $sconsNameExt") -              sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)              if (sconsExecutableFile != null) {                  // We're done!                  break              } -              // Check all the options in path              List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)              if (!allOptions.isEmpty()) { @@ -103,27 +138,32 @@ android {                  break              }          } -          if (sconsExecutableFile == null) {              throw new GradleException("Unable to find executable path for the '$sconsName' command.")          } else {              logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")          } -        // Creating gradle task to generate the native libraries for the default abi. -        def taskName = getSconsTaskName(buildType) -        tasks.create(name: taskName, type: Exec) { -            executable sconsExecutableFile.absolutePath -            args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors() -        } +        for (String selectedAbi : selectedAbis) { +            if (!supportedAbis.contains(selectedAbi)) { +                throw new GradleException("Invalid selected abi: $selectedAbi") +            } -        // Schedule the tasks so the generated libs are present before the aar file is packaged. -        tasks["merge${buildType}JniLibFolders"].dependsOn taskName +            // Creating gradle task to generate the native libraries for the selected abi. +            def taskName = getSconsTaskName(flavorName, buildType, selectedAbi) +            tasks.create(name: taskName, type: Exec) { +                executable sconsExecutableFile.absolutePath +                args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors() +            } + +            // Schedule the tasks so the generated libs are present before the aar file is packaged. +            tasks["merge${flavorName.capitalize()}${buildType.capitalize()}JniLibFolders"].dependsOn taskName +        }      }      // TODO: Enable when issues with AGP 7.1+ are resolved (https://github.com/GodotVR/godot_openxr/issues/187).  //    publishing { -//        singleVariant("release") { +//        singleVariant("templateRelease") {  //            withSourcesJar()  //            withJavadocJar()  //        } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 78848c109a..8a86136daf 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -47,7 +47,6 @@ import android.app.AlertDialog;  import android.app.PendingIntent;  import android.content.ClipData;  import android.content.ClipboardManager; -import android.content.ComponentName;  import android.content.Context;  import android.content.Intent;  import android.content.SharedPreferences; @@ -333,9 +332,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	}  	public void restart() { -		if (godotHost != null) { -			godotHost.onGodotRestartRequested(this); -		} +		runOnUiThread(() -> { +			if (godotHost != null) { +				godotHost.onGodotRestartRequested(this); +			} +		});  	}  	public void alert(final String message, final String title) { @@ -859,9 +860,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	private void forceQuit() {  		// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each  		// native Godot components that is started in Godot#onVideoInit. -		if (godotHost != null) { -			godotHost.onGodotForceQuit(this); -		} +		runOnUiThread(() -> { +			if (godotHost != null) { +				godotHost.onGodotForceQuit(this); +			} +		});  	}  	private boolean obbIsCorrupted(String f, String main_pack_md5) { @@ -1010,6 +1013,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,  				progress.mOverallTotal));  	} +  	public void initInputDevices() {  		mRenderView.initInputDevices();  	} @@ -1018,4 +1022,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	private GodotRenderView getRenderView() { // used by native side to get renderView  		return mRenderView;  	} + +	@Keep +	private void createNewGodotInstance(String[] args) { +		runOnUiThread(() -> { +			if (godotHost != null) { +				godotHost.onNewGodotInstanceRequested(args); +			} +		}); +	}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 61093d54de..08da1b1832 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -29,6 +29,8 @@  /*************************************************************************/  package org.godotengine.godot; +import org.godotengine.godot.gl.GLSurfaceView; +import org.godotengine.godot.gl.GodotRenderer;  import org.godotengine.godot.input.GodotGestureHandler;  import org.godotengine.godot.input.GodotInputHandler;  import org.godotengine.godot.utils.GLUtils; @@ -43,7 +45,6 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;  import android.annotation.SuppressLint;  import android.content.Context;  import android.graphics.PixelFormat; -import android.opengl.GLSurfaceView;  import android.os.Build;  import android.view.GestureDetector;  import android.view.KeyEvent; diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 8e8f993369..2e7b67194f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -60,8 +60,16 @@ public interface GodotHost {  	default void onGodotForceQuit(Godot instance) {}  	/** -	 * Invoked on the GL thread when the Godot instance wants to be restarted. It's up to the host +	 * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host  	 * to perform the appropriate action(s).  	 */  	default void onGodotRestartRequested(Godot instance) {} + +	/** +	 * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to +	 * perform the appropriate action(s). +	 * +	 * @param args Arguments used to initialize the new instance. +	 */ +	default void onNewGodotInstanceRequested(String[] args) {}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index b151e7eec1..e8e292df5d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -222,10 +222,14 @@ public class GodotIO {  	}  	public int getScreenDPI() { -		DisplayMetrics metrics = activity.getApplicationContext().getResources().getDisplayMetrics(); +		DisplayMetrics metrics = activity.getResources().getDisplayMetrics();  		return (int)(metrics.density * 160f);  	} +	public float getScaledDensity() { +		return activity.getResources().getDisplayMetrics().scaledDensity; +	} +  	public double getScreenRefreshRate(double fallback) {  		Display display = activity.getWindowManager().getDefaultDisplay();  		if (display != null) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 29e4b4b29e..253a51b83c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -30,6 +30,8 @@  package org.godotengine.godot; +import org.godotengine.godot.gl.GodotRenderer; +  import android.app.Activity;  import android.hardware.SensorEvent;  import android.view.Surface; @@ -68,7 +70,7 @@ public class GodotLib {  	 * @param p_surface  	 * @param p_width  	 * @param p_height -	 * @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int) +	 * @see org.godotengine.godot.gl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)  	 */  	public static native void resize(Surface p_surface, int p_width, int p_height); @@ -85,9 +87,9 @@ public class GodotLib {  	/**  	 * Invoked on the GL thread to draw the current frame. -	 * @see android.opengl.GLSurfaceView.Renderer#onDrawFrame(GL10) +	 * @see org.godotengine.godot.gl.GLSurfaceView.Renderer#onDrawFrame(GL10)  	 */ -	public static native void step(); +	public static native boolean step();  	/**  	 * Forward touch events from the main thread to the GL thread. diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java new file mode 100644 index 0000000000..af16cfce74 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java @@ -0,0 +1,566 @@ +// clang-format off + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.godotengine.godot.gl; + +import android.opengl.GLDebugHelper; +import android.opengl.GLException; + +import java.io.IOException; +import java.io.Writer; + +import javax.microedition.khronos.egl.EGL; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; + +class EGLLogWrapper implements EGL11 { +	private EGL10 mEgl10; +	Writer mLog; +	boolean mLogArgumentNames; +	boolean mCheckError; +	private int mArgCount; + + +	public EGLLogWrapper(EGL egl, int configFlags, Writer log) { +		mEgl10 = (EGL10) egl; +		mLog = log; +		mLogArgumentNames = +				(GLDebugHelper.CONFIG_LOG_ARGUMENT_NAMES & configFlags) != 0; +		mCheckError = +				(GLDebugHelper.CONFIG_CHECK_GL_ERROR & configFlags) != 0; +	} + +	public boolean eglChooseConfig(EGLDisplay display, int[] attrib_list, +								   EGLConfig[] configs, int config_size, int[] num_config) { +		begin("eglChooseConfig"); +		arg("display", display); +		arg("attrib_list", attrib_list); +		arg("config_size", config_size); +		end(); + +		boolean result = mEgl10.eglChooseConfig(display, attrib_list, configs, +				config_size, num_config); +		arg("configs", configs); +		arg("num_config", num_config); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglCopyBuffers(EGLDisplay display, EGLSurface surface, +								  Object native_pixmap) { +		begin("eglCopyBuffers"); +		arg("display", display); +		arg("surface", surface); +		arg("native_pixmap", native_pixmap); +		end(); + +		boolean result = mEgl10.eglCopyBuffers(display, surface, native_pixmap); +		returns(result); +		checkError(); +		return result; +	} + +	public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, +									   EGLContext share_context, int[] attrib_list) { +		begin("eglCreateContext"); +		arg("display", display); +		arg("config", config); +		arg("share_context", share_context); +		arg("attrib_list", attrib_list); +		end(); + +		EGLContext result = mEgl10.eglCreateContext(display, config, +				share_context, attrib_list); +		returns(result); +		checkError(); +		return result; +	} + +	public EGLSurface eglCreatePbufferSurface(EGLDisplay display, +											  EGLConfig config, int[] attrib_list) { +		begin("eglCreatePbufferSurface"); +		arg("display", display); +		arg("config", config); +		arg("attrib_list", attrib_list); +		end(); + +		EGLSurface result = mEgl10.eglCreatePbufferSurface(display, config, +				attrib_list); +		returns(result); +		checkError(); +		return result; +	} + +	public EGLSurface eglCreatePixmapSurface(EGLDisplay display, +											 EGLConfig config, Object native_pixmap, int[] attrib_list) { +		begin("eglCreatePixmapSurface"); +		arg("display", display); +		arg("config", config); +		arg("native_pixmap", native_pixmap); +		arg("attrib_list", attrib_list); +		end(); + +		EGLSurface result = mEgl10.eglCreatePixmapSurface(display, config, +				native_pixmap, attrib_list); +		returns(result); +		checkError(); +		return result; +	} + +	public EGLSurface eglCreateWindowSurface(EGLDisplay display, +											 EGLConfig config, Object native_window, int[] attrib_list) { +		begin("eglCreateWindowSurface"); +		arg("display", display); +		arg("config", config); +		arg("native_window", native_window); +		arg("attrib_list", attrib_list); +		end(); + +		EGLSurface result = mEgl10.eglCreateWindowSurface(display, config, +				native_window, attrib_list); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglDestroyContext(EGLDisplay display, EGLContext context) { +		begin("eglDestroyContext"); +		arg("display", display); +		arg("context", context); +		end(); + +		boolean result = mEgl10.eglDestroyContext(display, context); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglDestroySurface(EGLDisplay display, EGLSurface surface) { +		begin("eglDestroySurface"); +		arg("display", display); +		arg("surface", surface); +		end(); + +		boolean result = mEgl10.eglDestroySurface(display, surface); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, +									  int attribute, int[] value) { +		begin("eglGetConfigAttrib"); +		arg("display", display); +		arg("config", config); +		arg("attribute", attribute); +		end(); +		boolean result = mEgl10.eglGetConfigAttrib(display, config, attribute, +				value); +		arg("value", value); +		returns(result); +		checkError(); +		return false; +	} + +	public boolean eglGetConfigs(EGLDisplay display, EGLConfig[] configs, +								 int config_size, int[] num_config) { +		begin("eglGetConfigs"); +		arg("display", display); +		arg("config_size", config_size); +		end(); + +		boolean result = mEgl10.eglGetConfigs(display, configs, config_size, +				num_config); +		arg("configs", configs); +		arg("num_config", num_config); +		returns(result); +		checkError(); +		return result; +	} + +	public EGLContext eglGetCurrentContext() { +		begin("eglGetCurrentContext"); +		end(); + +		EGLContext result = mEgl10.eglGetCurrentContext(); +		returns(result); + +		checkError(); +		return result; +	} + +	public EGLDisplay eglGetCurrentDisplay() { +		begin("eglGetCurrentDisplay"); +		end(); + +		EGLDisplay result = mEgl10.eglGetCurrentDisplay(); +		returns(result); + +		checkError(); +		return result; +	} + +	public EGLSurface eglGetCurrentSurface(int readdraw) { +		begin("eglGetCurrentSurface"); +		arg("readdraw", readdraw); +		end(); + +		EGLSurface result = mEgl10.eglGetCurrentSurface(readdraw); +		returns(result); + +		checkError(); +		return result; +	} + +	public EGLDisplay eglGetDisplay(Object native_display) { +		begin("eglGetDisplay"); +		arg("native_display", native_display); +		end(); + +		EGLDisplay result = mEgl10.eglGetDisplay(native_display); +		returns(result); + +		checkError(); +		return result; +	} + +	public int eglGetError() { +		begin("eglGetError"); +		end(); + +		int result = mEgl10.eglGetError(); +		returns(getErrorString(result)); + +		return result; +	} + +	public boolean eglInitialize(EGLDisplay display, int[] major_minor) { +		begin("eglInitialize"); +		arg("display", display); +		end(); +		boolean result = mEgl10.eglInitialize(display, major_minor); +		returns(result); +		arg("major_minor", major_minor); +		checkError(); +		return result; +	} + +	public boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, +								  EGLSurface read, EGLContext context) { +		begin("eglMakeCurrent"); +		arg("display", display); +		arg("draw", draw); +		arg("read", read); +		arg("context", context); +		end(); +		boolean result = mEgl10.eglMakeCurrent(display, draw, read, context); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglQueryContext(EGLDisplay display, EGLContext context, +								   int attribute, int[] value) { +		begin("eglQueryContext"); +		arg("display", display); +		arg("context", context); +		arg("attribute", attribute); +		end(); +		boolean result = mEgl10.eglQueryContext(display, context, attribute, +				value); +		returns(value[0]); +		returns(result); +		checkError(); +		return result; +	} + +	public String eglQueryString(EGLDisplay display, int name) { +		begin("eglQueryString"); +		arg("display", display); +		arg("name", name); +		end(); +		String result = mEgl10.eglQueryString(display, name); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglQuerySurface(EGLDisplay display, EGLSurface surface, +								   int attribute, int[] value) { +		begin("eglQuerySurface"); +		arg("display", display); +		arg("surface", surface); +		arg("attribute", attribute); +		end(); +		boolean result = mEgl10.eglQuerySurface(display, surface, attribute, +				value); +		returns(value[0]); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) { +		begin("eglSwapBuffers"); +		arg("display", display); +		arg("surface", surface); +		end(); +		boolean result = mEgl10.eglSwapBuffers(display, surface); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglTerminate(EGLDisplay display) { +		begin("eglTerminate"); +		arg("display", display); +		end(); +		boolean result = mEgl10.eglTerminate(display); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglWaitGL() { +		begin("eglWaitGL"); +		end(); +		boolean result = mEgl10.eglWaitGL(); +		returns(result); +		checkError(); +		return result; +	} + +	public boolean eglWaitNative(int engine, Object bindTarget) { +		begin("eglWaitNative"); +		arg("engine", engine); +		arg("bindTarget", bindTarget); +		end(); +		boolean result = mEgl10.eglWaitNative(engine, bindTarget); +		returns(result); +		checkError(); +		return result; +	} + +	private void checkError() { +		int eglError; +		if ((eglError = mEgl10.eglGetError()) != EGL_SUCCESS) { +			String errorMessage = "eglError: " + getErrorString(eglError); +			logLine(errorMessage); +			if (mCheckError) { +				throw new GLException(eglError, errorMessage); +			} +		} +	} + +	private void logLine(String message) { +		log(message + '\n'); +	} + +	private void log(String message) { +		try { +			mLog.write(message); +		} catch (IOException e) { +			// Ignore exception, keep on trying +		} +	} + +	private void begin(String name) { +		log(name + '('); +		mArgCount = 0; +	} + +	private void arg(String name, String value) { +		if (mArgCount++ > 0) { +			log(", "); +		} +		if (mLogArgumentNames) { +			log(name + "="); +		} +		log(value); +	} + +	private void end() { +		log(");\n"); +		flush(); +	} + +	private void flush() { +		try { +			mLog.flush(); +		} catch (IOException e) { +			mLog = null; +		} +	} + +	private void arg(String name, int value) { +		arg(name, Integer.toString(value)); +	} + +	private void arg(String name, Object object) { +		arg(name, toString(object)); +	} + +	private void arg(String name, EGLDisplay object) { +		if (object == EGL10.EGL_DEFAULT_DISPLAY) { +			arg(name, "EGL10.EGL_DEFAULT_DISPLAY"); +		} else if (object == EGL_NO_DISPLAY) { +			arg(name, "EGL10.EGL_NO_DISPLAY"); +		} else { +			arg(name, toString(object)); +		} +	} + +	private void arg(String name, EGLContext object) { +		if (object == EGL10.EGL_NO_CONTEXT) { +			arg(name, "EGL10.EGL_NO_CONTEXT"); +		} else { +			arg(name, toString(object)); +		} +	} + +	private void arg(String name, EGLSurface object) { +		if (object == EGL10.EGL_NO_SURFACE) { +			arg(name, "EGL10.EGL_NO_SURFACE"); +		} else { +			arg(name, toString(object)); +		} +	} + +	private void returns(String result) { +		log(" returns " + result + ";\n"); +		flush(); +	} + +	private void returns(int result) { +		returns(Integer.toString(result)); +	} + +	private void returns(boolean result) { +		returns(Boolean.toString(result)); +	} + +	private void returns(Object result) { +		returns(toString(result)); +	} + +	private String toString(Object obj) { +		if (obj == null) { +			return "null"; +		} else { +			return obj.toString(); +		} +	} + +	private void arg(String name, int[] arr) { +		if (arr == null) { +			arg(name, "null"); +		} else { +			arg(name, toString(arr.length, arr, 0)); +		} +	} + +	private void arg(String name, Object[] arr) { +		if (arr == null) { +			arg(name, "null"); +		} else { +			arg(name, toString(arr.length, arr, 0)); +		} +	} + +	private String toString(int n, int[] arr, int offset) { +		StringBuilder buf = new StringBuilder(); +		buf.append("{\n"); +		int arrLen = arr.length; +		for (int i = 0; i < n; i++) { +			int index = offset + i; +			buf.append(" [" + index + "] = "); +			if (index < 0 || index >= arrLen) { +				buf.append("out of bounds"); +			} else { +				buf.append(arr[index]); +			} +			buf.append('\n'); +		} +		buf.append("}"); +		return buf.toString(); +	} + +	private String toString(int n, Object[] arr, int offset) { +		StringBuilder buf = new StringBuilder(); +		buf.append("{\n"); +		int arrLen = arr.length; +		for (int i = 0; i < n; i++) { +			int index = offset + i; +			buf.append(" [" + index + "] = "); +			if (index < 0 || index >= arrLen) { +				buf.append("out of bounds"); +			} else { +				buf.append(arr[index]); +			} +			buf.append('\n'); +		} +		buf.append("}"); +		return buf.toString(); +	} + +	private static String getHex(int value) { +		return "0x" + Integer.toHexString(value); +	} + +	public static String getErrorString(int error) { +		switch (error) { +			case EGL_SUCCESS: +				return "EGL_SUCCESS"; +			case EGL_NOT_INITIALIZED: +				return "EGL_NOT_INITIALIZED"; +			case EGL_BAD_ACCESS: +				return "EGL_BAD_ACCESS"; +			case EGL_BAD_ALLOC: +				return "EGL_BAD_ALLOC"; +			case EGL_BAD_ATTRIBUTE: +				return "EGL_BAD_ATTRIBUTE"; +			case EGL_BAD_CONFIG: +				return "EGL_BAD_CONFIG"; +			case EGL_BAD_CONTEXT: +				return "EGL_BAD_CONTEXT"; +			case EGL_BAD_CURRENT_SURFACE: +				return "EGL_BAD_CURRENT_SURFACE"; +			case EGL_BAD_DISPLAY: +				return "EGL_BAD_DISPLAY"; +			case EGL_BAD_MATCH: +				return "EGL_BAD_MATCH"; +			case EGL_BAD_NATIVE_PIXMAP: +				return "EGL_BAD_NATIVE_PIXMAP"; +			case EGL_BAD_NATIVE_WINDOW: +				return "EGL_BAD_NATIVE_WINDOW"; +			case EGL_BAD_PARAMETER: +				return "EGL_BAD_PARAMETER"; +			case EGL_BAD_SURFACE: +				return "EGL_BAD_SURFACE"; +			case EGL11.EGL_CONTEXT_LOST: +				return "EGL_CONTEXT_LOST"; +			default: +				return getHex(error); +		} +	} +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java new file mode 100644 index 0000000000..8449c08b88 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java @@ -0,0 +1,1939 @@ +// clang-format off + +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.godotengine.godot.gl; + +import android.content.Context; +import android.opengl.EGL14; +import android.opengl.EGLExt; +import android.opengl.GLDebugHelper; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import java.io.Writer; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; +import javax.microedition.khronos.opengles.GL10; + +/** + * An implementation of SurfaceView that uses the dedicated surface for + * displaying OpenGL rendering. + * <p> + * A GLSurfaceView provides the following features: + * <p> + * <ul> + * <li>Manages a surface, which is a special piece of memory that can be + * composited into the Android view system. + * <li>Manages an EGL display, which enables OpenGL to render into a surface. + * <li>Accepts a user-provided Renderer object that does the actual rendering. + * <li>Renders on a dedicated thread to decouple rendering performance from the + * UI thread. + * <li>Supports both on-demand and continuous rendering. + * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls. + * </ul> + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about how to use OpenGL, read the + * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p> + * </div> + * + * <h3>Using GLSurfaceView</h3> + * <p> + * Typically you use GLSurfaceView by subclassing it and overriding one or more of the + * View system input event methods. If your application does not need to override event + * methods then GLSurfaceView can be used as-is. For the most part + * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. + * For example, unlike a regular View, drawing is delegated to a separate Renderer object which + * is registered with the GLSurfaceView + * using the {@link #setRenderer(Renderer)} call. + * <p> + * <h3>Initializing GLSurfaceView</h3> + * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}. + * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or + * more of these methods before calling setRenderer: + * <ul> + * <li>{@link #setDebugFlags(int)} + * <li>{@link #setEGLConfigChooser(boolean)} + * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} + * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} + * <li>{@link #setGLWrapper(GLWrapper)} + * </ul> + * <p> + * <h4>Specifying the android.view.Surface</h4> + * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent + * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). + * The exact format of a TRANSLUCENT surface is device dependent, but it will be + * a 32-bit-per-pixel surface with 8 bits per component. + * <p> + * <h4>Choosing an EGL Configuration</h4> + * A given Android device may support multiple EGLConfig rendering configurations. + * The available configurations may differ in how many channels of data are present, as + * well as how many bits are allocated to each channel. Therefore, the first thing + * GLSurfaceView has to do when starting to render is choose what EGLConfig to use. + * <p> + * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format, + * with at least a 16-bit depth buffer and no stencil. + * <p> + * If you would prefer a different EGLConfig + * you can override the default behavior by calling one of the + * setEGLConfigChooser methods. + * <p> + * <h4>Debug Behavior</h4> + * You can optionally modify the behavior of GLSurfaceView by calling + * one or more of the debugging methods {@link #setDebugFlags(int)}, + * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but + * typically they are called before setRenderer so that they take effect immediately. + * <p> + * <h4>Setting a Renderer</h4> + * Finally, you must call {@link #setRenderer} to register a {@link Renderer}. + * The renderer is + * responsible for doing the actual OpenGL rendering. + * <p> + * <h3>Rendering Mode</h3> + * Once the renderer is set, you can control whether the renderer draws + * continuously or on-demand by calling + * {@link #setRenderMode}. The default is continuous rendering. + * <p> + * <h3>Activity Life-cycle</h3> + * A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients + * are required to call {@link #onPause()} when the activity stops and + * {@link #onResume()} when the activity starts. These calls allow GLSurfaceView to + * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate + * the OpenGL display. + * <p> + * <h3>Handling events</h3> + * <p> + * To handle an event you will typically subclass GLSurfaceView and override the + * appropriate method, just as you would with any other View. However, when handling + * the event, you may need to communicate with the Renderer object + * that's running in the rendering thread. You can do this using any + * standard Java cross-thread communication mechanism. In addition, + * one relatively easy way to communicate with your renderer is + * to call + * {@link #queueEvent(Runnable)}. For example: + * <pre class="prettyprint"> + * class MyGLSurfaceView extends GLSurfaceView { + * + *     private MyRenderer mMyRenderer; + * + *     public void start() { + *         mMyRenderer = ...; + *         setRenderer(mMyRenderer); + *     } + * + *     public boolean onKeyDown(int keyCode, KeyEvent event) { + *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { + *             queueEvent(new Runnable() { + *                 // This method will be called on the rendering + *                 // thread: + *                 public void run() { + *                     mMyRenderer.handleDpadCenter(); + *                 }}); + *             return true; + *         } + *         return super.onKeyDown(keyCode, event); + *     } + * } + * </pre> + * + */ +public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 { +	private final static String TAG = "GLSurfaceView"; +	private final static boolean LOG_ATTACH_DETACH = false; +	private final static boolean LOG_THREADS = false; +	private final static boolean LOG_PAUSE_RESUME = false; +	private final static boolean LOG_SURFACE = false; +	private final static boolean LOG_RENDERER = false; +	private final static boolean LOG_RENDERER_DRAW_FRAME = false; +	private final static boolean LOG_EGL = false; +	/** +	 * The renderer only renders +	 * when the surface is created, or when {@link #requestRender} is called. +	 * +	 * @see #getRenderMode() +	 * @see #setRenderMode(int) +	 * @see #requestRender() +	 */ +	public final static int RENDERMODE_WHEN_DIRTY = 0; +	/** +	 * The renderer is called +	 * continuously to re-render the scene. +	 * +	 * @see #getRenderMode() +	 * @see #setRenderMode(int) +	 */ +	public final static int RENDERMODE_CONTINUOUSLY = 1; + +	/** +	 * Check glError() after every GL call and throw an exception if glError indicates +	 * that an error has occurred. This can be used to help track down which OpenGL ES call +	 * is causing an error. +	 * +	 * @see #getDebugFlags +	 * @see #setDebugFlags +	 */ +	public final static int DEBUG_CHECK_GL_ERROR = 1; + +	/** +	 * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView". +	 * +	 * @see #getDebugFlags +	 * @see #setDebugFlags +	 */ +	public final static int DEBUG_LOG_GL_CALLS = 2; + +	/** +	 * Standard View constructor. In order to render something, you +	 * must call {@link #setRenderer} to register a renderer. +	 */ +	public GLSurfaceView(Context context) { +		super(context); +		init(); +	} + +	/** +	 * Standard View constructor. In order to render something, you +	 * must call {@link #setRenderer} to register a renderer. +	 */ +	public GLSurfaceView(Context context, AttributeSet attrs) { +		super(context, attrs); +		init(); +	} + +	@Override +	protected void finalize() throws Throwable { +		try { +			if (mGLThread != null) { +				// GLThread may still be running if this view was never +				// attached to a window. +				mGLThread.requestExitAndWait(); +			} +		} finally { +			super.finalize(); +		} +	} + +	private void init() { +		// Install a SurfaceHolder.Callback so we get notified when the +		// underlying surface is created and destroyed +		SurfaceHolder holder = getHolder(); +		holder.addCallback(this); +		// setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment +		// this statement if back-porting to 2.2 or older: +		// holder.setFormat(PixelFormat.RGB_565); +		// +		// setType is not needed for SDK 2.0 or newer. Uncomment this +		// statement if back-porting this code to older SDKs. +		// holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); +	} + +	/** +	 * Set the glWrapper. If the glWrapper is not null, its +	 * {@link GLWrapper#wrap(GL)} method is called +	 * whenever a surface is created. A GLWrapper can be used to wrap +	 * the GL object that's passed to the renderer. Wrapping a GL +	 * object enables examining and modifying the behavior of the +	 * GL calls made by the renderer. +	 * <p> +	 * Wrapping is typically used for debugging purposes. +	 * <p> +	 * The default value is null. +	 * @param glWrapper the new GLWrapper +	 */ +	public void setGLWrapper(GLWrapper glWrapper) { +		mGLWrapper = glWrapper; +	} + +	/** +	 * Set the debug flags to a new value. The value is +	 * constructed by OR-together zero or more +	 * of the DEBUG_CHECK_* constants. The debug flags take effect +	 * whenever a surface is created. The default value is zero. +	 * @param debugFlags the new debug flags +	 * @see #DEBUG_CHECK_GL_ERROR +	 * @see #DEBUG_LOG_GL_CALLS +	 */ +	public void setDebugFlags(int debugFlags) { +		mDebugFlags = debugFlags; +	} + +	/** +	 * Get the current value of the debug flags. +	 * @return the current value of the debug flags. +	 */ +	public int getDebugFlags() { +		return mDebugFlags; +	} + +	/** +	 * Control whether the EGL context is preserved when the GLSurfaceView is paused and +	 * resumed. +	 * <p> +	 * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. +	 * <p> +	 * Prior to API level 11, whether the EGL context is actually preserved or not +	 * depends upon whether the Android device can support an arbitrary number of +	 * EGL contexts or not. Devices that can only support a limited number of EGL +	 * contexts must release the EGL context in order to allow multiple applications +	 * to share the GPU. +	 * <p> +	 * If set to false, the EGL context will be released when the GLSurfaceView is paused, +	 * and recreated when the GLSurfaceView is resumed. +	 * <p> +	 * +	 * The default is false. +	 * +	 * @param preserveOnPause preserve the EGL context when paused +	 */ +	public void setPreserveEGLContextOnPause(boolean preserveOnPause) { +		mPreserveEGLContextOnPause = preserveOnPause; +	} + +	/** +	 * @return true if the EGL context will be preserved when paused +	 */ +	public boolean getPreserveEGLContextOnPause() { +		return mPreserveEGLContextOnPause; +	} + +	/** +	 * Set the renderer associated with this view. Also starts the thread that +	 * will call the renderer, which in turn causes the rendering to start. +	 * <p>This method should be called once and only once in the life-cycle of +	 * a GLSurfaceView. +	 * <p>The following GLSurfaceView methods can only be called <em>before</em> +	 * setRenderer is called: +	 * <ul> +	 * <li>{@link #setEGLConfigChooser(boolean)} +	 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)} +	 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)} +	 * </ul> +	 * <p> +	 * The following GLSurfaceView methods can only be called <em>after</em> +	 * setRenderer is called: +	 * <ul> +	 * <li>{@link #getRenderMode()} +	 * <li>{@link #onPause()} +	 * <li>{@link #onResume()} +	 * <li>{@link #queueEvent(Runnable)} +	 * <li>{@link #requestRender()} +	 * <li>{@link #setRenderMode(int)} +	 * </ul> +	 * +	 * @param renderer the renderer to use to perform OpenGL drawing. +	 */ +	public void setRenderer(Renderer renderer) { +		checkRenderThreadState(); +		if (mEGLConfigChooser == null) { +			mEGLConfigChooser = new SimpleEGLConfigChooser(true); +		} +		if (mEGLContextFactory == null) { +			mEGLContextFactory = new DefaultContextFactory(); +		} +		if (mEGLWindowSurfaceFactory == null) { +			mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); +		} +		mRenderer = renderer; +		mGLThread = new GLThread(mThisWeakRef); +		mGLThread.start(); +	} + +	/** +	 * Install a custom EGLContextFactory. +	 * <p>If this method is +	 * called, it must be called before {@link #setRenderer(Renderer)} +	 * is called. +	 * <p> +	 * If this method is not called, then by default +	 * a context will be created with no shared context and +	 * with a null attribute list. +	 */ +	public void setEGLContextFactory(EGLContextFactory factory) { +		checkRenderThreadState(); +		mEGLContextFactory = factory; +	} + +	/** +	 * Install a custom EGLWindowSurfaceFactory. +	 * <p>If this method is +	 * called, it must be called before {@link #setRenderer(Renderer)} +	 * is called. +	 * <p> +	 * If this method is not called, then by default +	 * a window surface will be created with a null attribute list. +	 */ +	public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { +		checkRenderThreadState(); +		mEGLWindowSurfaceFactory = factory; +	} + +	/** +	 * Install a custom EGLConfigChooser. +	 * <p>If this method is +	 * called, it must be called before {@link #setRenderer(Renderer)} +	 * is called. +	 * <p> +	 * If no setEGLConfigChooser method is called, then by default the +	 * view will choose an EGLConfig that is compatible with the current +	 * android.view.Surface, with a depth buffer depth of +	 * at least 16 bits. +	 * @param configChooser +	 */ +	public void setEGLConfigChooser(EGLConfigChooser configChooser) { +		checkRenderThreadState(); +		mEGLConfigChooser = configChooser; +	} + +	/** +	 * Install a config chooser which will choose a config +	 * as close to 16-bit RGB as possible, with or without an optional depth +	 * buffer as close to 16-bits as possible. +	 * <p>If this method is +	 * called, it must be called before {@link #setRenderer(Renderer)} +	 * is called. +	 * <p> +	 * If no setEGLConfigChooser method is called, then by default the +	 * view will choose an RGB_888 surface with a depth buffer depth of +	 * at least 16 bits. +	 * +	 * @param needDepth +	 */ +	public void setEGLConfigChooser(boolean needDepth) { +		setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); +	} + +	/** +	 * Install a config chooser which will choose a config +	 * with at least the specified depthSize and stencilSize, +	 * and exactly the specified redSize, greenSize, blueSize and alphaSize. +	 * <p>If this method is +	 * called, it must be called before {@link #setRenderer(Renderer)} +	 * is called. +	 * <p> +	 * If no setEGLConfigChooser method is called, then by default the +	 * view will choose an RGB_888 surface with a depth buffer depth of +	 * at least 16 bits. +	 * +	 */ +	public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, +									int alphaSize, int depthSize, int stencilSize) { +		setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, +				blueSize, alphaSize, depthSize, stencilSize)); +	} + +	/** +	 * Inform the default EGLContextFactory and default EGLConfigChooser +	 * which EGLContext client version to pick. +	 * <p>Use this method to create an OpenGL ES 2.0-compatible context. +	 * Example: +	 * <pre class="prettyprint"> +	 *     public MyView(Context context) { +	 *         super(context); +	 *         setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context. +	 *         setRenderer(new MyRenderer()); +	 *     } +	 * </pre> +	 * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by +	 * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's +	 * AndroidManifest.xml file. +	 * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)} +	 * is called. +	 * <p>This method only affects the behavior of the default EGLContexFactory and the +	 * default EGLConfigChooser. If +	 * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied +	 * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context. +	 * If +	 * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied +	 * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config. +	 * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0 +	 */ +	public void setEGLContextClientVersion(int version) { +		checkRenderThreadState(); +		mEGLContextClientVersion = version; +	} + +	/** +	 * Set the rendering mode. When renderMode is +	 * RENDERMODE_CONTINUOUSLY, the renderer is called +	 * repeatedly to re-render the scene. When renderMode +	 * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface +	 * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. +	 * <p> +	 * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance +	 * by allowing the GPU and CPU to idle when the view does not need to be updated. +	 * <p> +	 * This method can only be called after {@link #setRenderer(Renderer)} +	 * +	 * @param renderMode one of the RENDERMODE_X constants +	 * @see #RENDERMODE_CONTINUOUSLY +	 * @see #RENDERMODE_WHEN_DIRTY +	 */ +	public void setRenderMode(int renderMode) { +		mGLThread.setRenderMode(renderMode); +	} + +	/** +	 * Get the current rendering mode. May be called +	 * from any thread. Must not be called before a renderer has been set. +	 * @return the current rendering mode. +	 * @see #RENDERMODE_CONTINUOUSLY +	 * @see #RENDERMODE_WHEN_DIRTY +	 */ +	public int getRenderMode() { +		return mGLThread.getRenderMode(); +	} + +	/** +	 * Request that the renderer render a frame. +	 * This method is typically used when the render mode has been set to +	 * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. +	 * May be called +	 * from any thread. Must not be called before a renderer has been set. +	 */ +	public void requestRender() { +		mGLThread.requestRender(); +	} + +	/** +	 * This method is part of the SurfaceHolder.Callback interface, and is +	 * not normally called or subclassed by clients of GLSurfaceView. +	 */ +	public void surfaceCreated(SurfaceHolder holder) { +		mGLThread.surfaceCreated(); +	} + +	/** +	 * This method is part of the SurfaceHolder.Callback interface, and is +	 * not normally called or subclassed by clients of GLSurfaceView. +	 */ +	public void surfaceDestroyed(SurfaceHolder holder) { +		// Surface will be destroyed when we return +		mGLThread.surfaceDestroyed(); +	} + +	/** +	 * This method is part of the SurfaceHolder.Callback interface, and is +	 * not normally called or subclassed by clients of GLSurfaceView. +	 */ +	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { +		mGLThread.onWindowResize(w, h); +	} + +	/** +	 * This method is part of the SurfaceHolder.Callback2 interface, and is +	 * not normally called or subclassed by clients of GLSurfaceView. +	 */ +	@Override +	public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) { +		if (mGLThread != null) { +			mGLThread.requestRenderAndNotify(finishDrawing); +		} +	} + +	/** +	 * This method is part of the SurfaceHolder.Callback2 interface, and is +	 * not normally called or subclassed by clients of GLSurfaceView. +	 */ +	@Deprecated +	@Override +	public void surfaceRedrawNeeded(SurfaceHolder holder) { +		// Since we are part of the framework we know only surfaceRedrawNeededAsync +		// will be called. +	} + + +	/** +	 * Pause the rendering thread, optionally tearing down the EGL context +	 * depending upon the value of {@link #setPreserveEGLContextOnPause(boolean)}. +	 * +	 * This method should be called when it is no longer desirable for the +	 * GLSurfaceView to continue rendering, such as in response to +	 * {@link android.app.Activity#onStop Activity.onStop}. +	 * +	 * Must not be called before a renderer has been set. +	 */ +	public void onPause() { +		mGLThread.onPause(); +	} + +	/** +	 * Resumes the rendering thread, re-creating the OpenGL context if necessary. It +	 * is the counterpart to {@link #onPause()}. +	 * +	 * This method should typically be called in +	 * {@link android.app.Activity#onStart Activity.onStart}. +	 * +	 * Must not be called before a renderer has been set. +	 */ +	public void onResume() { +		mGLThread.onResume(); +	} + +	/** +	 * Queue a runnable to be run on the GL rendering thread. This can be used +	 * to communicate with the Renderer on the rendering thread. +	 * Must not be called before a renderer has been set. +	 * @param r the runnable to be run on the GL rendering thread. +	 */ +	public void queueEvent(Runnable r) { +		mGLThread.queueEvent(r); +	} + +	/** +	 * This method is used as part of the View class and is not normally +	 * called or subclassed by clients of GLSurfaceView. +	 */ +	@Override +	protected void onAttachedToWindow() { +		super.onAttachedToWindow(); +		if (LOG_ATTACH_DETACH) { +			Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); +		} +		if (mDetached && (mRenderer != null)) { +			int renderMode = RENDERMODE_CONTINUOUSLY; +			if (mGLThread != null) { +				renderMode = mGLThread.getRenderMode(); +			} +			mGLThread = new GLThread(mThisWeakRef); +			if (renderMode != RENDERMODE_CONTINUOUSLY) { +				mGLThread.setRenderMode(renderMode); +			} +			mGLThread.start(); +		} +		mDetached = false; +	} + +	@Override +	protected void onDetachedFromWindow() { +		if (LOG_ATTACH_DETACH) { +			Log.d(TAG, "onDetachedFromWindow"); +		} +		if (mGLThread != null) { +			mGLThread.requestExitAndWait(); +		} +		mDetached = true; +		super.onDetachedFromWindow(); +	} + +	// ---------------------------------------------------------------------- + +	/** +	 * An interface used to wrap a GL interface. +	 * <p>Typically +	 * used for implementing debugging and tracing on top of the default +	 * GL interface. You would typically use this by creating your own class +	 * that implemented all the GL methods by delegating to another GL instance. +	 * Then you could add your own behavior before or after calling the +	 * delegate. All the GLWrapper would do was instantiate and return the +	 * wrapper GL instance: +	 * <pre class="prettyprint"> +	 * class MyGLWrapper implements GLWrapper { +	 *     GL wrap(GL gl) { +	 *         return new MyGLImplementation(gl); +	 *     } +	 *     static class MyGLImplementation implements GL,GL10,GL11,... { +	 *         ... +	 *     } +	 * } +	 * </pre> +	 * @see #setGLWrapper(GLWrapper) +	 */ +	public interface GLWrapper { +		/** +		 * Wraps a gl interface in another gl interface. +		 * @param gl a GL interface that is to be wrapped. +		 * @return either the input argument or another GL object that wraps the input argument. +		 */ +		GL wrap(GL gl); +	} + +	/** +	 * A generic renderer interface. +	 * <p> +	 * The renderer is responsible for making OpenGL calls to render a frame. +	 * <p> +	 * GLSurfaceView clients typically create their own classes that implement +	 * this interface, and then call {@link GLSurfaceView#setRenderer} to +	 * register the renderer with the GLSurfaceView. +	 * <p> +	 * +	 * <div class="special reference"> +	 * <h3>Developer Guides</h3> +	 * <p>For more information about how to use OpenGL, read the +	 * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p> +	 * </div> +	 * +	 * <h3>Threading</h3> +	 * The renderer will be called on a separate thread, so that rendering +	 * performance is decoupled from the UI thread. Clients typically need to +	 * communicate with the renderer from the UI thread, because that's where +	 * input events are received. Clients can communicate using any of the +	 * standard Java techniques for cross-thread communication, or they can +	 * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method. +	 * <p> +	 * <h3>EGL Context Lost</h3> +	 * There are situations where the EGL rendering context will be lost. This +	 * typically happens when device wakes up after going to sleep. When +	 * the EGL context is lost, all OpenGL resources (such as textures) that are +	 * associated with that context will be automatically deleted. In order to +	 * keep rendering correctly, a renderer must recreate any lost resources +	 * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method +	 * is a convenient place to do this. +	 * +	 * +	 * @see #setRenderer(Renderer) +	 */ +	public interface Renderer { +		/** +		 * Called when the surface is created or recreated. +		 * <p> +		 * Called when the rendering thread +		 * starts and whenever the EGL context is lost. The EGL context will typically +		 * be lost when the Android device awakes after going to sleep. +		 * <p> +		 * Since this method is called at the beginning of rendering, as well as +		 * every time the EGL context is lost, this method is a convenient place to put +		 * code to create resources that need to be created when the rendering +		 * starts, and that need to be recreated when the EGL context is lost. +		 * Textures are an example of a resource that you might want to create +		 * here. +		 * <p> +		 * Note that when the EGL context is lost, all OpenGL resources associated +		 * with that context will be automatically deleted. You do not need to call +		 * the corresponding "glDelete" methods such as glDeleteTextures to +		 * manually delete these lost resources. +		 * <p> +		 * @param gl the GL interface. Use <code>instanceof</code> to +		 * test if the interface supports GL11 or higher interfaces. +		 * @param config the EGLConfig of the created surface. Can be used +		 * to create matching pbuffers. +		 */ +		void onSurfaceCreated(GL10 gl, EGLConfig config); + +		/** +		 * Called when the surface changed size. +		 * <p> +		 * Called after the surface is created and whenever +		 * the OpenGL ES surface size changes. +		 * <p> +		 * Typically you will set your viewport here. If your camera +		 * is fixed then you could also set your projection matrix here: +		 * <pre class="prettyprint"> +		 * void onSurfaceChanged(GL10 gl, int width, int height) { +		 *     gl.glViewport(0, 0, width, height); +		 *     // for a fixed camera, set the projection too +		 *     float ratio = (float) width / height; +		 *     gl.glMatrixMode(GL10.GL_PROJECTION); +		 *     gl.glLoadIdentity(); +		 *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); +		 * } +		 * </pre> +		 * @param gl the GL interface. Use <code>instanceof</code> to +		 * test if the interface supports GL11 or higher interfaces. +		 * @param width +		 * @param height +		 */ +		void onSurfaceChanged(GL10 gl, int width, int height); + +		// -- GODOT start -- +		/** +		 * Called to draw the current frame. +		 * <p> +		 * This method is responsible for drawing the current frame. +		 * <p> +		 * The implementation of this method typically looks like this: +		 * <pre class="prettyprint"> +		 * boolean onDrawFrame(GL10 gl) { +		 *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); +		 *     //... other gl calls to render the scene ... +		 *     return true; +		 * } +		 * </pre> +		 * @param gl the GL interface. Use <code>instanceof</code> to +		 * test if the interface supports GL11 or higher interfaces. +		 * +		 * @return true if the buffers should be swapped, false otherwise. +		 */ +		boolean onDrawFrame(GL10 gl); +		// -- GODOT end -- +	} + +	/** +	 * An interface for customizing the eglCreateContext and eglDestroyContext calls. +	 * <p> +	 * This interface must be implemented by clients wishing to call +	 * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} +	 */ +	public interface EGLContextFactory { +		EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); +		void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); +	} + +	private class DefaultContextFactory implements EGLContextFactory { +		private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + +		public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { +			int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, +					EGL10.EGL_NONE }; + +			return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, +					mEGLContextClientVersion != 0 ? attrib_list : null); +		} + +		public void destroyContext(EGL10 egl, EGLDisplay display, +								   EGLContext context) { +			if (!egl.eglDestroyContext(display, context)) { +				Log.e("DefaultContextFactory", "display:" + display + " context: " + context); +				if (LOG_THREADS) { +					Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId()); +				} +				EglHelper.throwEglException("eglDestroyContex", egl.eglGetError()); +			} +		} +	} + +	/** +	 * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. +	 * <p> +	 * This interface must be implemented by clients wishing to call +	 * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} +	 */ +	public interface EGLWindowSurfaceFactory { +		/** +		 *  @return null if the surface cannot be constructed. +		 */ +		EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, +									   Object nativeWindow); +		void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); +	} + +	private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { + +		public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, +											  EGLConfig config, Object nativeWindow) { +			EGLSurface result = null; +			try { +				result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); +			} catch (IllegalArgumentException e) { +				// This exception indicates that the surface flinger surface +				// is not valid. This can happen if the surface flinger surface has +				// been torn down, but the application has not yet been +				// notified via SurfaceHolder.Callback.surfaceDestroyed. +				// In theory the application should be notified first, +				// but in practice sometimes it is not. See b/4588890 +				Log.e(TAG, "eglCreateWindowSurface", e); +			} +			return result; +		} + +		public void destroySurface(EGL10 egl, EGLDisplay display, +								   EGLSurface surface) { +			egl.eglDestroySurface(display, surface); +		} +	} + +	/** +	 * An interface for choosing an EGLConfig configuration from a list of +	 * potential configurations. +	 * <p> +	 * This interface must be implemented by clients wishing to call +	 * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)} +	 */ +	public interface EGLConfigChooser { +		/** +		 * Choose a configuration from the list. Implementors typically +		 * implement this method by calling +		 * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the +		 * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. +		 * @param egl the EGL10 for the current display. +		 * @param display the current display. +		 * @return the chosen configuration. +		 */ +		EGLConfig chooseConfig(EGL10 egl, EGLDisplay display); +	} + +	private abstract class BaseConfigChooser +			implements EGLConfigChooser { +		public BaseConfigChooser(int[] configSpec) { +			mConfigSpec = filterConfigSpec(configSpec); +		} + +		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { +			int[] num_config = new int[1]; +			if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, +					num_config)) { +				throw new IllegalArgumentException("eglChooseConfig failed"); +			} + +			int numConfigs = num_config[0]; + +			if (numConfigs <= 0) { +				throw new IllegalArgumentException( +						"No configs match configSpec"); +			} + +			EGLConfig[] configs = new EGLConfig[numConfigs]; +			if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, +					num_config)) { +				throw new IllegalArgumentException("eglChooseConfig#2 failed"); +			} +			EGLConfig config = chooseConfig(egl, display, configs); +			if (config == null) { +				throw new IllegalArgumentException("No config chosen"); +			} +			return config; +		} + +		abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, +										EGLConfig[] configs); + +		protected int[] mConfigSpec; + +		private int[] filterConfigSpec(int[] configSpec) { +			if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) { +				return configSpec; +			} +			/* We know none of the subclasses define EGL_RENDERABLE_TYPE. +			 * And we know the configSpec is well formed. +			 */ +			int len = configSpec.length; +			int[] newConfigSpec = new int[len + 2]; +			System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); +			newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; +			if (mEGLContextClientVersion == 2) { +				newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT;  /* EGL_OPENGL_ES2_BIT */ +			} else { +				newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */ +			} +			newConfigSpec[len+1] = EGL10.EGL_NONE; +			return newConfigSpec; +		} +	} + +	/** +	 * Choose a configuration with exactly the specified r,g,b,a sizes, +	 * and at least the specified depth and stencil sizes. +	 */ +	private class ComponentSizeChooser extends BaseConfigChooser { +		public ComponentSizeChooser(int redSize, int greenSize, int blueSize, +									int alphaSize, int depthSize, int stencilSize) { +			super(new int[] { +					EGL10.EGL_RED_SIZE, redSize, +					EGL10.EGL_GREEN_SIZE, greenSize, +					EGL10.EGL_BLUE_SIZE, blueSize, +					EGL10.EGL_ALPHA_SIZE, alphaSize, +					EGL10.EGL_DEPTH_SIZE, depthSize, +					EGL10.EGL_STENCIL_SIZE, stencilSize, +					EGL10.EGL_NONE}); +			mValue = new int[1]; +			mRedSize = redSize; +			mGreenSize = greenSize; +			mBlueSize = blueSize; +			mAlphaSize = alphaSize; +			mDepthSize = depthSize; +			mStencilSize = stencilSize; +		} + +		@Override +		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, +									  EGLConfig[] configs) { +			for (EGLConfig config : configs) { +				int d = findConfigAttrib(egl, display, config, +						EGL10.EGL_DEPTH_SIZE, 0); +				int s = findConfigAttrib(egl, display, config, +						EGL10.EGL_STENCIL_SIZE, 0); +				if ((d >= mDepthSize) && (s >= mStencilSize)) { +					int r = findConfigAttrib(egl, display, config, +							EGL10.EGL_RED_SIZE, 0); +					int g = findConfigAttrib(egl, display, config, +							EGL10.EGL_GREEN_SIZE, 0); +					int b = findConfigAttrib(egl, display, config, +							EGL10.EGL_BLUE_SIZE, 0); +					int a = findConfigAttrib(egl, display, config, +							EGL10.EGL_ALPHA_SIZE, 0); +					if ((r == mRedSize) && (g == mGreenSize) +							&& (b == mBlueSize) && (a == mAlphaSize)) { +						return config; +					} +				} +			} +			return null; +		} + +		private int findConfigAttrib(EGL10 egl, EGLDisplay display, +									 EGLConfig config, int attribute, int defaultValue) { + +			if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { +				return mValue[0]; +			} +			return defaultValue; +		} + +		private int[] mValue; +		// Subclasses can adjust these values: +		protected int mRedSize; +		protected int mGreenSize; +		protected int mBlueSize; +		protected int mAlphaSize; +		protected int mDepthSize; +		protected int mStencilSize; +	} + +	/** +	 * This class will choose a RGB_888 surface with +	 * or without a depth buffer. +	 * +	 */ +	private class SimpleEGLConfigChooser extends ComponentSizeChooser { +		public SimpleEGLConfigChooser(boolean withDepthBuffer) { +			super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0); +		} +	} + +	/** +	 * An EGL helper class. +	 */ + +	private static class EglHelper { +		public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) { +			mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; +		} + +		/** +		 * Initialize EGL for a given configuration spec. +		 */ +		public void start() { +			if (LOG_EGL) { +				Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); +			} +			/* +			 * Get an EGL instance +			 */ +			mEgl = (EGL10) EGLContext.getEGL(); + +			/* +			 * Get to the default display. +			 */ +			mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + +			if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { +				throw new RuntimeException("eglGetDisplay failed"); +			} + +			/* +			 * We can now initialize EGL for that display +			 */ +			int[] version = new int[2]; +			if(!mEgl.eglInitialize(mEglDisplay, version)) { +				throw new RuntimeException("eglInitialize failed"); +			} +			GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +			if (view == null) { +				mEglConfig = null; +				mEglContext = null; +			} else { +				mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); + +				/* +				 * Create an EGL context. We want to do this as rarely as we can, because an +				 * EGL context is a somewhat heavy object. +				 */ +				mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); +			} +			if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { +				mEglContext = null; +				throwEglException("createContext"); +			} +			if (LOG_EGL) { +				Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); +			} + +			mEglSurface = null; +		} + +		/** +		 * Create an egl surface for the current SurfaceHolder surface. If a surface +		 * already exists, destroy it before creating the new surface. +		 * +		 * @return true if the surface was created successfully. +		 */ +		public boolean createSurface() { +			if (LOG_EGL) { +				Log.w("EglHelper", "createSurface()  tid=" + Thread.currentThread().getId()); +			} +			/* +			 * Check preconditions. +			 */ +			if (mEgl == null) { +				throw new RuntimeException("egl not initialized"); +			} +			if (mEglDisplay == null) { +				throw new RuntimeException("eglDisplay not initialized"); +			} +			if (mEglConfig == null) { +				throw new RuntimeException("mEglConfig not initialized"); +			} + +			/* +			 *  The window size has changed, so we need to create a new +			 *  surface. +			 */ +			destroySurfaceImp(); + +			/* +			 * Create an EGL surface we can render into. +			 */ +			GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +			if (view != null) { +				mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, +						mEglDisplay, mEglConfig, view.getHolder()); +			} else { +				mEglSurface = null; +			} + +			if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { +				int error = mEgl.eglGetError(); +				if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { +					Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); +				} +				return false; +			} + +			/* +			 * Before we can issue GL commands, we need to make sure +			 * the context is current and bound to a surface. +			 */ +			if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { +				/* +				 * Could not make the context current, probably because the underlying +				 * SurfaceView surface has been destroyed. +				 */ +				logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); +				return false; +			} + +			return true; +		} + +		/** +		 * Create a GL object for the current EGL context. +		 * @return +		 */ +		GL createGL() { + +			GL gl = mEglContext.getGL(); +			GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +			if (view != null) { +				if (view.mGLWrapper != null) { +					gl = view.mGLWrapper.wrap(gl); +				} + +				if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { +					int configFlags = 0; +					Writer log = null; +					if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { +						configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; +					} +					if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { +						log = new LogWriter(); +					} +					gl = GLDebugHelper.wrap(gl, configFlags, log); +				} +			} +			return gl; +		} + +		/** +		 * Display the current render surface. +		 * @return the EGL error code from eglSwapBuffers. +		 */ +		public int swap() { +			if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { +				return mEgl.eglGetError(); +			} +			return EGL10.EGL_SUCCESS; +		} + +		public void destroySurface() { +			if (LOG_EGL) { +				Log.w("EglHelper", "destroySurface()  tid=" + Thread.currentThread().getId()); +			} +			destroySurfaceImp(); +		} + +		private void destroySurfaceImp() { +			if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { +				mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, +						EGL10.EGL_NO_SURFACE, +						EGL10.EGL_NO_CONTEXT); +				GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +				if (view != null) { +					view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); +				} +				mEglSurface = null; +			} +		} + +		public void finish() { +			if (LOG_EGL) { +				Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); +			} +			if (mEglContext != null) { +				GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +				if (view != null) { +					view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); +				} +				mEglContext = null; +			} +			if (mEglDisplay != null) { +				mEgl.eglTerminate(mEglDisplay); +				mEglDisplay = null; +			} +		} + +		private void throwEglException(String function) { +			throwEglException(function, mEgl.eglGetError()); +		} + +		public static void throwEglException(String function, int error) { +			String message = formatEglError(function, error); +			if (LOG_THREADS) { +				Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " +						+ message); +			} +			throw new RuntimeException(message); +		} + +		public static void logEglErrorAsWarning(String tag, String function, int error) { +			Log.w(tag, formatEglError(function, error)); +		} + +		public static String formatEglError(String function, int error) { +			return function + " failed: " + EGLLogWrapper.getErrorString(error); +		} + +		private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef; +		EGL10 mEgl; +		EGLDisplay mEglDisplay; +		EGLSurface mEglSurface; +		EGLConfig mEglConfig; +		EGLContext mEglContext; + +	} + +	/** +	 * A generic GL Thread. Takes care of initializing EGL and GL. Delegates +	 * to a Renderer instance to do the actual drawing. Can be configured to +	 * render continuously or on request. +	 * +	 * All potentially blocking synchronization is done through the +	 * sGLThreadManager object. This avoids multiple-lock ordering issues. +	 * +	 */ +	static class GLThread extends Thread { +		GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) { +			super(); +			mWidth = 0; +			mHeight = 0; +			mRequestRender = true; +			mRenderMode = RENDERMODE_CONTINUOUSLY; +			mWantRenderNotification = false; +			mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; +		} + +		@Override +		public void run() { +			setName("GLThread " + getId()); +			if (LOG_THREADS) { +				Log.i("GLThread", "starting tid=" + getId()); +			} + +			try { +				guardedRun(); +			} catch (InterruptedException e) { +				// fall thru and exit normally +			} finally { +				sGLThreadManager.threadExiting(this); +			} +		} + +		/* +		 * This private method should only be called inside a +		 * synchronized(sGLThreadManager) block. +		 */ +		private void stopEglSurfaceLocked() { +			if (mHaveEglSurface) { +				mHaveEglSurface = false; +				mEglHelper.destroySurface(); +			} +		} + +		/* +		 * This private method should only be called inside a +		 * synchronized(sGLThreadManager) block. +		 */ +		private void stopEglContextLocked() { +			if (mHaveEglContext) { +				mEglHelper.finish(); +				mHaveEglContext = false; +				sGLThreadManager.releaseEglContextLocked(this); +			} +		} +		private void guardedRun() throws InterruptedException { +			mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); +			mHaveEglContext = false; +			mHaveEglSurface = false; +			mWantRenderNotification = false; + +			try { +				GL10 gl = null; +				boolean createEglContext = false; +				boolean createEglSurface = false; +				boolean createGlInterface = false; +				boolean lostEglContext = false; +				boolean sizeChanged = false; +				boolean wantRenderNotification = false; +				boolean doRenderNotification = false; +				boolean askedToReleaseEglContext = false; +				int w = 0; +				int h = 0; +				Runnable event = null; +				Runnable finishDrawingRunnable = null; + +				while (true) { +					synchronized (sGLThreadManager) { +						while (true) { +							if (mShouldExit) { +								return; +							} + +							if (! mEventQueue.isEmpty()) { +								event = mEventQueue.remove(0); +								break; +							} + +							// Update the pause state. +							boolean pausing = false; +							if (mPaused != mRequestPaused) { +								pausing = mRequestPaused; +								mPaused = mRequestPaused; +								sGLThreadManager.notifyAll(); +								if (LOG_PAUSE_RESUME) { +									Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId()); +								} +							} + +							// Do we need to give up the EGL context? +							if (mShouldReleaseEglContext) { +								if (LOG_SURFACE) { +									Log.i("GLThread", "releasing EGL context because asked to tid=" + getId()); +								} +								stopEglSurfaceLocked(); +								stopEglContextLocked(); +								mShouldReleaseEglContext = false; +								askedToReleaseEglContext = true; +							} + +							// Have we lost the EGL context? +							if (lostEglContext) { +								stopEglSurfaceLocked(); +								stopEglContextLocked(); +								lostEglContext = false; +							} + +							// When pausing, release the EGL surface: +							if (pausing && mHaveEglSurface) { +								if (LOG_SURFACE) { +									Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); +								} +								stopEglSurfaceLocked(); +							} + +							// When pausing, optionally release the EGL Context: +							if (pausing && mHaveEglContext) { +								GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +								boolean preserveEglContextOnPause = view == null ? +										false : view.mPreserveEGLContextOnPause; +								if (!preserveEglContextOnPause) { +									stopEglContextLocked(); +									if (LOG_SURFACE) { +										Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); +									} +								} +							} + +							// Have we lost the SurfaceView surface? +							if ((! mHasSurface) && (! mWaitingForSurface)) { +								if (LOG_SURFACE) { +									Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); +								} +								if (mHaveEglSurface) { +									stopEglSurfaceLocked(); +								} +								mWaitingForSurface = true; +								mSurfaceIsBad = false; +								sGLThreadManager.notifyAll(); +							} + +							// Have we acquired the surface view surface? +							if (mHasSurface && mWaitingForSurface) { +								if (LOG_SURFACE) { +									Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); +								} +								mWaitingForSurface = false; +								sGLThreadManager.notifyAll(); +							} + +							if (doRenderNotification) { +								if (LOG_SURFACE) { +									Log.i("GLThread", "sending render notification tid=" + getId()); +								} +								mWantRenderNotification = false; +								doRenderNotification = false; +								mRenderComplete = true; +								sGLThreadManager.notifyAll(); +							} + +							if (mFinishDrawingRunnable != null) { +								finishDrawingRunnable = mFinishDrawingRunnable; +								mFinishDrawingRunnable = null; +							} + +							// Ready to draw? +							if (readyToDraw()) { + +								// If we don't have an EGL context, try to acquire one. +								if (! mHaveEglContext) { +									if (askedToReleaseEglContext) { +										askedToReleaseEglContext = false; +									} else { +										try { +											mEglHelper.start(); +										} catch (RuntimeException t) { +											sGLThreadManager.releaseEglContextLocked(this); +											throw t; +										} +										mHaveEglContext = true; +										createEglContext = true; + +										sGLThreadManager.notifyAll(); +									} +								} + +								if (mHaveEglContext && !mHaveEglSurface) { +									mHaveEglSurface = true; +									createEglSurface = true; +									createGlInterface = true; +									sizeChanged = true; +								} + +								if (mHaveEglSurface) { +									if (mSizeChanged) { +										sizeChanged = true; +										w = mWidth; +										h = mHeight; +										mWantRenderNotification = true; +										if (LOG_SURFACE) { +											Log.i("GLThread", +													"noticing that we want render notification tid=" +															+ getId()); +										} + +										// Destroy and recreate the EGL surface. +										createEglSurface = true; + +										mSizeChanged = false; +									} +									mRequestRender = false; +									sGLThreadManager.notifyAll(); +									if (mWantRenderNotification) { +										wantRenderNotification = true; +									} +									break; +								} +							} else { +								if (finishDrawingRunnable != null) { +									Log.w(TAG, "Warning, !readyToDraw() but waiting for " + +											"draw finished! Early reporting draw finished."); +									finishDrawingRunnable.run(); +									finishDrawingRunnable = null; +								} +							} +							// By design, this is the only place in a GLThread thread where we wait(). +							if (LOG_THREADS) { +								Log.i("GLThread", "waiting tid=" + getId() +										+ " mHaveEglContext: " + mHaveEglContext +										+ " mHaveEglSurface: " + mHaveEglSurface +										+ " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface +										+ " mPaused: " + mPaused +										+ " mHasSurface: " + mHasSurface +										+ " mSurfaceIsBad: " + mSurfaceIsBad +										+ " mWaitingForSurface: " + mWaitingForSurface +										+ " mWidth: " + mWidth +										+ " mHeight: " + mHeight +										+ " mRequestRender: " + mRequestRender +										+ " mRenderMode: " + mRenderMode); +							} +							sGLThreadManager.wait(); +						} +					} // end of synchronized(sGLThreadManager) + +					if (event != null) { +						event.run(); +						event = null; +						continue; +					} + +					if (createEglSurface) { +						if (LOG_SURFACE) { +							Log.w("GLThread", "egl createSurface"); +						} +						if (mEglHelper.createSurface()) { +							synchronized(sGLThreadManager) { +								mFinishedCreatingEglSurface = true; +								sGLThreadManager.notifyAll(); +							} +						} else { +							synchronized(sGLThreadManager) { +								mFinishedCreatingEglSurface = true; +								mSurfaceIsBad = true; +								sGLThreadManager.notifyAll(); +							} +							continue; +						} +						createEglSurface = false; +					} + +					if (createGlInterface) { +						gl = (GL10) mEglHelper.createGL(); + +						createGlInterface = false; +					} + +					// -- GODOT start -- +					if (createEglContext) { +						if (LOG_RENDERER) { +							Log.w("GLThread", "onSurfaceCreated"); +						} +						GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +						if (view != null) { +							try { +								view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); +							} finally { +							} +						} +						createEglContext = false; +					} + +					if (sizeChanged) { +						if (LOG_RENDERER) { +							Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); +						} +						GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +						if (view != null) { +							try { +								view.mRenderer.onSurfaceChanged(gl, w, h); +							} finally { +							} +						} +						sizeChanged = false; +					} + +					boolean swapBuffers = false; +					if (LOG_RENDERER_DRAW_FRAME) { +						Log.w("GLThread", "onDrawFrame tid=" + getId()); +					} +					{ +						GLSurfaceView view = mGLSurfaceViewWeakRef.get(); +						if (view != null) { +							try { +								swapBuffers = view.mRenderer.onDrawFrame(gl); +								if (finishDrawingRunnable != null) { +									finishDrawingRunnable.run(); +									finishDrawingRunnable = null; +								} +							} finally {} +						} +					} +					if (swapBuffers) { +						int swapError = mEglHelper.swap(); +						switch (swapError) { +							case EGL10.EGL_SUCCESS: +								break; +							case EGL11.EGL_CONTEXT_LOST: +								if (LOG_SURFACE) { +									Log.i("GLThread", "egl context lost tid=" + getId()); +								} +								lostEglContext = true; +								break; +							default: +								// Other errors typically mean that the current surface is bad, +								// probably because the SurfaceView surface has been destroyed, +								// but we haven't been notified yet. +								// Log the error to help developers understand why rendering stopped. +								EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); + +								synchronized (sGLThreadManager) { +									mSurfaceIsBad = true; +									sGLThreadManager.notifyAll(); +								} +								break; +						} +					} +					// -- GODOT end -- + +					if (wantRenderNotification) { +						doRenderNotification = true; +						wantRenderNotification = false; +					} +				} + +			} finally { +				/* +				 * clean-up everything... +				 */ +				synchronized (sGLThreadManager) { +					stopEglSurfaceLocked(); +					stopEglContextLocked(); +				} +			} +		} + +		public boolean ableToDraw() { +			return mHaveEglContext && mHaveEglSurface && readyToDraw(); +		} + +		private boolean readyToDraw() { +			return (!mPaused) && mHasSurface && (!mSurfaceIsBad) +					&& (mWidth > 0) && (mHeight > 0) +					&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); +		} + +		public void setRenderMode(int renderMode) { +			if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { +				throw new IllegalArgumentException("renderMode"); +			} +			synchronized(sGLThreadManager) { +				mRenderMode = renderMode; +				sGLThreadManager.notifyAll(); +			} +		} + +		public int getRenderMode() { +			synchronized(sGLThreadManager) { +				return mRenderMode; +			} +		} + +		public void requestRender() { +			synchronized(sGLThreadManager) { +				mRequestRender = true; +				sGLThreadManager.notifyAll(); +			} +		} + +		public void requestRenderAndNotify(Runnable finishDrawing) { +			synchronized(sGLThreadManager) { +				// If we are already on the GL thread, this means a client callback +				// has caused reentrancy, for example via updating the SurfaceView parameters. +				// We will return to the client rendering code, so here we don't need to +				// do anything. +				if (Thread.currentThread() == this) { +					return; +				} + +				mWantRenderNotification = true; +				mRequestRender = true; +				mRenderComplete = false; +				mFinishDrawingRunnable = finishDrawing; + +				sGLThreadManager.notifyAll(); +			} +		} + +		public void surfaceCreated() { +			synchronized(sGLThreadManager) { +				if (LOG_THREADS) { +					Log.i("GLThread", "surfaceCreated tid=" + getId()); +				} +				mHasSurface = true; +				mFinishedCreatingEglSurface = false; +				sGLThreadManager.notifyAll(); +				while (mWaitingForSurface +						&& !mFinishedCreatingEglSurface +						&& !mExited) { +					try { +						sGLThreadManager.wait(); +					} catch (InterruptedException e) { +						Thread.currentThread().interrupt(); +					} +				} +			} +		} + +		public void surfaceDestroyed() { +			synchronized(sGLThreadManager) { +				if (LOG_THREADS) { +					Log.i("GLThread", "surfaceDestroyed tid=" + getId()); +				} +				mHasSurface = false; +				sGLThreadManager.notifyAll(); +				while((!mWaitingForSurface) && (!mExited)) { +					try { +						sGLThreadManager.wait(); +					} catch (InterruptedException e) { +						Thread.currentThread().interrupt(); +					} +				} +			} +		} + +		public void onPause() { +			synchronized (sGLThreadManager) { +				if (LOG_PAUSE_RESUME) { +					Log.i("GLThread", "onPause tid=" + getId()); +				} +				mRequestPaused = true; +				sGLThreadManager.notifyAll(); +				while ((! mExited) && (! mPaused)) { +					if (LOG_PAUSE_RESUME) { +						Log.i("Main thread", "onPause waiting for mPaused."); +					} +					try { +						sGLThreadManager.wait(); +					} catch (InterruptedException ex) { +						Thread.currentThread().interrupt(); +					} +				} +			} +		} + +		public void onResume() { +			synchronized (sGLThreadManager) { +				if (LOG_PAUSE_RESUME) { +					Log.i("GLThread", "onResume tid=" + getId()); +				} +				mRequestPaused = false; +				mRequestRender = true; +				mRenderComplete = false; +				sGLThreadManager.notifyAll(); +				while ((! mExited) && mPaused && (!mRenderComplete)) { +					if (LOG_PAUSE_RESUME) { +						Log.i("Main thread", "onResume waiting for !mPaused."); +					} +					try { +						sGLThreadManager.wait(); +					} catch (InterruptedException ex) { +						Thread.currentThread().interrupt(); +					} +				} +			} +		} + +		public void onWindowResize(int w, int h) { +			synchronized (sGLThreadManager) { +				mWidth = w; +				mHeight = h; +				mSizeChanged = true; +				mRequestRender = true; +				mRenderComplete = false; + +				// If we are already on the GL thread, this means a client callback +				// has caused reentrancy, for example via updating the SurfaceView parameters. +				// We need to process the size change eventually though and update our EGLSurface. +				// So we set the parameters and return so they can be processed on our +				// next iteration. +				if (Thread.currentThread() == this) { +					return; +				} + +				sGLThreadManager.notifyAll(); + +				// Wait for thread to react to resize and render a frame +				while (! mExited && !mPaused && !mRenderComplete +						&& ableToDraw()) { +					if (LOG_SURFACE) { +						Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId()); +					} +					try { +						sGLThreadManager.wait(); +					} catch (InterruptedException ex) { +						Thread.currentThread().interrupt(); +					} +				} +			} +		} + +		public void requestExitAndWait() { +			// don't call this from GLThread thread or it is a guaranteed +			// deadlock! +			synchronized(sGLThreadManager) { +				mShouldExit = true; +				sGLThreadManager.notifyAll(); +				while (! mExited) { +					try { +						sGLThreadManager.wait(); +					} catch (InterruptedException ex) { +						Thread.currentThread().interrupt(); +					} +				} +			} +		} + +		public void requestReleaseEglContextLocked() { +			mShouldReleaseEglContext = true; +			sGLThreadManager.notifyAll(); +		} + +		/** +		 * Queue an "event" to be run on the GL rendering thread. +		 * @param r the runnable to be run on the GL rendering thread. +		 */ +		public void queueEvent(Runnable r) { +			if (r == null) { +				throw new IllegalArgumentException("r must not be null"); +			} +			synchronized(sGLThreadManager) { +				mEventQueue.add(r); +				sGLThreadManager.notifyAll(); +			} +		} + +		// Once the thread is started, all accesses to the following member +		// variables are protected by the sGLThreadManager monitor +		private boolean mShouldExit; +		private boolean mExited; +		private boolean mRequestPaused; +		private boolean mPaused; +		private boolean mHasSurface; +		private boolean mSurfaceIsBad; +		private boolean mWaitingForSurface; +		private boolean mHaveEglContext; +		private boolean mHaveEglSurface; +		private boolean mFinishedCreatingEglSurface; +		private boolean mShouldReleaseEglContext; +		private int mWidth; +		private int mHeight; +		private int mRenderMode; +		private boolean mRequestRender; +		private boolean mWantRenderNotification; +		private boolean mRenderComplete; +		private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); +		private boolean mSizeChanged = true; +		private Runnable mFinishDrawingRunnable = null; + +		// End of member variables protected by the sGLThreadManager monitor. + +		private EglHelper mEglHelper; + +		/** +		 * Set once at thread construction time, nulled out when the parent view is garbage +		 * called. This weak reference allows the GLSurfaceView to be garbage collected while +		 * the GLThread is still alive. +		 */ +		private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef; + +	} + +	static class LogWriter extends Writer { + +		@Override public void close() { +			flushBuilder(); +		} + +		@Override public void flush() { +			flushBuilder(); +		} + +		@Override public void write(char[] buf, int offset, int count) { +			for(int i = 0; i < count; i++) { +				char c = buf[offset + i]; +				if ( c == '\n') { +					flushBuilder(); +				} +				else { +					mBuilder.append(c); +				} +			} +		} + +		private void flushBuilder() { +			if (mBuilder.length() > 0) { +				Log.v("GLSurfaceView", mBuilder.toString()); +				mBuilder.delete(0, mBuilder.length()); +			} +		} + +		private StringBuilder mBuilder = new StringBuilder(); +	} + + +	private void checkRenderThreadState() { +		if (mGLThread != null) { +			throw new IllegalStateException( +					"setRenderer has already been called for this instance."); +		} +	} + +	private static class GLThreadManager { +		private static String TAG = "GLThreadManager"; + +		public synchronized void threadExiting(GLThread thread) { +			if (LOG_THREADS) { +				Log.i("GLThread", "exiting tid=" +  thread.getId()); +			} +			thread.mExited = true; +			notifyAll(); +		} + +		/* +		 * Releases the EGL context. Requires that we are already in the +		 * sGLThreadManager monitor when this is called. +		 */ +		public void releaseEglContextLocked(GLThread thread) { +			notifyAll(); +		} +	} + +	private static final GLThreadManager sGLThreadManager = new GLThreadManager(); + +	private final WeakReference<GLSurfaceView> mThisWeakRef = +			new WeakReference<GLSurfaceView>(this); +	private GLThread mGLThread; +	private Renderer mRenderer; +	private boolean mDetached; +	private EGLConfigChooser mEGLConfigChooser; +	private EGLContextFactory mEGLContextFactory; +	private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; +	private GLWrapper mGLWrapper; +	private int mDebugFlags; +	private int mEGLContextClientVersion; +	private boolean mPreserveEGLContextOnPause; +} + diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java index e3956ac459..5c4fd00f6d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java @@ -28,38 +28,38 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -package org.godotengine.godot; +package org.godotengine.godot.gl; +import org.godotengine.godot.GodotLib;  import org.godotengine.godot.plugin.GodotPlugin;  import org.godotengine.godot.plugin.GodotPluginRegistry; -import org.godotengine.godot.utils.GLUtils; - -import android.opengl.GLSurfaceView;  import javax.microedition.khronos.egl.EGLConfig;  import javax.microedition.khronos.opengles.GL10;  /** - * Godot's renderer implementation. + * Godot's GL renderer implementation.   */ -class GodotRenderer implements GLSurfaceView.Renderer { +public class GodotRenderer implements GLSurfaceView.Renderer {  	private final GodotPluginRegistry pluginRegistry;  	private boolean activityJustResumed = false; -	GodotRenderer() { +	public GodotRenderer() {  		this.pluginRegistry = GodotPluginRegistry.getPluginRegistry();  	} -	public void onDrawFrame(GL10 gl) { +	public boolean onDrawFrame(GL10 gl) {  		if (activityJustResumed) {  			GodotLib.onRendererResumed();  			activityJustResumed = false;  		} -		GodotLib.step(); +		boolean swapBuffers = GodotLib.step();  		for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {  			plugin.onGLDrawFrame(gl);  		} + +		return swapBuffers;  	}  	public void onSurfaceChanged(GL10 gl, int width, int height) { @@ -76,13 +76,13 @@ class GodotRenderer implements GLSurfaceView.Renderer {  		}  	} -	void onActivityResumed() { +	public void onActivityResumed() {  		// We defer invoking GodotLib.onRendererResumed() until the first draw frame call.  		// This ensures we have a valid GL context and surface when we do so.  		activityJustResumed = true;  	} -	void onActivityPaused() { +	public void onActivityPaused() {  		GodotLib.onRendererPaused();  	}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java index 4c1c84affb..e35d4f5828 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java @@ -30,8 +30,9 @@  package org.godotengine.godot.xr.ovr; +import org.godotengine.godot.gl.GLSurfaceView; +  import android.opengl.EGLExt; -import android.opengl.GLSurfaceView;  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGLConfig; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java index 2b4369b8a6..deb9c4bb1d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java @@ -30,8 +30,9 @@  package org.godotengine.godot.xr.ovr; +import org.godotengine.godot.gl.GLSurfaceView; +  import android.opengl.EGL14; -import android.opengl.GLSurfaceView;  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGLConfig; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java index fbfe0a3a75..f087b7dc74 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java @@ -30,7 +30,7 @@  package org.godotengine.godot.xr.ovr; -import android.opengl.GLSurfaceView; +import org.godotengine.godot.gl.GLSurfaceView;  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGLConfig; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java index 9fde1961ea..445238b1c2 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java @@ -30,10 +30,9 @@  package org.godotengine.godot.xr.regular; +import org.godotengine.godot.gl.GLSurfaceView;  import org.godotengine.godot.utils.GLUtils; -import android.opengl.GLSurfaceView; -  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGLConfig;  import javax.microedition.khronos.egl.EGLDisplay; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java index ce1184a75c..5d62723170 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java @@ -30,9 +30,9 @@  package org.godotengine.godot.xr.regular; +import org.godotengine.godot.gl.GLSurfaceView;  import org.godotengine.godot.utils.GLUtils; -import android.opengl.GLSurfaceView;  import android.util.Log;  import javax.microedition.khronos.egl.EGL10; diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java index 420dda45a0..68329c5c49 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java @@ -30,8 +30,6 @@  package org.godotengine.godot.xr.regular; -import org.godotengine.godot.utils.GLUtils; -  import android.util.Log;  import javax.microedition.khronos.egl.EGL10;  |