diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index 6c991765..dfb42920 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -2,8 +2,8 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include +#include #include -#include #include #include "skyline/loader/loader.h" #include "skyline/common.h" @@ -21,8 +21,7 @@ std::weak_ptr GpuWeak; std::weak_ptr InputWeak; extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) { - Fps = 0; - FrameTime = 0; + Fps = FrameTime = 0; pthread_setname_np(pthread_self(), "EmuMain"); @@ -65,31 +64,39 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication( close(romFd); } -extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject) { +extern "C" JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject) { auto os{OsWeak.lock()}; - while (!os) - os = OsWeak.lock(); + if (!os) + return false; auto process{os->state.process}; - while (!process) { - __sync_synchronize(); - process = os->state.process; - } + if (!process) + return false; process->Kill(true, false, true); + return true; } -extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *, jobject, jobject surface) { +extern "C" JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *, jobject, jobject surface) { auto gpu{GpuWeak.lock()}; - while (!gpu) - gpu = GpuWeak.lock(); + if (!gpu) + return false; gpu->presentation.UpdateSurface(surface); + return true; } -extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *, jobject) { - return Fps; -} +extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceStatistics(JNIEnv *env, jobject thiz) { + static jclass clazz{}; + if (!clazz) + clazz = env->GetObjectClass(thiz); -extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *, jobject) { - return static_cast(FrameTime) / 100; + static jfieldID fpsField{}; + if (!fpsField) + fpsField = env->GetFieldID(clazz, "fps", "I"); + env->SetIntField(thiz, fpsField, Fps); + + static jfieldID frametimeField{}; + if (!frametimeField) + frametimeField = env->GetFieldID(clazz, "frametime", "F"); + env->SetFloatField(thiz, frametimeField, static_cast(FrameTime) / 100); } extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) { diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 2f18c5b2..0ac096a5 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -132,7 +132,7 @@ namespace skyline { } /** - * @brief A way to implicitly convert a pointer to size_t and leave it unaffected if it isn't a pointer + * @brief A way to implicitly convert a pointer to uintptr_t and leave it unaffected if it isn't a pointer */ template T PointerValue(T item) { @@ -144,6 +144,17 @@ namespace skyline { return reinterpret_cast(item); } + /** + * @brief A way to implicitly convert an integral to a pointer, if the return type is a pointer + */ + template + Return ValuePointer(T item) { + if constexpr (std::is_pointer::value) + return reinterpret_cast(item); + else + return item; + } + /** * @return The value aligned up to the next multiple * @note The multiple needs to be a power of 2 @@ -151,7 +162,7 @@ namespace skyline { template constexpr TypeVal AlignUp(TypeVal value, TypeMul multiple) { multiple--; - return (PointerValue(value) + multiple) & ~(multiple); + return ValuePointer((PointerValue(value) + multiple) & ~(multiple)); } /** @@ -160,7 +171,7 @@ namespace skyline { */ template constexpr TypeVal AlignDown(TypeVal value, TypeMul multiple) { - return PointerValue(value) & ~(multiple - 1); + return ValuePointer(PointerValue(value) & ~(multiple - 1)); } /** diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index da5a3bef..9fd18d48 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -59,7 +59,7 @@ namespace skyline::nce { constexpr u16 instructionCount{20}; // The amount of previous instructions to print auto offset{ctx.pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))}; span instructions(reinterpret_cast(offset), instructionCount); - if (mprotect(instructions.data(), instructions.size_bytes(), PROT_READ | PROT_WRITE | PROT_EXEC) == 0) { + if (mprotect(util::AlignDown(instructions.data(), PAGE_SIZE), util::AlignUp(instructions.size_bytes(), PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC) == 0) { for (auto &instruction : instructions) { instruction = __builtin_bswap32(instruction); @@ -75,7 +75,7 @@ namespace skyline::nce { state.logger->Debug("Process Trace:{}", trace); state.logger->Debug("Raw Instructions: 0x{}", raw); } else { - cpuContext += fmt::format("\nPC: 0x{:X}", ctx.pc); + cpuContext += fmt::format("\nPC: 0x{:X} ('mprotect' failed with '{}')", ctx.pc, strerror(errno)); } if (ctx.fault_address) @@ -85,7 +85,7 @@ namespace skyline::nce { cpuContext += fmt::format("\nStack Pointer: 0x{:X}", ctx.sp); for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2) - cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]); + cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? 'X' : '\0', index + 1, ctx.regs[index]); state.logger->Debug("CPU Context:{}", cpuContext); } diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 18a1dca9..bb8304a2 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -68,26 +68,25 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String) /** - * Terminate of all emulator threads and cause [emulationThread] to return + * @return If it successfully caused the [emulationThread] to gracefully stop */ - private external fun stopEmulation() + private external fun stopEmulation() : Boolean /** * This sets the surface object in libskyline to the provided value, emulation is halted if set to null * * @param surface The value to set surface to + * @return If the value was successfully set */ - private external fun setSurface(surface : Surface?) + private external fun setSurface(surface : Surface?) : Boolean + + var fps : Int = 0 + var frametime : Float = 0.0f /** - * This returns the current FPS of the application + * Writes the current performance statistics into [fps] and [frametime] fields */ - private external fun getFps() : Int - - /** - * This returns the current frame-time of the application - */ - private external fun getFrametime() : Float + private external fun updatePerformanceStatistics() /** * This initializes a guest controller in libskyline @@ -170,7 +169,10 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo emulationThread = Thread { executeApplication(rom.toString(), romType, romFd.detachFd(), preferenceFd.detachFd(), applicationContext.filesDir.canonicalPath + "/") if (shouldFinish) - runOnUiThread { finish() } + runOnUiThread { + emulationThread.join() + finish() + } } emulationThread.start() @@ -203,7 +205,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo if (settings.perfStats) { perf_stats.postDelayed(object : Runnable { override fun run() { - perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms" + updatePerformanceStatistics() + perf_stats.text = "$fps FPS\n${frametime}ms" perf_stats.postDelayed(this, 250) } }, 250) @@ -234,8 +237,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo super.onNewIntent(intent) shouldFinish = false - stopEmulation() - emulationThread.join() + while (emulationThread.isAlive) + if (stopEmulation()) + emulationThread.join() shouldFinish = true @@ -246,8 +250,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo super.onDestroy() shouldFinish = false - stopEmulation() - emulationThread.join() + while (emulationThread.isAlive) + if (stopEmulation()) + emulationThread.join() vibrators.forEach { (_, vibrator) -> vibrator.cancel() } vibrators.clear() @@ -259,7 +264,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo override fun surfaceCreated(holder : SurfaceHolder) { Log.d(Tag, "surfaceCreated Holder: $holder") surface = holder.surface - setSurface(surface) + while (emulationThread.isAlive) + if (setSurface(surface)) + return } /** @@ -275,7 +282,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo override fun surfaceDestroyed(holder : SurfaceHolder) { Log.d(Tag, "surfaceDestroyed Holder: $holder") surface = null - setSurface(surface) + while (emulationThread.isAlive) + if (setSurface(surface)) + return } /**