Merge pull request #26672 from RandomShaper/fix-22955-android-context-loss
Restart game on GL context loss on Android
This commit is contained in:
commit
1100d6a8f2
10 changed files with 101 additions and 47 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue