diff options
| author | bunnei <bunneidev@gmail.com> | 2023-01-22 16:56:40 -0800 | 
|---|---|---|
| committer | bunnei <bunneidev@gmail.com> | 2023-06-03 00:05:28 -0700 | 
| commit | 1e94d16dadd5453fbc4b2f9fc44256662a2e9fcf (patch) | |
| tree | 8f81714b704cf611b2944e6820f62bc942d42458 | |
| parent | 6cc21a56d96ce2ace61369dbff77ebbe682b2b05 (diff) | |
android: jni: native: Consolidate emulation state into EmulationSession singleton.
- Fixes state management issues across multiple boots.
- Fixes crashes related to unsafe access of perf stats.
| -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 @@  #include "core/cpu_manager.h"  #include "core/file_sys/registered_cache.h"  #include "core/file_sys/vfs_real.h" +#include "core/hid/hid_core.h"  #include "core/hle/service/filesystem/filesystem.h"  #include "core/perf_stats.h"  #include "jni/config.h" @@ -26,10 +27,139 @@  namespace { -ANativeWindow* s_surf{}; -std::unique_ptr<EmuWindow_Android> emu_window; -std::atomic<bool> stop_run{true}; -Core::System system_; +class EmulationSession final { +public: +    EmulationSession() = default; +    ~EmulationSession() = default; + +    static EmulationSession& GetInstance() { +        return s_instance; +    } + +    const Core::System& System() const { +        return system; +    } + +    Core::System& System() { +        return system; +    } + +    const EmuWindow_Android& Window() const { +        return *window; +    } + +    EmuWindow_Android& Window() { +        return *window; +    } + +    ANativeWindow* NativeWindow() const { +        return native_window; +    } + +    void SetNativeWindow(ANativeWindow* native_window_) { +        native_window = native_window_; +    } + +    bool IsRunning() const { +        return system.IsPoweredOn(); +    } + +    const Core::PerfStatsResults& PerfStats() const { +        std::scoped_lock perf_stats_lock(perf_stats_mutex); +        return perf_stats; +    } + +    void SurfaceChanged() { +        if (!IsRunning()) { +            return; +        } +        window->OnSurfaceChanged(native_window); +    } + +    Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { +        std::scoped_lock lock(mutex); + +        // Loads the configuration. +        Config{}; + +        // Create the render window. +        window = std::make_unique<EmuWindow_Android>(&input_subsystem, native_window); + +        // Initialize system. +        system.SetShuttingDown(false); +        system.Initialize(); +        system.ApplySettings(); +        system.HIDCore().ReloadInputDevices(); +        system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); +        system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); +        system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); + +        // Load the ROM. +        const Core::SystemResultStatus load_result{system.Load(EmulationSession::GetInstance().Window(), filepath)}; +        if (load_result != Core::SystemResultStatus::Success) { +            return load_result; +        } + +        // Complete initialization. +        system.GPU().Start(); +        system.GetCpuManager().OnGpuReady(); +        system.RegisterExitCallback([&] { HaltEmulation(); }); + +        return Core::SystemResultStatus::Success; +    } + +    void ShutdownEmulation() { +        std::scoped_lock lock(mutex); + +        // Unload user input. +        system.HIDCore().UnloadInputDevices(); + +        // Shutdown the main emulated process +        system.DetachDebugger(); +        system.ShutdownMainProcess(); +        detached_tasks.WaitForAllTasks(); + +        // Tear down the render window. +        window.reset(); +    } + +    void HaltEmulation() { +        cv.notify_one(); +    } + +    void RunEmulation() { +        std::unique_lock lock(mutex); + +        void(system.Run()); + +        if (system.DebuggerEnabled()) { +            system.InitializeDebugger(); +        } + +        while (cv.wait_for(lock, std::chrono::seconds (1)) == std::cv_status::timeout) { +            std::scoped_lock perf_stats_lock(perf_stats_mutex); +            perf_stats = system.GetAndResetPerfStats(); +        } +    } + +private: +    static EmulationSession s_instance; + +    ANativeWindow* native_window{}; + +    InputCommon::InputSubsystem input_subsystem; +    Common::DetachedTasks detached_tasks; +    Core::System system; + +    Core::PerfStatsResults perf_stats{}; +    mutable std::mutex perf_stats_mutex; + +    std::unique_ptr<EmuWindow_Android> window; +    std::mutex mutex; +    std::condition_variable_any cv; +}; + +/*static*/ EmulationSession EmulationSession::s_instance;  std::string UTF16ToUTF8(std::u16string_view input) {      std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; @@ -56,7 +186,6 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {      Common::Log::Initialize();      Common::Log::SetColorConsoleBackendEnabled(true);      Common::Log::Start(); -    Common::DetachedTasks detached_tasks;      MicroProfileOnThreadCreate("EmuThread");      SCOPE_EXIT({ MicroProfileShutdown(); }); @@ -68,46 +197,13 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {          return Core::SystemResultStatus::ErrorLoader;      } -    // Loads the configuration. -    Config{}; - -    system_.Initialize(); -    system_.ApplySettings(); - -    InputCommon::InputSubsystem input_subsystem{}; - -    emu_window = std::make_unique<EmuWindow_Android>(&input_subsystem, s_surf); - -    system_.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); -    system_.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); -    system_.GetFileSystemController().CreateFactories(*system_.GetFilesystem()); - -    const Core::SystemResultStatus load_result{system_.Load(*emu_window, filepath)}; - -    if (load_result != Core::SystemResultStatus::Success) { -        return load_result; -    } - -    system_.GPU().Start(); -    system_.GetCpuManager().OnGpuReady(); -    system_.RegisterExitCallback([&] { exit(0); }); - -    void(system_.Run()); - -    if (system_.DebuggerEnabled()) { -        system_.InitializeDebugger(); -    } - -    stop_run = false; -    while (!stop_run) { -        std::this_thread::sleep_for(std::chrono::seconds(1)); +    const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); +    if (result != Core::SystemResultStatus::Success) { +        return result;      } -    system_.DetachDebugger(); -    void(system_.Pause()); -    system_.ShutdownMainProcess(); - -    detached_tasks.WaitForAllTasks(); +    EmulationSession::GetInstance().RunEmulation(); +    EmulationSession::GetInstance().ShutdownEmulation();      return Core::SystemResultStatus::Success;  } @@ -117,22 +213,15 @@ extern "C" {  void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,                                                              [[maybe_unused]] jclass clazz,                                                              jobject surf) { -    s_surf = ANativeWindow_fromSurface(env, surf); - -    if (emu_window) { -        emu_window->OnSurfaceChanged(s_surf); -    } - -    LOG_INFO(Frontend, "surface changed"); +    EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); +    EmulationSession::GetInstance().SurfaceChanged();  }  void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,                                                                [[maybe_unused]] jclass clazz) { -    ANativeWindow_release(s_surf); -    s_surf = nullptr; -    if (emu_window) { -        emu_window->OnSurfaceChanged(s_surf); -    } +    ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); +    EmulationSession::GetInstance().SetNativeWindow(nullptr); +    EmulationSession::GetInstance().SurfaceChanged();  }  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                                                              [[maybe_unused]] jclass clazz) {}  void Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation([[maybe_unused]] JNIEnv* env, -                                                           [[maybe_unused]] jclass clazz) {} +                                                           [[maybe_unused]] jclass clazz) { +    EmulationSession::GetInstance().HaltEmulation(); +}  jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning([[maybe_unused]] JNIEnv* env,                                                             [[maybe_unused]] jclass clazz) { -    return static_cast<jboolean>(!stop_run); +    return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());  }  jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env,                                                                  [[maybe_unused]] jclass clazz,                                                                  [[maybe_unused]] jstring j_device,                                                                  jint j_button, jint action) { -    emu_window->OnGamepadEvent(j_button, action != 0); +    if (EmulationSession::GetInstance().IsRunning()) { +        EmulationSession::GetInstance().Window().OnGamepadEvent(j_button, action != 0); +    }      return static_cast<jboolean>(true);  } @@ -183,7 +276,10 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent([[maybe_unused          x /= r;          y /= r;      } -    emu_window->OnGamepadMoveEvent(x, y); + +    if (EmulationSession::GetInstance().IsRunning()) { +        EmulationSession::GetInstance().Window().OnGamepadMoveEvent(x, y); +    }      return static_cast<jboolean>(false);  } @@ -198,13 +294,18 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent([[maybe_unused]] JNI                                                                [[maybe_unused]] jclass clazz,                                                                jfloat x, jfloat y,                                                                jboolean pressed) { -    return static_cast<jboolean>(emu_window->OnTouchEvent(x, y, pressed)); +    if (EmulationSession::GetInstance().IsRunning()) { +        return static_cast<jboolean>(EmulationSession::GetInstance().Window().OnTouchEvent(x, y, pressed)); +    } +    return static_cast<jboolean>(false);  }  void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env,                                                            [[maybe_unused]] jclass clazz, jfloat x,                                                            jfloat y) { -    emu_window->OnTouchMoved(x, y); +    if (EmulationSession::GetInstance().IsRunning()) { +        EmulationSession::GetInstance().Window().OnTouchMoved(x, y); +    }  }  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]]                                                                    [[maybe_unused]] jclass clazz) {      jdoubleArray j_stats = env->NewDoubleArray(4); -    if (!stop_run && system_.IsPoweredOn()) { -        const auto results = system_.GetAndResetPerfStats(); +    if (EmulationSession::GetInstance().IsRunning()) { +        const auto results = EmulationSession::GetInstance().PerfStats();          // Converting the structure into an array makes it easier to pass it to the frontend          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                                                                        jstring j_path) {      const std::string path = GetJString(env, j_path); -    if (!stop_run) { -        stop_run = true; -    } -      const Core::SystemResultStatus result{RunEmulation(path)};      if (result != Core::SystemResultStatus::Success) {          env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | 
