diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index cecdfa15..0fe0b311 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -5,8 +5,9 @@ #include bool Halt; +jobject Surface; uint FaultCount; -std::mutex jniMtx; +skyline::Mutex jniMtx; void signalHandler(int signal) { syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal)); @@ -54,10 +55,19 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, logger->Info("Done in: {} ms", (std::chrono::duration_cast(end - start).count())); } -extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_lockMutex(JNIEnv *env, jobject instance) { +extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) { jniMtx.lock(); -} - -extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_unlockMutex(JNIEnv *env, jobject instance) { + Halt = halt; + jniMtx.unlock(); +} + +extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) { + jniMtx.lock(); + if(!env->IsSameObject(Surface, nullptr)) + env->DeleteGlobalRef(Surface); + if(!env->IsSameObject(surface, nullptr)) + Surface = env->NewGlobalRef(surface); + else + Surface = surface; jniMtx.unlock(); } diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index 38937a45..872492b3 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -4,6 +4,21 @@ #include namespace skyline { + void Mutex::lock() { + while (flag.exchange(true, std::memory_order_relaxed)); + std::atomic_thread_fence(std::memory_order_acquire); + } + + void Mutex::unlock() { + std::atomic_thread_fence(std::memory_order_release); + flag.store(false, std::memory_order_relaxed); + } + + bool Mutex::try_lock() { + bool fal = false; + return flag.compare_exchange_strong(fal, true, std::memory_order_relaxed); + } + Settings::Settings(const int preferenceFd) { tinyxml2::XMLDocument pref; if (pref.LoadFile(fdopen(preferenceFd, "r"))) diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 999bdf21..e3715439 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -5,6 +5,7 @@ #include #include #include +#import #include #include #include @@ -113,7 +114,31 @@ namespace skyline { enum class Sreg { Sp, Pc, PState }; /** - * @brief The Logger class is to write log output + * @brief The Mutex class is a wrapper around an atomic bool used for synchronization + */ + class Mutex { + std::atomic flag{false}; //!< This atomic bool holds the status of the lock + + public: + /** + * @brief Wait on and lock the mutex + */ + void lock(); + + /** + * @brief Lock the mutex if it is unlocked else return + * @return If the mutex was successfully locked or not + */ + bool try_lock(); + + /** + * @brief Unlock the mutex if it is held by this thread + */ + void unlock(); + }; + + /** + * @brief The Logger class is to write log output to file and logcat */ class Logger { private: diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index 99796ba7..09ba3008 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -8,9 +8,10 @@ #include extern bool Halt; +extern jobject Surface; namespace skyline::gpu { - GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;"))), bufferQueue(state), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { + GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface)), bufferQueue(state), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { ANativeWindow_acquire(window); resolution.width = static_cast(ANativeWindow_getWidth(window)); resolution.height = static_cast(ANativeWindow_getHeight(window)); @@ -23,16 +24,16 @@ namespace skyline::gpu { void GPU::Loop() { if (surfaceUpdate) { - if (!state.jvmManager->CheckNull("surface", "Landroid/view/Surface;")) { - window = ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;")); - ANativeWindow_acquire(window); - resolution.width = static_cast(ANativeWindow_getWidth(window)); - resolution.height = static_cast(ANativeWindow_getHeight(window)); - format = ANativeWindow_getFormat(window); - } else + if (state.jvmManager->CheckNull(Surface)) return; + window = ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface); + ANativeWindow_acquire(window); + resolution.width = static_cast(ANativeWindow_getWidth(window)); + resolution.height = static_cast(ANativeWindow_getHeight(window)); + format = ANativeWindow_getFormat(window); + surfaceUpdate = true; } else - surfaceUpdate = state.jvmManager->CheckNull("surface", "Landroid/view/Surface;"); + surfaceUpdate = state.jvmManager->CheckNull(Surface); if (!bufferQueue.displayQueue.empty()) { auto &buffer = bufferQueue.displayQueue.front(); bufferQueue.displayQueue.pop(); diff --git a/app/src/main/cpp/skyline/jvm.cpp b/app/src/main/cpp/skyline/jvm.cpp index 9f6fb15c..d14901a1 100644 --- a/app/src/main/cpp/skyline/jvm.cpp +++ b/app/src/main/cpp/skyline/jvm.cpp @@ -1,7 +1,27 @@ #include "jvm.h" +thread_local JNIEnv *env; + namespace skyline { - JvmManager::JvmManager(JNIEnv *env, jobject instance) : env(env), instance(instance), instanceClass(env->GetObjectClass(instance)) {} + JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast(environ->NewGlobalRef(environ->GetObjectClass(instance)))) { + env = environ; + if(env->GetJavaVM(&vm) < 0) + throw exception("Cannot get JavaVM from environment"); + } + + void JvmManager::AttachThread() { + if(!env) + vm->AttachCurrentThread(&env, nullptr); + } + + void JvmManager::DetachThread() { + if(env) + vm->DetachCurrentThread(); + } + + JNIEnv* JvmManager::GetEnv() { + return env; + } jobject JvmManager::GetField(const char *key, const char *signature) { return env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)); @@ -10,4 +30,8 @@ namespace skyline { bool JvmManager::CheckNull(const char *key, const char *signature) { return env->IsSameObject(env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)), nullptr); } + + bool JvmManager::CheckNull(jobject& object) { + return env->IsSameObject(object, nullptr); + } } diff --git a/app/src/main/cpp/skyline/jvm.h b/app/src/main/cpp/skyline/jvm.h index aa418841..ebf31d1a 100644 --- a/app/src/main/cpp/skyline/jvm.h +++ b/app/src/main/cpp/skyline/jvm.h @@ -9,7 +9,7 @@ namespace skyline { */ class JvmManager { public: - JNIEnv *env; //!< A pointer to the JNI environment + JavaVM *vm{}; //!< A pointer to the Java VM jobject instance; //!< A reference to the activity jclass instanceClass; //!< The class of the activity @@ -19,6 +19,21 @@ namespace skyline { */ JvmManager(JNIEnv *env, jobject instance); + /** + * @brief Attach the current thread to the Java VM + */ + void AttachThread(); + + /** + * @brief Detach the current thread to the Java VM + */ + void DetachThread(); + + /** + * @brief Returns a pointer to the JNI environment for the current thread + */ + JNIEnv* GetEnv(); + /** * @brief Retrieves a specific field of the given type from the activity * @tparam objectType The type of the object in the field @@ -27,6 +42,7 @@ namespace skyline { */ template inline objectType GetField(const char *key) { + JNIEnv *env = GetEnv(); if constexpr(std::is_same()) return env->GetBooleanField(instance, env->GetFieldID(instanceClass, key, "Z")); else if constexpr(std::is_same()) @@ -60,5 +76,12 @@ namespace skyline { * @return If the field is null or not */ bool CheckNull(const char *key, const char *signature); + + /** + * @brief Checks if a specific jobject is null or not + * @param object The jobject to check + * @return If the object is null or not + */ + bool CheckNull(jobject &object); }; } diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 616fb57f..e65e3016 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -6,10 +6,9 @@ #include "guest.h" extern bool Halt; -extern std::mutex jniMtx; +extern skyline::Mutex jniMtx; namespace skyline { - namespace instr { /** * @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction. @@ -205,7 +204,7 @@ namespace skyline { void NCE::Execute() { int status = 0; while (!Halt && !state.os->processMap.empty()) { - jniMtx.lock(); + std::lock_guard jniGd(jniMtx); for (const auto &process : state.os->processMap) { state.os->thisProcess = process.second; state.os->thisThread = process.second->threadMap.at(process.first); @@ -263,8 +262,6 @@ namespace skyline { } state.os->serviceManager.Loop(); state.gpu->Loop(); - Halt = state.jvmManager->GetField("halt"); - jniMtx.unlock(); } for (const auto &process : state.os->processMap) { state.os->KillThread(process.first); @@ -406,8 +403,7 @@ namespace skyline { u32 *end = address + (code.size() / sizeof(u32)); i64 patchOffset = offset; - std::vector patch; - patch.resize((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32)); + std::vector patch((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32)); std::memcpy(patch.data(), reinterpret_cast(&guest::saveCtx), guest::saveCtxSize); offset += guest::saveCtxSize; diff --git a/app/src/main/java/emu/skyline/GameActivity.kt b/app/src/main/java/emu/skyline/GameActivity.kt index 82bf7036..188b0ed3 100644 --- a/app/src/main/java/emu/skyline/GameActivity.kt +++ b/app/src/main/java/emu/skyline/GameActivity.kt @@ -24,13 +24,12 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal private lateinit var preferenceFd: ParcelFileDescriptor private lateinit var logFd: ParcelFileDescriptor private var surface: Surface? = null - private var inputQueue: Long? = null + private var inputQueue: Long = 0L private lateinit var gameThread: Thread - private var halt: Boolean = false private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int) - private external fun lockMutex() - private external fun unlockMutex() + private external fun setHalt(halt: Boolean) + private external fun setSurface(surface: Surface?) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -56,7 +55,7 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal override fun onDestroy() { super.onDestroy() - halt = true + setHalt(true) gameThread.join() romFd.close() preferenceFd.close() @@ -65,9 +64,8 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal override fun surfaceCreated(holder: SurfaceHolder?) { Log.d("surfaceCreated", "Holder: ${holder.toString()}") - lockMutex() surface = holder!!.surface - unlockMutex() + setSurface(surface) } override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { @@ -76,9 +74,8 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal override fun surfaceDestroyed(holder: SurfaceHolder?) { Log.d("surfaceDestroyed", "Holder: ${holder.toString()}") - lockMutex() surface = null - unlockMutex() + setSurface(surface) } override fun onInputQueueCreated(queue: InputQueue?) { @@ -86,10 +83,12 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal val clazz = Class.forName("android.view.InputQueue") val method: Method = clazz.getMethod("getNativePtr") inputQueue = method.invoke(queue)!! as Long + //setQueue(inputQueue) } override fun onInputQueueDestroyed(queue: InputQueue?) { Log.d("onInputQueueDestroyed", "InputQueue: ${queue.toString()}") - inputQueue = null + inputQueue = 0L + //setQueue(inputQueue) } } diff --git a/build.gradle b/build.gradle index 5d423337..e546a246 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong