mirror of
https://github.com/amwatson/CitraVR.git
synced 2024-09-20 03:11:40 +02:00
move thread/JNI management out of VrApp so that all its objects de-init while JNIEnv is active
This commit is contained in:
parent
a8cd1e2bff
commit
c68b499ad9
4 changed files with 105 additions and 68 deletions
|
@ -30,15 +30,25 @@ License : Licensed under GPLv3 or any later version.
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
XrInstance instance;
|
||||
XrInstance instance = XR_NULL_HANDLE;
|
||||
void OXR_CheckErrors(XrResult result, const char* function, bool failOnError) {
|
||||
if (XR_FAILED(result)) {
|
||||
char errorBuffer[XR_MAX_RESULT_STRING_SIZE];
|
||||
xrResultToString(instance, result, errorBuffer);
|
||||
if (failOnError) {
|
||||
FAIL("OpenXR error: %s: %s\n", function, errorBuffer);
|
||||
if (instance == XR_NULL_HANDLE) {
|
||||
if (failOnError) {
|
||||
FAIL("OpenXR error: %s: \"%s\" (error code 0x%x)", function, "Instance is null",
|
||||
result);
|
||||
} else {
|
||||
ALOGV("OpenXR error: {}: \"{}\" (error code 0x%x)", function, "Instance is null",
|
||||
result);
|
||||
}
|
||||
} else {
|
||||
ALOGV("OpenXR error: {}: {}\n", function, errorBuffer);
|
||||
char errorBuffer[XR_MAX_RESULT_STRING_SIZE];
|
||||
xrResultToString(instance, result, errorBuffer);
|
||||
if (failOnError) {
|
||||
FAIL("OpenXR error: %s: \"%s\" (error code 0x%x)", function, errorBuffer, result);
|
||||
} else {
|
||||
ALOGV("OpenXR error: {}: \"{}\" (error code 0x%x)", function, errorBuffer, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +61,6 @@ void OXR_CheckErrors(XrResult result, const char* function, bool failOnErr
|
|||
================================================================================
|
||||
*/
|
||||
namespace {
|
||||
#define DECL_PFN(pfn) PFN_##pfn pfn = nullptr
|
||||
#define INIT_PFN(pfn) OXR(xrGetInstanceProcAddr(instance, #pfn, (PFN_xrVoidFunction*)(&pfn)))
|
||||
|
||||
[[maybe_unused]] void XrEnumerateLayerProperties() {
|
||||
XrResult result;
|
||||
PFN_xrEnumerateApiLayerProperties xrEnumerateApiLayerProperties;
|
||||
|
|
|
@ -466,6 +466,7 @@ int32_t GameSurfaceLayer::Init(const XrSession& session, const jobject activityO
|
|||
|
||||
void GameSurfaceLayer::Shutdown() {
|
||||
xrDestroySwapchain(mSwapchain.mHandle);
|
||||
mSwapchain.mHandle = XR_NULL_HANDLE;
|
||||
mEnv->DeleteGlobalRef(mVrGameSurfaceClass);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ struct Message {
|
|||
// Note: Keep this in-sync with VrMessageQueue.java.
|
||||
enum Type {
|
||||
SHOW_KEYBOARD = 0, // payload 0 = hide keyboard, 1 = show keyboard
|
||||
SHOW_ERROR_MESSAGE = 1 // payload 0 = show error message, 1 = hide error message
|
||||
SHOW_ERROR_MESSAGE = 1, // payload 0 = show error message, 1 = hide error message
|
||||
EXIT_NEEDED = 2 // payload ignored
|
||||
};
|
||||
|
||||
Message() {}
|
||||
|
|
|
@ -150,42 +150,17 @@ uint32_t GetDefaultGameResolutionFactorForHmd(const VRSettings::HMDType& hmdType
|
|||
|
||||
class VRApp {
|
||||
public:
|
||||
VRApp(JavaVM* jvm, jobject activityObjectGlobalRef)
|
||||
: mVm(jvm)
|
||||
, mActivityObject(activityObjectGlobalRef)
|
||||
, mIsStopRequested(false) {
|
||||
assert(mVm != nullptr);
|
||||
mThread = std::thread([this]() { MainLoop(); });
|
||||
}
|
||||
VRApp(jobject activityObjectGlobalRef)
|
||||
: mActivityObject(activityObjectGlobalRef) {}
|
||||
|
||||
~VRApp() {
|
||||
assert(mVm != nullptr);
|
||||
// Note: this is in most cases already going to be true by the time the
|
||||
// destructor is called, because it is set to true in onStop()
|
||||
mIsStopRequested = true;
|
||||
ALOGI("Waiting for VRApp thread to join");
|
||||
if (mThread.joinable()) { mThread.join(); }
|
||||
ALOGI("VRApp thread joined");
|
||||
JNIEnv* jni;
|
||||
if (mVm->AttachCurrentThread(&jni, nullptr) != JNI_OK) {
|
||||
// on most of the android systems, calling exit() isn't like the end
|
||||
// of the world. The reapers get to it within a few seconds
|
||||
ALOGD("{}() ERROR: could not attach to JVM", __FUNCTION__);
|
||||
exit(0);
|
||||
}
|
||||
jni->DeleteGlobalRef(mActivityObject);
|
||||
}
|
||||
~VRApp() { assert(mIsStopRequested); }
|
||||
|
||||
void MainLoop() {
|
||||
void MainLoop(JNIEnv* jni) {
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Init
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
JNIEnv* jni;
|
||||
if (mVm->AttachCurrentThread(&jni, nullptr) != JNI_OK) {
|
||||
FAIL("%s(): Could not attach to JVM", __FUNCTION__);
|
||||
}
|
||||
Init(jni);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
@ -195,25 +170,15 @@ public:
|
|||
while (!mIsStopRequested) { Frame(jni); }
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Shutdown
|
||||
// Exit
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
ALOGI("::MainLoop() exiting");
|
||||
|
||||
mVm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
private:
|
||||
void Init(JNIEnv* jni) {
|
||||
// Gotta set this after the JNIEnv is attached, or else it'll be
|
||||
// overwritten
|
||||
prctl(PR_SET_NAME, (long)"CS::Main", 0, 0, 0);
|
||||
if (gOpenXr == nullptr) {
|
||||
gOpenXr = std::make_unique<OpenXr>();
|
||||
const int32_t ret = gOpenXr->Init(mVm, mActivityObject);
|
||||
if (ret < 0) { FAIL("OpenXR::Init() failed: error code %d", ret); }
|
||||
}
|
||||
vr::gSession = gOpenXr->mSession;
|
||||
assert(gOpenXr != nullptr);
|
||||
mInputStateStatic =
|
||||
std::make_unique<InputStateStatic>(OpenXr::GetInstance(), gOpenXr->mSession);
|
||||
|
||||
|
@ -886,7 +851,8 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void HandleSessionStateChangedEvent(JNIEnv *jni, const XrEventDataSessionStateChanged& newState) {
|
||||
void HandleSessionStateChangedEvent(JNIEnv* jni,
|
||||
const XrEventDataSessionStateChanged& newState) {
|
||||
static XrSessionState lastState = XR_SESSION_STATE_UNKNOWN;
|
||||
if (newState.state != lastState) {
|
||||
ALOGV("{}(): Received XR_SESSION_STATE_CHANGED state {}->{} "
|
||||
|
@ -1038,6 +1004,11 @@ private:
|
|||
}
|
||||
break;
|
||||
}
|
||||
case Message::Type::EXIT_NEEDED: {
|
||||
ALOGD("Received EXIT_NEEDED message");
|
||||
mIsStopRequested = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ALOGE("Unknown message type: %d", message.mType);
|
||||
|
@ -1048,15 +1019,14 @@ private:
|
|||
|
||||
uint64_t mFrameIndex = 0;
|
||||
std::thread mThread;
|
||||
JavaVM* mVm;
|
||||
jobject mActivityObject;
|
||||
|
||||
std::atomic<bool> mIsStopRequested = {false};
|
||||
bool mIsXrSessionActive = false;
|
||||
bool mHasFocus = false;
|
||||
bool mIsKeyboardActive = false;
|
||||
bool mShouldShowErrorMessage = false;
|
||||
bool mIsEmulationPaused = false;
|
||||
bool mIsStopRequested = false;
|
||||
bool mIsXrSessionActive = false;
|
||||
bool mHasFocus = false;
|
||||
bool mIsKeyboardActive = false;
|
||||
bool mShouldShowErrorMessage = false;
|
||||
bool mIsEmulationPaused = false;
|
||||
|
||||
std::unique_ptr<CursorLayer> mCursorLayer;
|
||||
std::unique_ptr<UILayer> mErrorMessageLayer;
|
||||
|
@ -1075,15 +1045,78 @@ private:
|
|||
jmethodID mOpenSettingsMethodID = nullptr;
|
||||
};
|
||||
|
||||
class VRAppThread {
|
||||
public:
|
||||
VRAppThread(JavaVM* jvm, JNIEnv* jni, jobject activityObject)
|
||||
: mVm(jvm)
|
||||
, mActivityObjectGlobalRef(jni->NewGlobalRef(activityObject)) {
|
||||
assert(jvm != nullptr);
|
||||
assert(activityObject != nullptr);
|
||||
mThread = std::thread([this]() {ThreadFn();});
|
||||
}
|
||||
|
||||
~VRAppThread() {
|
||||
gMessageQueue.Post(Message(Message::Type::EXIT_NEEDED, 0));
|
||||
// Note: this is in most cases already going to be true by the time the
|
||||
// destructor is called, because it is set to true in onStop()
|
||||
ALOGI("Waiting for VRAppThread to join");
|
||||
if (mThread.joinable()) { mThread.join(); }
|
||||
ALOGI("VRAppThread joined");
|
||||
}
|
||||
|
||||
private:
|
||||
void ThreadFn() {
|
||||
assert(mVm != nullptr);
|
||||
ALOGI("VRAppThread: starting");
|
||||
JNIEnv* jni = nullptr;
|
||||
if (mVm->AttachCurrentThread(&jni, nullptr) != JNI_OK) {
|
||||
FAIL("%s(): Could not attach to mVm", __FUNCTION__);
|
||||
}
|
||||
// Gotta set this after the JNIEnv is attached, or else it'll be
|
||||
// overwritten
|
||||
prctl(PR_SET_NAME, (long)"CVR::Main", 0, 0, 0);
|
||||
|
||||
ThreadFnJNI(jni);
|
||||
|
||||
mVm->DetachCurrentThread();
|
||||
ALOGI("VRAppThread: exited");
|
||||
}
|
||||
|
||||
// All operations assume that the JNIEnv is attached
|
||||
void ThreadFnJNI(JNIEnv* jni) {
|
||||
assert(jni != nullptr);
|
||||
assert(mActivityObjectGlobalRef != nullptr);
|
||||
if (gOpenXr == nullptr) {
|
||||
gOpenXr = std::make_unique<OpenXr>();
|
||||
const int32_t ret = gOpenXr->Init(mVm, mActivityObjectGlobalRef);
|
||||
if (ret < 0) { FAIL("OpenXR::Init() failed: error code %d", ret); }
|
||||
}
|
||||
vr::gSession = gOpenXr->mSession;
|
||||
|
||||
{ std::make_unique<VRApp>(mActivityObjectGlobalRef)->MainLoop(jni); }
|
||||
|
||||
ALOGI("::MainLoop() exited");
|
||||
|
||||
gOpenXr->Shutdown();
|
||||
|
||||
jni->DeleteGlobalRef(mActivityObjectGlobalRef);
|
||||
mActivityObjectGlobalRef = nullptr;
|
||||
}
|
||||
|
||||
JavaVM* mVm;
|
||||
jobject mActivityObjectGlobalRef;
|
||||
std::thread mThread;
|
||||
};
|
||||
|
||||
struct VRAppHandle {
|
||||
VRAppHandle(VRApp* _p)
|
||||
VRAppHandle(VRAppThread* _p)
|
||||
: p(_p) {}
|
||||
VRAppHandle(jlong _l)
|
||||
: l(_l) {}
|
||||
|
||||
union {
|
||||
VRApp* p = nullptr;
|
||||
jlong l;
|
||||
VRAppThread* p = nullptr;
|
||||
jlong l;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1095,7 +1128,7 @@ Java_org_citra_citra_1emu_vr_VrActivity_nativeOnCreate(JNIEnv* env, jobject thiz
|
|||
|
||||
JavaVM* jvm;
|
||||
env->GetJavaVM(&jvm);
|
||||
auto ret = VRAppHandle(new VRApp(jvm, env->NewGlobalRef(thiz))).l;
|
||||
auto ret = VRAppHandle(new VRAppThread(jvm, env, thiz)).l;
|
||||
ALOGI("nativeOnCreate {}", ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1105,11 +1138,6 @@ Java_org_citra_citra_1emu_vr_VrActivity_nativeOnDestroy(JNIEnv* env, jobject thi
|
|||
|
||||
ALOGI("nativeOnDestroy {}", static_cast<long>(handle));
|
||||
if (handle != 0) { delete VRAppHandle(handle).p; }
|
||||
|
||||
// Even though OpenXR is created on a different thread, this
|
||||
// should be ok because thread exit is a fence, and the delete waits to
|
||||
// join.
|
||||
if (gOpenXr != nullptr) { gOpenXr->Shutdown(); }
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
|
|
Loading…
Reference in a new issue