mirror of
https://github.com/amwatson/CitraVR.git
synced 2024-09-19 19:01:38 +02:00
add JNIClassNames so that FindClass isn't used at runtime (this was a bug introduced by the upstream merge that seems to affect some quest users)
This commit is contained in:
parent
84a2725906
commit
289ce5de91
10 changed files with 108 additions and 76 deletions
|
@ -149,7 +149,7 @@ android {
|
|||
productFlavors {
|
||||
create("canary") {
|
||||
dimension = "version"
|
||||
applicationIdSuffix = ".canary"
|
||||
applicationIdSuffix = ".playtest"
|
||||
}
|
||||
|
||||
create("nightly") {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "jni/applets/swkbd.h"
|
||||
#include "jni/camera/still_image_camera.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "vr/utils/JniClassNames.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
|
@ -254,6 +255,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||
SoftwareKeyboard::InitJNI(env);
|
||||
Camera::StillImage::InitJNI(env);
|
||||
AndroidStorage::InitJNI(env, s_native_library_class);
|
||||
VR::JNI::InitJNI(env);
|
||||
|
||||
return JNI_VERSION;
|
||||
}
|
||||
|
|
|
@ -425,7 +425,7 @@ void GameSurfaceLayer::SetTopPanelFromController(const XrVector3f& controllerPos
|
|||
// Set the initial distance of the window from the viewer.
|
||||
const float sphereRadius =
|
||||
XrMath::Vector3f::Length(mTopPanel.mPanelFromWorld.position - viewerPosition);
|
||||
XrVector3f windowPosition =
|
||||
const XrVector3f windowPosition =
|
||||
CalculatePanelPosition(viewerPosition, controllerPosition, sphereRadius);
|
||||
if (windowPosition.z >= -0.5f) { return; }
|
||||
if (XrMath::Vector3f::LengthSq(windowPosition - mLowerPanel.mPanelFromWorld.position) <
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "RibbonLayer.h"
|
||||
|
||||
#include "../utils/JniClassNames.h"
|
||||
#include "../utils/LogUtils.h"
|
||||
#include "../utils/XrMath.h"
|
||||
|
||||
|
@ -38,8 +39,8 @@ XrQuaternionf CalculatePanelRotation(const XrVector3f& windowPosition,
|
|||
|
||||
RibbonLayer::RibbonLayer(const XrVector3f&& position, const XrQuaternionf&& orientation,
|
||||
JNIEnv* jni, jobject activityObject, const XrSession& session)
|
||||
: UILayer("org/citra/citra_emu/vr/ui/VrRibbonLayer", std::move(position),
|
||||
std::move(orientation), jni, activityObject, session)
|
||||
: UILayer(VR::JniGlobalRef::gVrRibbonLayerClass, std::move(position), std::move(orientation),
|
||||
jni, activityObject, session)
|
||||
, mInitialPose({orientation, position}) {
|
||||
mIsMenuBackgroundSelectedMethodId =
|
||||
jni->GetMethodID(GetVrUILayerClass(), "isMenuBackgroundSelected", "()Z");
|
||||
|
|
|
@ -159,16 +159,15 @@ XrVector2f GetDensityScaleForSize(const int32_t texWidth, const int32_t texHeigh
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
UILayer::UILayer(const std::string& className, const XrVector3f&& position,
|
||||
UILayer::UILayer(const jclass classObject, const XrVector3f&& position,
|
||||
const XrQuaternionf&& orientation, JNIEnv* env, jobject activityObject,
|
||||
const XrSession& session)
|
||||
: mPanelFromWorld(XrPosef{orientation, position})
|
||||
, mSession(session)
|
||||
, mEnv(env) {
|
||||
const int32_t initializationStatus = Init(className, activityObject, position, session);
|
||||
const int32_t initializationStatus = Init(classObject, activityObject, position, session);
|
||||
if (initializationStatus < 0) {
|
||||
FAIL("Could not initialize UILayer(%s) -- error '%d'", className.c_str(),
|
||||
initializationStatus);
|
||||
FAIL("Could not initialize %s() -- error '%d'", __FUNCTION__, initializationStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,10 +214,9 @@ bool UILayer::GetRayIntersectionWithPanel(const XrVector3f& start,
|
|||
}
|
||||
|
||||
// Next error code: -8
|
||||
int32_t UILayer::Init(const std::string& className, const jobject activityObject,
|
||||
int32_t UILayer::Init(const jclass classObject, const jobject activityObject,
|
||||
const XrVector3f& position, const XrSession& session) {
|
||||
mVrUILayerClass = JniUtils::GetGlobalClassReference(mEnv, activityObject, className.c_str());
|
||||
BAIL_ON_COND(mVrUILayerClass == nullptr, "No java UI Layer class", -1);
|
||||
mVrUILayerClass = classObject;
|
||||
|
||||
jmethodID vrUILayerConstructor =
|
||||
mEnv->GetMethodID(mVrUILayerClass, "<init>", "(Lorg/citra/citra_emu/vr/VrActivity;)V");
|
||||
|
|
|
@ -65,8 +65,10 @@ class UILayer {
|
|||
|
||||
public:
|
||||
/** Constructor.
|
||||
* @param className: the class name of the Java class representing the UI layer.
|
||||
* @param classObject: the class object of the Java class representing the UI layer.
|
||||
* This class should subclass org.citra.citra_emu.ui.VrUILayer.
|
||||
* We do it this way because after the v4 update with quest v64, FindClass() returns
|
||||
* null on the JNI threads (even with the original classloader).
|
||||
* @param position: position of the layer
|
||||
* @param orientation: orientation of the layer
|
||||
* @param jni: the JNI environment. Should be attached to the current thread
|
||||
|
@ -74,7 +76,7 @@ public:
|
|||
* the class information for UILayer
|
||||
* @param session a valid XrSession
|
||||
*/
|
||||
UILayer(const std::string& className, const XrVector3f&& position,
|
||||
UILayer(const jclass classObject, const XrVector3f&& position,
|
||||
const XrQuaternionf&& orientation, JNIEnv* jni, jobject activityObject,
|
||||
const XrSession& session);
|
||||
~UILayer();
|
||||
|
@ -132,8 +134,8 @@ protected:
|
|||
XrPosef mPanelFromWorld;
|
||||
|
||||
private:
|
||||
int Init(const std::string& className, const jobject activityObject, const XrVector3f& position,
|
||||
const XrSession& session);
|
||||
int Init(const jclass classObject, const jobject activityObject, const XrVector3f& position,
|
||||
const XrSession& session);
|
||||
void Shutdown();
|
||||
|
||||
/** Creates the swapchain.
|
||||
|
|
|
@ -6,42 +6,36 @@
|
|||
|
||||
namespace VR {
|
||||
namespace JniGlobalRef {
|
||||
jmethodID gFindClassMethodID = nullptr;
|
||||
jobject gClassLoader = nullptr;
|
||||
jclass gVrKeyboardLayerClass = nullptr;
|
||||
jclass gVrErrorMessageLayerClass = nullptr;
|
||||
jclass gVrRibbonLayerClass = nullptr;
|
||||
} // namespace JniGlobalRef
|
||||
} // namespace VR
|
||||
|
||||
void VR::JNI::InitJNI(JNIEnv* jni, jobject activityObject) {
|
||||
void VR::JNI::InitJNI(JNIEnv* jni) {
|
||||
assert(jni != nullptr);
|
||||
const jclass activityClass = jni->GetObjectClass(activityObject);
|
||||
if (activityClass == nullptr) { FAIL("Failed to get activity class"); }
|
||||
|
||||
// Get the getClassLoader method ID
|
||||
const jmethodID getClassLoaderMethod =
|
||||
jni->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
if (getClassLoaderMethod == nullptr) { FAIL("Failed to get getClassLoader method ID"); }
|
||||
VR::JniGlobalRef::gVrKeyboardLayerClass = static_cast<jclass>(
|
||||
jni->NewGlobalRef(jni->FindClass("org/citra/citra_emu/vr/ui/VrKeyboardLayer")));
|
||||
if (VR::JniGlobalRef::gVrKeyboardLayerClass == nullptr) {
|
||||
FAIL("Could not find VrKeyboardLayer class");
|
||||
}
|
||||
VR::JniGlobalRef::gVrErrorMessageLayerClass = static_cast<jclass>(
|
||||
jni->NewGlobalRef(jni->FindClass("org/citra/citra_emu/vr/ui/VrErrorMessageLayer")));
|
||||
if (VR::JniGlobalRef::gVrErrorMessageLayerClass == nullptr) {
|
||||
FAIL("Could not find VrErrorMessageLayer class");
|
||||
}
|
||||
|
||||
// Call getClassLoader of the activity object to obtain the class loader
|
||||
const jobject classLoaderObject = jni->CallObjectMethod(activityObject, getClassLoaderMethod);
|
||||
if (classLoaderObject == nullptr) { FAIL("Failed to get class loader object"); }
|
||||
|
||||
JniGlobalRef::gClassLoader = jni->NewGlobalRef(classLoaderObject);
|
||||
|
||||
// Step 3: Cache the findClass method ID
|
||||
jclass classLoaderClass = jni->FindClass("java/lang/ClassLoader");
|
||||
if (classLoaderClass == nullptr) { FAIL("Failed to find class loader class"); }
|
||||
JniGlobalRef::gFindClassMethodID =
|
||||
jni->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (JniGlobalRef::gFindClassMethodID == nullptr) { FAIL("Failed to get findClass method ID"); }
|
||||
|
||||
// Cleanup local references
|
||||
jni->DeleteLocalRef(activityClass);
|
||||
jni->DeleteLocalRef(classLoaderClass);
|
||||
VR::JniGlobalRef::gVrRibbonLayerClass = static_cast<jclass>(
|
||||
jni->NewGlobalRef(jni->FindClass("org/citra/citra_emu/vr/ui/VrRibbonLayer")));
|
||||
if (VR::JniGlobalRef::gVrRibbonLayerClass == nullptr) {
|
||||
FAIL("Could not find VrRibbonLayer class");
|
||||
}
|
||||
}
|
||||
|
||||
void VR::JNI::CleanupJNI(JNIEnv* jni) {
|
||||
assert(jni != nullptr);
|
||||
if (JniGlobalRef::gClassLoader != nullptr) { jni->DeleteGlobalRef(JniGlobalRef::gClassLoader); }
|
||||
JniGlobalRef::gClassLoader = nullptr;
|
||||
JniGlobalRef::gFindClassMethodID = nullptr;
|
||||
VR::JniGlobalRef::gVrKeyboardLayerClass = nullptr;
|
||||
VR::JniGlobalRef::gVrErrorMessageLayerClass = nullptr;
|
||||
VR::JniGlobalRef::gVrRibbonLayerClass = nullptr;
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
|
||||
namespace VR {
|
||||
namespace JniGlobalRef {
|
||||
extern jmethodID gFindClassMethodID;
|
||||
extern jobject gClassLoader;
|
||||
extern jclass gVrKeyboardLayerClass;
|
||||
extern jclass gVrErrorMessageLayerClass;
|
||||
extern jclass gVrRibbonLayerClass;
|
||||
|
||||
} // namespace JniGlobalRef
|
||||
|
||||
namespace JNI {
|
||||
// Called during JNI_OnLoad
|
||||
void InitJNI(JNIEnv* env, jobject activityObject);
|
||||
void InitJNI(JNIEnv* env);
|
||||
// Called during JNI_OnUnload
|
||||
void CleanupJNI(JNIEnv* env);
|
||||
} // namespace JNI
|
||||
|
|
|
@ -12,39 +12,74 @@ License : Licensed under GPLv3 or any later version.
|
|||
|
||||
#include "JniUtils.h"
|
||||
|
||||
#include "JniClassNames.h"
|
||||
#include "LogUtils.h"
|
||||
|
||||
jclass JniUtils::GetGlobalClassReference(JNIEnv* env, jobject activityObject,
|
||||
jclass JniUtils::GetGlobalClassReference(JNIEnv* jni, jobject activityObject,
|
||||
const std::string& className) {
|
||||
// Convert dot ('.') to slash ('/') in class name (Java uses dots, JNI uses slashes for class
|
||||
// names)
|
||||
std::string correctedClassName = className;
|
||||
std::replace(correctedClassName.begin(), correctedClassName.end(), '.', '/');
|
||||
|
||||
// Convert std::string to jstring
|
||||
jstring classNameJString = env->NewStringUTF(correctedClassName.c_str());
|
||||
|
||||
// Use the global class loader to find the class
|
||||
jclass clazz = static_cast<jclass>(env->CallObjectMethod(
|
||||
VR::JniGlobalRef::gClassLoader, VR::JniGlobalRef::gFindClassMethodID, classNameJString));
|
||||
if (clazz == nullptr) {
|
||||
// Class not found
|
||||
ALOGE("Class not found: {}", correctedClassName.c_str());
|
||||
// First, get the class object of the activity to get its class loader
|
||||
const jclass activityClass = jni->GetObjectClass(activityObject);
|
||||
if (activityClass == nullptr) {
|
||||
ALOGE("Failed to get activity class");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Clean up the local reference to the class name jstring
|
||||
env->DeleteLocalRef(classNameJString);
|
||||
|
||||
// Check for exceptions and handle them. This is crucial to prevent crashes due to uncaught
|
||||
// exceptions.
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return nullptr; // Class not found or other issue
|
||||
// Get the getClassLoader method ID
|
||||
const jmethodID getClassLoaderMethod =
|
||||
jni->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
if (getClassLoaderMethod == nullptr) {
|
||||
ALOGE("Failed to get getClassLoader method ID");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Return a global reference to the class
|
||||
return static_cast<jclass>(env->NewGlobalRef(clazz));
|
||||
// Call getClassLoader of the activity object to obtain the class loader
|
||||
const jobject classLoaderObject = jni->CallObjectMethod(activityObject, getClassLoaderMethod);
|
||||
if (classLoaderObject == nullptr) {
|
||||
ALOGE("Failed to get class loader object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the class loader class
|
||||
const jclass classLoaderClass = jni->FindClass("java/lang/ClassLoader");
|
||||
if (classLoaderClass == nullptr) {
|
||||
ALOGE("Failed to get class loader class");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the findClass method ID from the class loader class
|
||||
const jmethodID findClassMethod =
|
||||
jni->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (findClassMethod == nullptr) {
|
||||
ALOGE("Failed to get findClass method ID");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Convert the class name string to a jstring
|
||||
const jstring javaClassName = jni->NewStringUTF(className.c_str());
|
||||
if (javaClassName == nullptr) {
|
||||
ALOGE("Failed to convert class name to jstring");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Call findClass on the class loader object with the class name
|
||||
const jclass classToFind = static_cast<jclass>(
|
||||
jni->CallObjectMethod(classLoaderObject, findClassMethod, javaClassName));
|
||||
|
||||
// Clean up local references
|
||||
jni->DeleteLocalRef(activityClass);
|
||||
jni->DeleteLocalRef(classLoaderObject);
|
||||
jni->DeleteLocalRef(classLoaderClass);
|
||||
jni->DeleteLocalRef(javaClassName);
|
||||
|
||||
if (classToFind == nullptr) {
|
||||
// Handle error (Class not found)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a global reference to the class
|
||||
const jclass globalClassRef = reinterpret_cast<jclass>(jni->NewGlobalRef(classToFind));
|
||||
|
||||
// Clean up the local reference of the class
|
||||
jni->DeleteLocalRef(classToFind);
|
||||
|
||||
return globalClassRef;
|
||||
}
|
||||
|
|
|
@ -301,12 +301,12 @@ private:
|
|||
gOpenXr->mSession);
|
||||
|
||||
mKeyboardLayer = std::make_unique<UILayer>(
|
||||
"org/citra/citra_emu/vr/ui/VrKeyboardLayer", XrVector3f{0, -0.4f, -0.5f},
|
||||
VR::JniGlobalRef::gVrKeyboardLayerClass, XrVector3f{0, -0.4f, -0.5f},
|
||||
XrMath::Quatf::FromEuler(-MATH_FLOAT_PI / 4.0f, 0.0f, 0.0f), jni, mActivityObject,
|
||||
gOpenXr->mSession);
|
||||
|
||||
mErrorMessageLayer = std::make_unique<UILayer>(
|
||||
"org/citra/citra_emu/vr/ui/VrErrorMessageLayer", XrVector3f{0, -0.1f, -1.0f},
|
||||
VR::JniGlobalRef::gVrErrorMessageLayerClass, XrVector3f{0, -0.1f, -1.0f},
|
||||
XrQuaternionf{0, 0, 0, 1}, jni, mActivityObject, gOpenXr->mSession);
|
||||
|
||||
// Create the cursor layer.
|
||||
|
@ -1256,7 +1256,6 @@ Java_org_citra_citra_1emu_vr_VrActivity_nativeOnCreate(JNIEnv* env, jobject thiz
|
|||
// time to first frame.
|
||||
gOnCreateStartTime = std::chrono::steady_clock::now();
|
||||
|
||||
VR::JNI::InitJNI(env, thiz);
|
||||
JavaVM* jvm;
|
||||
env->GetJavaVM(&jvm);
|
||||
auto ret = VRAppHandle(new VRAppThread(jvm, env, thiz)).l;
|
||||
|
|
Loading…
Reference in a new issue