summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2023-01-22 16:56:40 -0800
committerGravatar bunnei2023-06-03 00:05:28 -0700
commit1e94d16dadd5453fbc4b2f9fc44256662a2e9fcf (patch)
tree8f81714b704cf611b2944e6820f62bc942d42458
parentandroid: Frontend: Fix rendering aspect ratio & add a setting for it. (diff)
downloadyuzu-1e94d16dadd5453fbc4b2f9fc44256662a2e9fcf.tar.gz
yuzu-1e94d16dadd5453fbc4b2f9fc44256662a2e9fcf.tar.xz
yuzu-1e94d16dadd5453fbc4b2f9fc44256662a2e9fcf.zip
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.cpp231
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
27namespace { 28namespace {
28 29
29ANativeWindow* s_surf{}; 30class EmulationSession final {
30std::unique_ptr<EmuWindow_Android> emu_window; 31public:
31std::atomic<bool> stop_run{true}; 32 EmulationSession() = default;
32Core::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
145private:
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
34std::string UTF16ToUTF8(std::u16string_view input) { 164std::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" {
117void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, 213void 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
129void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, 220void 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
138void Java_org_yuzu_yuzu_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused]] jclass clazz) {} 227void 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
155void Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation([[maybe_unused]] JNIEnv* env, 244void 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
158jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning([[maybe_unused]] JNIEnv* env, 249jboolean 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
163jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent([[maybe_unused]] JNIEnv* env, 254jboolean 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
204void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env, 303void 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
210jintArray Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon([[maybe_unused]] JNIEnv* env, 311jintArray 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(),