Selaa lähdekoodia

Restart game on GL context loss on Android

Bonus:
Remove useless old code about reload hooks

Fixes #22955.
Pedro J. Estébanez 6 vuotta sitten
vanhempi
commit
2d0d64794e

+ 5 - 0
platform/android/AndroidManifest.xml.template

@@ -36,4 +36,9 @@ $$ADD_APPLICATION_CHUNKS$$
 
     </application>
 
+    <instrumentation android:icon="@drawable/icon"
+                     android:label="@string/godot_project_name_string"
+                     android:name="org.godotengine.godot.GodotInstrumentation"
+                     android:targetPackage="com.godot.game" />
+
 </manifest>

+ 23 - 5
platform/android/java/src/org/godotengine/godot/Godot.java

@@ -38,6 +38,7 @@ 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.DialogInterface;
 import android.content.Intent;
@@ -318,6 +319,23 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		});
 	}
 
+	public void restart() {
+		// HACK:
+		//
+		// Currently it's very hard to properly deinitialize Godot on Android to restart the game
+		// from scratch. Therefore, we need to kill the whole app process and relaunch it.
+		//
+		// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
+		// releasing and reloading native libs or resetting their state somehow and clearing statics).
+		//
+		// Using instrumentation is a way of making the whole app process restart, because Android
+		// will kill any process of the same package which was already running.
+		//
+		Bundle args = new Bundle();
+		args.putParcelable("intent", mCurrentIntent);
+		startInstrumentation(new ComponentName(Godot.this, GodotInstrumentation.class), null, args);
+	}
+
 	public void alert(final String message, final String title) {
 		final Activity activity = this;
 		runOnUiThread(new Runnable() {
@@ -415,7 +433,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
 		mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
 
-		GodotLib.initialize(this, io.needsReloadHooks(), getAssets(), use_apk_expansion);
+		GodotLib.initialize(this, getAssets(), use_apk_expansion);
 
 		result_callback = null;
 
@@ -921,10 +939,10 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	// Audio
 
 	/**
-     * The download state should trigger changes in the UI --- it may be useful
-     * to show the state as being indeterminate at times. This sample can be
-     * considered a guideline.
-     */
+	 * The download state should trigger changes in the UI --- it may be useful
+	 * to show the state as being indeterminate at times. This sample can be
+	 * considered a guideline.
+	 */
 	@Override
 	public void onDownloadStateChanged(int newState) {
 		setState(newState);

+ 0 - 5
platform/android/java/src/org/godotengine/godot/GodotIO.java

@@ -500,11 +500,6 @@ public class GodotIO {
 		return (int)(metrics.density * 160f);
 	}
 
-	public boolean needsReloadHooks() {
-
-		return false;
-	}
-
 	public void showKeyboard(String p_existing_text) {
 		if (edit != null)
 			edit.showKeyboard(p_existing_text);

+ 50 - 0
platform/android/java/src/org/godotengine/godot/GodotInstrumentation.java

@@ -0,0 +1,50 @@
+/*************************************************************************/
+/*  GodotInstrumentation.java                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+package org.godotengine.godot;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class GodotInstrumentation extends Instrumentation {
+	private Intent intent;
+
+	@Override
+	public void onCreate(Bundle arguments) {
+		intent = arguments.getParcelable("intent");
+		start();
+	}
+
+	@Override
+	public void onStart() {
+		startActivitySync(intent);
+	}
+}

+ 2 - 2
platform/android/java/src/org/godotengine/godot/GodotLib.java

@@ -45,9 +45,9 @@ public class GodotLib {
      * @param height the current view height
      */
 
-	public static native void initialize(Godot p_instance, boolean need_reload_hook, Object p_asset_manager, boolean use_apk_expansion);
+	public static native void initialize(Godot p_instance, Object p_asset_manager, boolean use_apk_expansion);
 	public static native void setup(String[] p_cmdline);
-	public static native void resize(int width, int height, boolean reload);
+	public static native void resize(int width, int height);
 	public static native void newcontext(boolean p_32_bits);
 	public static native void back();
 	public static native void step();

+ 3 - 7
platform/android/java/src/org/godotengine/godot/GodotView.java

@@ -79,7 +79,6 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 	private Context ctx;
 
 	private GodotIO io;
-	private static boolean firsttime = true;
 	private static boolean use_gl3 = false;
 	private static boolean use_32 = false;
 	private static boolean use_debug_opengl = false;
@@ -97,10 +96,8 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 
 		activity = p_activity;
 
-		if (!p_io.needsReloadHooks()) {
-			//will only work on SDK 11+!!
-			setPreserveEGLContextOnPause(true);
-		}
+		setPreserveEGLContextOnPause(true);
+
 		mInputManager = InputManagerCompat.Factory.getInputManager(this.getContext());
 		mInputManager.registerInputDeviceListener(this, null);
 		init(false, 16, 0);
@@ -718,8 +715,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
 
 		public void onSurfaceChanged(GL10 gl, int width, int height) {
 
-			GodotLib.resize(width, height, !firsttime);
-			firsttime = false;
+			GodotLib.resize(width, height);
 			for (int i = 0; i < Godot.singleton_count; i++) {
 				Godot.singletons[i].onGLSurfaceChanged(gl, width, height);
 			}

+ 16 - 9
platform/android/java_glue.cpp

@@ -599,6 +599,7 @@ static jobject godot_io;
 typedef void (*GFXInitFunc)(void *ud, bool gl2);
 
 static jmethodID _on_video_init = 0;
+static jmethodID _restart = 0;
 static jobject _godot_instance;
 
 static jmethodID _openURI = 0;
@@ -757,7 +758,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
 	virtual_keyboard_height = p_height;
 }
 
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) {
 
 	initialized = true;
 
@@ -783,6 +784,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
 		godot_io = gob;
 
 		_on_video_init = env->GetMethodID(cls, "onVideoInit", "()V");
+		_restart = env->GetMethodID(cls, "restart", "()V");
 		_setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V");
 		_alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
 		_getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I");
@@ -821,7 +823,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
 	}
 
 	os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, _set_clipboard, _get_clipboard, p_use_apk_expansion);
-	os_android->set_need_reload_hooks(p_need_reload_hook);
 
 	char wd[500];
 	getcwd(wd, 500);
@@ -932,7 +933,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo
 	_initialize_java_modules();
 }
 
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height) {
 
 	if (os_android)
 		os_android->set_display_size(Size2(width, height));
@@ -941,12 +942,15 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) {
 
 	if (os_android) {
-		os_android->set_context_is_16_bits(!p_32_bits);
-	}
-
-	if (os_android && step > 0) {
-
-		os_android->reload_gfx();
+		if (step == 0) {
+			// During startup
+			os_android->set_context_is_16_bits(!p_32_bits);
+		} else {
+			// GL context recreated because it was lost; restart app to let it reload everything
+			os_android->main_loop_end();
+			env->CallVoidMethod(_godot_instance, _restart);
+			step = -1; // Ensure no further steps are attempted
+		}
 	}
 }
 
@@ -958,6 +962,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, job
 }
 
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) {
+	if (step == -1)
+		return;
+
 	if (step == 0) {
 
 		// Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id,

+ 2 - 2
platform/android/java_glue.h

@@ -35,9 +35,9 @@
 #include <jni.h>
 
 extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj);

+ 0 - 14
platform/android/os_android.cpp

@@ -548,14 +548,6 @@ void OS_Android::set_display_size(Size2 p_size) {
 	default_videomode.height = p_size.y;
 }
 
-void OS_Android::reload_gfx() {
-
-	if (gfx_init_func)
-		gfx_init_func(gfx_init_ud, use_gl2);
-	//if (rasterizer)
-	//	rasterizer->reload_vram();
-}
-
 Error OS_Android::shell_open(String p_uri) {
 
 	if (open_uri_func)
@@ -607,11 +599,6 @@ int OS_Android::get_screen_dpi(int p_screen) const {
 	return 160;
 }
 
-void OS_Android::set_need_reload_hooks(bool p_needs_them) {
-
-	use_reload_hooks = p_needs_them;
-}
-
 String OS_Android::get_user_data_dir() const {
 
 	if (data_dir_cache != String())
@@ -765,7 +752,6 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI
 	set_screen_orientation_func = p_screen_orient;
 	set_keep_screen_on_func = p_set_keep_screen_on_func;
 	alert_func = p_alert_func;
-	use_reload_hooks = false;
 
 	Vector<Logger *> loggers;
 	loggers.push_back(memnew(AndroidLogger));

+ 0 - 3
platform/android/os_android.h

@@ -94,7 +94,6 @@ private:
 	void *gfx_init_ud;
 
 	bool use_gl2;
-	bool use_reload_hooks;
 	bool use_apk_expansion;
 
 	bool use_16bits_fbo;
@@ -203,10 +202,8 @@ public:
 	void set_opengl_extensions(const char *p_gl_extensions);
 	void set_display_size(Size2 p_size);
 
-	void reload_gfx();
 	void set_context_is_16_bits(bool p_is_16);
 
-	void set_need_reload_hooks(bool p_needs_them);
 	virtual void set_screen_orientation(ScreenOrientation p_orientation);
 
 	virtual Error shell_open(String p_uri);