Enough JNI/threading hackery to get it to run on Android! Broken readbacks make it crash alot though.

This commit is contained in:
Henrik Rydgård 2018-01-28 17:27:23 +01:00
parent 7abb8702ce
commit bd3a681fd3
5 changed files with 52 additions and 37 deletions

View file

@ -21,5 +21,5 @@ class AndroidGraphicsContext : public GraphicsContext {
public: public:
// This is different than the base class function since on // This is different than the base class function since on
// Android (EGL, Vulkan) we do have all this info on the render thread. // Android (EGL, Vulkan) we do have all this info on the render thread.
virtual bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {} virtual bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
}; };

View file

@ -51,10 +51,6 @@
#include "app-android.h" #include "app-android.h"
JNIEnv *jniEnvMain;
JNIEnv *jniEnvGraphics;
JavaVM *javaVM;
bool useCPUThread = true; bool useCPUThread = true;
enum class EmuThreadState { enum class EmuThreadState {
@ -113,6 +109,12 @@ static int backbuffer_format; // Android PixelFormat enum
static int desiredBackbufferSizeX; static int desiredBackbufferSizeX;
static int desiredBackbufferSizeY; static int desiredBackbufferSizeY;
// Cache the class loader so we can use it from native threads. Required for TextAndroid.
JavaVM* gJvm = nullptr;
static jobject gClassLoader;
static jmethodID gFindClassMethod;
static jmethodID postCommand; static jmethodID postCommand;
static jobject nativeActivity; static jobject nativeActivity;
static volatile bool exitRenderLoop; static volatile bool exitRenderLoop;
@ -132,9 +134,44 @@ static std::map<SystemPermission, PermissionStatus> permissions;
AndroidGraphicsContext *graphicsContext; AndroidGraphicsContext *graphicsContext;
static void EmuThreadFunc(JavaVM *vm) { JNIEnv* getEnv() {
JNIEnv *env; JNIEnv *env;
vm->AttachCurrentThread(&env, nullptr); int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
if(status < 0) {
status = gJvm->AttachCurrentThread(&env, NULL);
if(status < 0) {
return nullptr;
}
}
return env;
}
jclass findClass(const char* name) {
return static_cast<jclass>(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewStringUTF(name)));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
ILOG("JNI_OnLoad");
gJvm = pjvm; // cache the JavaVM pointer
auto env = getEnv();
//replace with one of your classes in the line below
auto randomClass = env->FindClass("org/ppsspp/ppsspp/NativeActivity");
jclass classClass = env->GetObjectClass(randomClass);
auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
"()Ljava/lang/ClassLoader;");
gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod));
gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
return JNI_VERSION_1_6;
}
static void EmuThreadFunc() {
JNIEnv *env;
gJvm->AttachCurrentThread(&env, nullptr);
setCurrentThreadName("Emu");
// There's no real requirement that NativeInit happen on this thread. // There's no real requirement that NativeInit happen on this thread.
// We just call the update/render loop here. // We just call the update/render loop here.
emuThreadState = (int)EmuThreadState::RUNNING; emuThreadState = (int)EmuThreadState::RUNNING;
@ -142,14 +179,12 @@ static void EmuThreadFunc(JavaVM *vm) {
UpdateRunLoopAndroid(env); UpdateRunLoopAndroid(env);
} }
emuThreadState = (int)EmuThreadState::STOPPED; emuThreadState = (int)EmuThreadState::STOPPED;
vm->DetachCurrentThread(); gJvm->DetachCurrentThread();
} }
static void EmuThreadStart(JNIEnv *env) { static void EmuThreadStart(JNIEnv *env) {
emuThreadState = (int)EmuThreadState::START_REQUESTED; emuThreadState = (int)EmuThreadState::START_REQUESTED;
JavaVM *vm; emuThread = std::thread(&EmuThreadFunc);
env->GetJavaVM(&vm);
emuThread = std::thread(&EmuThreadFunc, vm);
} }
static void EmuThreadStop() { static void EmuThreadStop() {
@ -306,9 +341,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
(JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath,
jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
jint jAndroidVersion, jstring jboard) { jint jAndroidVersion, jstring jboard) {
jniEnvMain = env;
env->GetJavaVM(&javaVM);
setCurrentThreadName("androidInit"); setCurrentThreadName("androidInit");
// Makes sure we get early permission grants. // Makes sure we get early permission grants.
@ -472,15 +504,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
// JavaEGL // JavaEGL
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
// Need to get the local JNI env for the graphics thread. Used later in draw_text_android.
int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6);
if (res != JNI_OK) {
ELOG("GetEnv failed: %d", res);
}
if (!graphicsContext)
Crash();
// We should be running on the render thread here. // We should be running on the render thread here.
std::string errorMessage; std::string errorMessage;
if (renderer_inited) { if (renderer_inited) {
@ -495,7 +518,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env,
ILOG("NativeApp.displayInit() first time"); ILOG("NativeApp.displayInit() first time");
graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0); graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
graphicsContext->ThreadStart(); graphicsContext->ThreadStart();
NativeInitGraphics(graphicsContext);
renderer_inited = true; renderer_inited = true;
} }
NativeMessageReceived("recreateviews", ""); NativeMessageReceived("recreateviews", "");
@ -548,6 +570,7 @@ void UpdateRunLoopAndroid(JNIEnv *env) {
while (!renderer_inited) { while (!renderer_inited) {
sleep_ms(20); sleep_ms(20);
} }
NativeInitGraphics(graphicsContext);
} }
NativeUpdate(); NativeUpdate();
@ -852,12 +875,6 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf);
// Need to get the local JNI env for the graphics thread. Used later in draw_text_android.
int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6);
if (res != JNI_OK) {
ELOG("GetEnv failed: %d", res);
}
WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres);
if (wnd == nullptr) { if (wnd == nullptr) {
@ -922,7 +939,6 @@ retry:
graphicsContext = nullptr; graphicsContext = nullptr;
renderLoopRunning = false; renderLoopRunning = false;
WLOG("Render loop function exited."); WLOG("Render loop function exited.");
jniEnvGraphics = nullptr;
return true; return true;
} }

View file

@ -6,8 +6,7 @@
#include <jni.h> #include <jni.h>
extern JNIEnv *jniEnvMain; jclass findClass(const char* name);
extern JNIEnv *jniEnvGraphics; JNIEnv* getEnv();
extern JavaVM *javaVM;
#endif #endif

View file

@ -16,9 +16,9 @@
#include <jni.h> #include <jni.h>
TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) { TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) {
env_ = jniEnvGraphics; env_ = getEnv();
const char *textRendererClassName = "org/ppsspp/ppsspp/TextRenderer"; const char *textRendererClassName = "org/ppsspp/ppsspp/TextRenderer";
jclass localClass = env_->FindClass(textRendererClassName); jclass localClass = findClass(textRendererClassName);
cls_textRenderer = reinterpret_cast<jclass>(env_->NewGlobalRef(localClass)); cls_textRenderer = reinterpret_cast<jclass>(env_->NewGlobalRef(localClass));
ILOG("cls_textRender: %p", cls_textRenderer); ILOG("cls_textRender: %p", cls_textRenderer);
if (cls_textRenderer) { if (cls_textRenderer) {