|
@@ -17,6 +17,7 @@
|
|
|
#include "core/cpu_manager.h"
|
|
#include "core/cpu_manager.h"
|
|
|
#include "core/file_sys/registered_cache.h"
|
|
#include "core/file_sys/registered_cache.h"
|
|
|
#include "core/file_sys/vfs_real.h"
|
|
#include "core/file_sys/vfs_real.h"
|
|
|
|
|
+#include "core/hid/hid_core.h"
|
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
|
#include "core/perf_stats.h"
|
|
#include "core/perf_stats.h"
|
|
|
#include "jni/config.h"
|
|
#include "jni/config.h"
|
|
@@ -26,10 +27,139 @@
|
|
|
|
|
|
|
|
namespace {
|
|
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::string UTF16ToUTF8(std::u16string_view input) {
|
|
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
|
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::Initialize();
|
|
|
Common::Log::SetColorConsoleBackendEnabled(true);
|
|
Common::Log::SetColorConsoleBackendEnabled(true);
|
|
|
Common::Log::Start();
|
|
Common::Log::Start();
|
|
|
- Common::DetachedTasks detached_tasks;
|
|
|
|
|
|
|
|
|
|
MicroProfileOnThreadCreate("EmuThread");
|
|
MicroProfileOnThreadCreate("EmuThread");
|
|
|
SCOPE_EXIT({ MicroProfileShutdown(); });
|
|
SCOPE_EXIT({ MicroProfileShutdown(); });
|
|
@@ -68,46 +197,13 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
|
|
return Core::SystemResultStatus::ErrorLoader;
|
|
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;
|
|
return Core::SystemResultStatus::Success;
|
|
|
}
|
|
}
|
|
@@ -117,22 +213,15 @@ extern "C" {
|
|
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
|
|
[[maybe_unused]] jclass clazz,
|
|
[[maybe_unused]] jclass clazz,
|
|
|
jobject surf) {
|
|
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,
|
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
|
|
|
[[maybe_unused]] jclass clazz) {
|
|
[[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) {}
|
|
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) {}
|
|
[[maybe_unused]] jclass clazz) {}
|
|
|
|
|
|
|
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation([[maybe_unused]] JNIEnv* env,
|
|
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,
|
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning([[maybe_unused]] JNIEnv* env,
|
|
|
[[maybe_unused]] jclass clazz) {
|
|
[[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,
|
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env,
|
|
|
[[maybe_unused]] jclass clazz,
|
|
[[maybe_unused]] jclass clazz,
|
|
|
[[maybe_unused]] jstring j_device,
|
|
[[maybe_unused]] jstring j_device,
|
|
|
jint j_button, jint action) {
|
|
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);
|
|
return static_cast<jboolean>(true);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -183,7 +276,10 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent([[maybe_unused
|
|
|
x /= r;
|
|
x /= r;
|
|
|
y /= r;
|
|
y /= r;
|
|
|
}
|
|
}
|
|
|
- emu_window->OnGamepadMoveEvent(x, y);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (EmulationSession::GetInstance().IsRunning()) {
|
|
|
|
|
+ EmulationSession::GetInstance().Window().OnGamepadMoveEvent(x, y);
|
|
|
|
|
+ }
|
|
|
return static_cast<jboolean>(false);
|
|
return static_cast<jboolean>(false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -198,13 +294,18 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent([[maybe_unused]] JNI
|
|
|
[[maybe_unused]] jclass clazz,
|
|
[[maybe_unused]] jclass clazz,
|
|
|
jfloat x, jfloat y,
|
|
jfloat x, jfloat y,
|
|
|
jboolean pressed) {
|
|
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,
|
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env,
|
|
|
[[maybe_unused]] jclass clazz, jfloat x,
|
|
[[maybe_unused]] jclass clazz, jfloat x,
|
|
|
jfloat y) {
|
|
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,
|
|
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) {
|
|
[[maybe_unused]] jclass clazz) {
|
|
|
jdoubleArray j_stats = env->NewDoubleArray(4);
|
|
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
|
|
// 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,
|
|
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) {
|
|
jstring j_path) {
|
|
|
const std::string path = GetJString(env, j_path);
|
|
const std::string path = GetJString(env, j_path);
|
|
|
|
|
|
|
|
- if (!stop_run) {
|
|
|
|
|
- stop_run = true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
const Core::SystemResultStatus result{RunEmulation(path)};
|
|
const Core::SystemResultStatus result{RunEmulation(path)};
|
|
|
if (result != Core::SystemResultStatus::Success) {
|
|
if (result != Core::SystemResultStatus::Success) {
|
|
|
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
|
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|