diff options
| -rw-r--r-- | src/android/app/src/main/jni/native.cpp | 231 |
1 files changed, 164 insertions, 67 deletions
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 5d93da237..6a499b97d 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/cpu_manager.h" | 17 | #include "core/cpu_manager.h" |
| 18 | #include "core/file_sys/registered_cache.h" | 18 | #include "core/file_sys/registered_cache.h" |
| 19 | #include "core/file_sys/vfs_real.h" | 19 | #include "core/file_sys/vfs_real.h" |
| 20 | #include "core/hid/hid_core.h" | ||
| 20 | #include "core/hle/service/filesystem/filesystem.h" | 21 | #include "core/hle/service/filesystem/filesystem.h" |
| 21 | #include "core/perf_stats.h" | 22 | #include "core/perf_stats.h" |
| 22 | #include "jni/config.h" | 23 | #include "jni/config.h" |
| @@ -26,10 +27,139 @@ | |||
| 26 | 27 | ||
| 27 | namespace { | 28 | namespace { |
| 28 | 29 | ||
| 29 | ANativeWindow* s_surf{}; | 30 | class EmulationSession final { |
| 30 | std::unique_ptr<EmuWindow_Android> emu_window; | 31 | public: |
| 31 | std::atomic<bool> stop_run{true}; | 32 | EmulationSession() = default; |
| 32 | Core::System system_; | 33 | ~EmulationSession() = default; |
| 34 | |||
| 35 | static EmulationSession& GetInstance() { | ||
| 36 | return s_instance; | ||
| 37 | } | ||
| 38 | |||
| 39 | const Core::System& System() const { | ||
| 40 | return system; | ||
| 41 | } | ||
| 42 | |||
| 43 | Core::System& System() { | ||
| 44 | return system; | ||
| 45 | } | ||
| 46 | |||
| 47 | const EmuWindow_Android& Window() const { | ||
| 48 | return *window; | ||
| 49 | } | ||
| 50 | |||
| 51 | EmuWindow_Android& Window() { | ||
| 52 | return *window; | ||
| 53 | } | ||
| 54 | |||
| 55 | ANativeWindow* NativeWindow() const { | ||
| 56 | return native_window; | ||
| 57 | } | ||
| 58 | |||
| 59 | void SetNativeWindow(ANativeWindow* native_window_) { | ||
| 60 | native_window = native_window_; | ||
| 61 | } | ||
| 62 | |||
| 63 | bool IsRunning() const { | ||
| 64 | return system.IsPoweredOn(); | ||
| 65 | } | ||
| 66 | |||
| 67 | const Core::PerfStatsResults& PerfStats() const { | ||
| 68 | std::scoped_lock perf_stats_lock(perf_stats_mutex); | ||
| 69 | return perf_stats; | ||
| 70 | } | ||
| 71 | |||
| 72 | void SurfaceChanged() { | ||
| 73 | if (!IsRunning()) { | ||
| 74 | return; | ||
| 75 | } | ||
| 76 | window->OnSurfaceChanged(native_window); | ||
| 77 | } | ||
| 78 | |||
| 79 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { | ||
| 80 | std::scoped_lock lock(mutex); | ||
| 81 | |||
| 82 | // Loads the configuration. | ||
| 83 | Config{}; | ||
| 84 | |||
| 85 | // Create the render window. | ||
| 86 | window = std::make_unique<EmuWindow_Android>(&input_subsystem, native_window); | ||
| 87 | |||
| 88 | // Initialize system. | ||
| 89 | system.SetShuttingDown(false); | ||
| 90 | system.Initialize(); | ||
| 91 | system.ApplySettings(); | ||
| 92 | system.HIDCore().ReloadInputDevices(); | ||
| 93 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 94 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||
| 95 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | ||
| 96 | |||
| 97 | // Load the ROM. | ||
| 98 | const Core::SystemResultStatus load_result{system.Load(EmulationSession::GetInstance().Window(), filepath)}; | ||
| 99 | if (load_result != Core::SystemResultStatus::Success) { | ||
| 100 | return load_result; | ||
| 101 | } | ||
| 102 | |||
| 103 | // Complete initialization. | ||
| 104 | system.GPU().Start(); | ||
| 105 | system.GetCpuManager().OnGpuReady(); | ||
| 106 | system.RegisterExitCallback([&] { HaltEmulation(); }); | ||
| 107 | |||
| 108 | return Core::SystemResultStatus::Success; | ||
| 109 | } | ||
| 110 | |||
| 111 | void ShutdownEmulation() { | ||
| 112 | std::scoped_lock lock(mutex); | ||
| 113 | |||
| 114 | // Unload user input. | ||
| 115 | system.HIDCore().UnloadInputDevices(); | ||
| 116 | |||
| 117 | // Shutdown the main emulated process | ||
| 118 | system.DetachDebugger(); | ||
| 119 | system.ShutdownMainProcess(); | ||
| 120 | detached_tasks.WaitForAllTasks(); | ||
| 121 | |||
| 122 | // Tear down the render window. | ||
| 123 | window.reset(); | ||
| 124 | } | ||
| 125 | |||
| 126 | void HaltEmulation() { | ||
| 127 | cv.notify_one(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void RunEmulation() { | ||
| 131 | std::unique_lock lock(mutex); | ||
| 132 | |||
| 133 | void(system.Run()); | ||
| 134 | |||
| 135 | if (system.DebuggerEnabled()) { | ||
| 136 | system.InitializeDebugger(); | ||
| 137 | } | ||
| 138 | |||
| 139 | while (cv.wait_for(lock, std::chrono::seconds (1)) == std::cv_status::timeout) { | ||
| 140 | std::scoped_lock perf_stats_lock(perf_stats_mutex); | ||
| 141 | perf_stats = system.GetAndResetPerfStats(); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | private: | ||
| 146 | static EmulationSession s_instance; | ||
| 147 | |||
| 148 | ANativeWindow* native_window{}; | ||
| 149 | |||
| 150 | InputCommon::InputSubsystem input_subsystem; | ||
| 151 | Common::DetachedTasks detached_tasks; | ||
| 152 | Core::System system; | ||
| 153 | |||
| 154 | Core::PerfStatsResults perf_stats{}; | ||
| 155 | mutable std::mutex perf_stats_mutex; | ||
| 156 | |||
| 157 | std::unique_ptr<EmuWindow_Android> window; | ||
| 158 | std::mutex mutex; | ||
| 159 | std::condition_variable_any cv; | ||
| 160 | }; | ||
| 161 | |||
| 162 | /*static*/ EmulationSession EmulationSession::s_instance; | ||
| 33 | 163 | ||
| 34 | std::string UTF16ToUTF8(std::u16string_view input) { | 164 | std::string UTF16ToUTF8(std::u16string_view input) { |
| 35 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; | 165 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; |
| @@ -56,7 +186,6 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 56 | Common::Log::Initialize(); | 186 | Common::Log::Initialize(); |
| 57 | Common::Log::SetColorConsoleBackendEnabled(true); | 187 | Common::Log::SetColorConsoleBackendEnabled(true); |
| 58 | Common::Log::Start(); | 188 | Common::Log::Start(); |
| 59 | Common::DetachedTasks detached_tasks; | ||
| 60 | 189 | ||
| 61 | MicroProfileOnThreadCreate("EmuThread"); | 190 | MicroProfileOnThreadCreate("EmuThread"); |
| 62 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 191 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| @@ -68,46 +197,13 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 68 | return Core::SystemResultStatus::ErrorLoader; | 197 | return Core::SystemResultStatus::ErrorLoader; |
| 69 | } | 198 | } |
| 70 | 199 | ||
| 71 | // Loads the configuration. | 200 | const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); |
| 72 | Config{}; | 201 | if (result != Core::SystemResultStatus::Success) { |
| 73 | 202 | return result; | |
| 74 | system_.Initialize(); | ||
| 75 | system_.ApplySettings(); | ||
| 76 | |||
| 77 | InputCommon::InputSubsystem input_subsystem{}; | ||
| 78 | |||
| 79 | emu_window = std::make_unique<EmuWindow_Android>(&input_subsystem, s_surf); | ||
| 80 | |||
| 81 | system_.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 82 | system_.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||
| 83 | system_.GetFileSystemController().CreateFactories(*system_.GetFilesystem()); | ||
| 84 | |||
| 85 | const Core::SystemResultStatus load_result{system_.Load(*emu_window, filepath)}; | ||
| 86 | |||
| 87 | if (load_result != Core::SystemResultStatus::Success) { | ||
| 88 | return load_result; | ||
| 89 | } | ||
| 90 | |||
| 91 | system_.GPU().Start(); | ||
| 92 | system_.GetCpuManager().OnGpuReady(); | ||
| 93 | system_.RegisterExitCallback([&] { exit(0); }); | ||
| 94 | |||
| 95 | void(system_.Run()); | ||
| 96 | |||
| 97 | if (system_.DebuggerEnabled()) { | ||
| 98 | system_.InitializeDebugger(); | ||
| 99 | } | ||
| 100 | |||
| 101 | stop_run = false; | ||
| 102 | while (!stop_run) { | ||
| 103 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 104 | } | 203 | } |
| 105 | 204 | ||
| 106 | system_.DetachDebugger(); | 205 | EmulationSession::GetInstance().RunEmulation(); |
| 107 | void(system_.Pause()); | 206 | EmulationSession::GetInstance().ShutdownEmulation(); |
| 108 | system_.ShutdownMainProcess(); | ||
| 109 | |||
| 110 | detached_tasks.WaitForAllTasks(); | ||
| 111 | 207 | ||
| 112 | return Core::SystemResultStatus::Success; | 208 | return Core::SystemResultStatus::Success; |
| 113 | } | 209 | } |
| @@ -117,22 +213,15 @@ extern "C" { | |||
| 117 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, | 213 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, |
| 118 | [[maybe_unused]] jclass clazz, | 214 | [[maybe_unused]] jclass clazz, |
| 119 | jobject surf) { | 215 | jobject surf) { |
| 120 | s_surf = ANativeWindow_fromSurface(env, surf); | 216 | EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); |
| 121 | 217 | EmulationSession::GetInstance().SurfaceChanged(); | |
| 122 | if (emu_window) { | ||
| 123 | emu_window->OnSurfaceChanged(s_surf); | ||
| 124 | } | ||
| 125 | |||
| 126 | LOG_INFO(Frontend, "surface changed"); | ||
| 127 | } | 218 | } |
| 128 | 219 | ||
| 129 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, | 220 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, |
| 130 | [[maybe_unused]] jclass clazz) { | 221 | [[maybe_unused]] jclass clazz) { |
| 131 | ANativeWindow_release(s_surf); | 222 | ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); |
| 132 | s_surf = nullptr; | 223 | EmulationSession::GetInstance().SetNativeWindow(nullptr); |
| 133 | if (emu_window) { | 224 | EmulationSession::GetInstance().SurfaceChanged(); |
| 134 | emu_window->OnSurfaceChanged(s_surf); | ||
| 135 | } | ||
| 136 | } | 225 | } |
| 137 | 226 | ||
| 138 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused]] jclass clazz) {} | 227 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused]] jclass clazz) {} |
| @@ -153,18 +242,22 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation([[maybe_unused]] JNIEn | |||
| 153 | [[maybe_unused]] jclass clazz) {} | 242 | [[maybe_unused]] jclass clazz) {} |
| 154 | 243 | ||
| 155 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation([[maybe_unused]] JNIEnv* env, | 244 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation([[maybe_unused]] JNIEnv* env, |
| 156 | [[maybe_unused]] jclass clazz) {} | 245 | [[maybe_unused]] jclass clazz) { |
| 246 | EmulationSession::GetInstance().HaltEmulation(); | ||
| 247 | } | ||
| 157 | 248 | ||
| 158 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning([[maybe_unused]] JNIEnv* env, | 249 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning([[maybe_unused]] JNIEnv* env, |
| 159 | [[maybe_unused]] jclass clazz) { | 250 | [[maybe_unused]] jclass clazz) { |
| 160 | return static_cast<jboolean>(!stop_run); | 251 | return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); |
| 161 | } | 252 | } |
| 162 | 253 | ||
| 163 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env, | 254 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env, |
| 164 | [[maybe_unused]] jclass clazz, | 255 | [[maybe_unused]] jclass clazz, |
| 165 | [[maybe_unused]] jstring j_device, | 256 | [[maybe_unused]] jstring j_device, |
| 166 | jint j_button, jint action) { | 257 | jint j_button, jint action) { |
| 167 | emu_window->OnGamepadEvent(j_button, action != 0); | 258 | if (EmulationSession::GetInstance().IsRunning()) { |
| 259 | EmulationSession::GetInstance().Window().OnGamepadEvent(j_button, action != 0); | ||
| 260 | } | ||
| 168 | return static_cast<jboolean>(true); | 261 | return static_cast<jboolean>(true); |
| 169 | } | 262 | } |
| 170 | 263 | ||
| @@ -183,7 +276,10 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent([[maybe_unused | |||
| 183 | x /= r; | 276 | x /= r; |
| 184 | y /= r; | 277 | y /= r; |
| 185 | } | 278 | } |
| 186 | emu_window->OnGamepadMoveEvent(x, y); | 279 | |
| 280 | if (EmulationSession::GetInstance().IsRunning()) { | ||
| 281 | EmulationSession::GetInstance().Window().OnGamepadMoveEvent(x, y); | ||
| 282 | } | ||
| 187 | return static_cast<jboolean>(false); | 283 | return static_cast<jboolean>(false); |
| 188 | } | 284 | } |
| 189 | 285 | ||
| @@ -198,13 +294,18 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent([[maybe_unused]] JNI | |||
| 198 | [[maybe_unused]] jclass clazz, | 294 | [[maybe_unused]] jclass clazz, |
| 199 | jfloat x, jfloat y, | 295 | jfloat x, jfloat y, |
| 200 | jboolean pressed) { | 296 | jboolean pressed) { |
| 201 | return static_cast<jboolean>(emu_window->OnTouchEvent(x, y, pressed)); | 297 | if (EmulationSession::GetInstance().IsRunning()) { |
| 298 | return static_cast<jboolean>(EmulationSession::GetInstance().Window().OnTouchEvent(x, y, pressed)); | ||
| 299 | } | ||
| 300 | return static_cast<jboolean>(false); | ||
| 202 | } | 301 | } |
| 203 | 302 | ||
| 204 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, | 303 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, |
| 205 | [[maybe_unused]] jclass clazz, jfloat x, | 304 | [[maybe_unused]] jclass clazz, jfloat x, |
| 206 | jfloat y) { | 305 | jfloat y) { |
| 207 | emu_window->OnTouchMoved(x, y); | 306 | if (EmulationSession::GetInstance().IsRunning()) { |
| 307 | EmulationSession::GetInstance().Window().OnTouchMoved(x, y); | ||
| 308 | } | ||
| 208 | } | 309 | } |
| 209 | 310 | ||
| 210 | jintArray Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon([[maybe_unused]] JNIEnv* env, | 311 | jintArray Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon([[maybe_unused]] JNIEnv* env, |
| @@ -309,8 +410,8 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats([[maybe_unused]] | |||
| 309 | [[maybe_unused]] jclass clazz) { | 410 | [[maybe_unused]] jclass clazz) { |
| 310 | jdoubleArray j_stats = env->NewDoubleArray(4); | 411 | jdoubleArray j_stats = env->NewDoubleArray(4); |
| 311 | 412 | ||
| 312 | if (!stop_run && system_.IsPoweredOn()) { | 413 | if (EmulationSession::GetInstance().IsRunning()) { |
| 313 | const auto results = system_.GetAndResetPerfStats(); | 414 | const auto results = EmulationSession::GetInstance().PerfStats(); |
| 314 | 415 | ||
| 315 | // Converting the structure into an array makes it easier to pass it to the frontend | 416 | // Converting the structure into an array makes it easier to pass it to the frontend |
| 316 | double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, | 417 | double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, |
| @@ -330,10 +431,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2([[maybe_unus | |||
| 330 | jstring j_path) { | 431 | jstring j_path) { |
| 331 | const std::string path = GetJString(env, j_path); | 432 | const std::string path = GetJString(env, j_path); |
| 332 | 433 | ||
| 333 | if (!stop_run) { | ||
| 334 | stop_run = true; | ||
| 335 | } | ||
| 336 | |||
| 337 | const Core::SystemResultStatus result{RunEmulation(path)}; | 434 | const Core::SystemResultStatus result{RunEmulation(path)}; |
| 338 | if (result != Core::SystemResultStatus::Success) { | 435 | if (result != Core::SystemResultStatus::Success) { |
| 339 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | 436 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), |