Merge pull request #26672 from RandomShaper/fix-22955-android-context-loss

Restart game on GL context loss on Android
This commit is contained in:
Rémi Verschelde 2019-03-06 22:53:12 +01:00 committed by GitHub
commit 1100d6a8f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 47 deletions

View file

@ -36,4 +36,9 @@ $$ADD_APPLICATION_CHUNKS$$
</application> </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> </manifest>

View file

@ -38,6 +38,7 @@ import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; 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) { public void alert(final String message, final String title) {
final Activity activity = this; final Activity activity = this;
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@ -415,7 +433,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); 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; result_callback = null;
@ -921,10 +939,10 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
// Audio // Audio
/** /**
* The download state should trigger changes in the UI --- it may be useful * 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 * to show the state as being indeterminate at times. This sample can be
* considered a guideline. * considered a guideline.
*/ */
@Override @Override
public void onDownloadStateChanged(int newState) { public void onDownloadStateChanged(int newState) {
setState(newState); setState(newState);

View file

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

View file

@ -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);
}
}

View file

@ -45,9 +45,9 @@ public class GodotLib {
* @param height the current view height * @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 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 newcontext(boolean p_32_bits);
public static native void back(); public static native void back();
public static native void step(); public static native void step();

View file

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

View file

@ -599,6 +599,7 @@ static jobject godot_io;
typedef void (*GFXInitFunc)(void *ud, bool gl2); typedef void (*GFXInitFunc)(void *ud, bool gl2);
static jmethodID _on_video_init = 0; static jmethodID _on_video_init = 0;
static jmethodID _restart = 0;
static jobject _godot_instance; static jobject _godot_instance;
static jmethodID _openURI = 0; static jmethodID _openURI = 0;
@ -757,7 +758,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
virtual_keyboard_height = p_height; 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; initialized = true;
@ -783,6 +784,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
godot_io = gob; godot_io = gob;
_on_video_init = env->GetMethodID(cls, "onVideoInit", "()V"); _on_video_init = env->GetMethodID(cls, "onVideoInit", "()V");
_restart = env->GetMethodID(cls, "restart", "()V");
_setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); _setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V");
_alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); _alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
_getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I"); _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 = 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]; char wd[500];
getcwd(wd, 500); getcwd(wd, 500);
@ -932,7 +933,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo
_initialize_java_modules(); _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) if (os_android)
os_android->set_display_size(Size2(width, height)); 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) { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) {
if (os_android) { if (os_android) {
os_android->set_context_is_16_bits(!p_32_bits); if (step == 0) {
} // During startup
os_android->set_context_is_16_bits(!p_32_bits);
if (os_android && step > 0) { } else {
// GL context recreated because it was lost; restart app to let it reload everything
os_android->reload_gfx(); 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) { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) {
if (step == -1)
return;
if (step == 0) { if (step == 0) {
// Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id,

View file

@ -35,9 +35,9 @@
#include <jni.h> #include <jni.h>
extern "C" { 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_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_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_step(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj);

View file

@ -548,14 +548,6 @@ void OS_Android::set_display_size(Size2 p_size) {
default_videomode.height = p_size.y; 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) { Error OS_Android::shell_open(String p_uri) {
if (open_uri_func) if (open_uri_func)
@ -607,11 +599,6 @@ int OS_Android::get_screen_dpi(int p_screen) const {
return 160; 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 { String OS_Android::get_user_data_dir() const {
if (data_dir_cache != String()) 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_screen_orientation_func = p_screen_orient;
set_keep_screen_on_func = p_set_keep_screen_on_func; set_keep_screen_on_func = p_set_keep_screen_on_func;
alert_func = p_alert_func; alert_func = p_alert_func;
use_reload_hooks = false;
Vector<Logger *> loggers; Vector<Logger *> loggers;
loggers.push_back(memnew(AndroidLogger)); loggers.push_back(memnew(AndroidLogger));

View file

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